Compare commits

..

No commits in common. "main" and "0.9.21" have entirely different histories.
main ... 0.9.21

67 changed files with 1777 additions and 3317 deletions

View File

@ -10,7 +10,7 @@
<RootNamespace>DCFApixels.DragonECS</RootNamespace>
<Title>DragonECS</Title>
<Version>1.0.1</Version>
<Version>0.9.21</Version>
<Authors>DCFApixels</Authors>
<Description>ECS Framework for Game Engines with C# and .Net Platform</Description>
<Copyright>DCFApixels</Copyright>

View File

@ -20,29 +20,33 @@
<tr>
<td nowrap width="100">
<a href="README-RU.md">
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"><br/>
<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">
<img src="https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930"><br/>
<img src="https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930"></br>
<span>English</span>
</a>
</td>
<td nowrap width="100">
<a href="README-ZH.md">
<img src="https://github.com/user-attachments/assets/8e598a9a-826c-4a1f-b842-0c56301d2927"><br/>
<img src="https://github.com/user-attachments/assets/8e598a9a-826c-4a1f-b842-0c56301d2927"></br>
<span>中文</span>
</a>
</td>
</tr>
</table>
DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system) фреймворк, нацеленный на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Разработан на чистом C#, без зависимостей и генерации кода. Вдохновлен [LeoEcs Lite](https://github.com/Leopotam/ecslite).
</br>
> [!Note]
> Есть вопросы или хотите быть в курсе разработки? Присоединяйтесь к нашему комьюнити! -> [Обратная связь](#обратная-связь)
DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system) фреймворк нацеленный на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Разработан на чистом C#, без зависимостей и генерации кода. Вдохновлен [LeoEcs Lite](https://github.com/Leopotam/ecslite).
> [!WARNING]
> Проект предрелизной версии, поэтому API может меняться. В ветке main актуальная и рабочая версия.
>
> Если есть неясные моменты, вопросы можно задать тут [Обратная связь](#обратная-связь)
## Оглавление
- [Установка](#установка)
@ -73,24 +77,24 @@ DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system)
- [Расширение фреймворка](#расширение-фреймворка)
- [Компоненты мира](#компоненты-мира)
- [Конфиги](#конфиги)
- [Проекты на DragonECS](#проекты-на-dragonecs)
- [Проекты на DragonECS](#Проекты-на-DragonECS)
- [FAQ](#faq)
- [Обратная связь](#обратная-связь)
- [Лицензия](#лицензия)
</br>
# Установка
Семантика версионирования - [Открыть](https://gist.github.com/DCFApixels/af79284955bf40e9476cdcac79d7b098#file-dcfapixels_versioning-md)
Семантика версионирования - [Открыть](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
## Окружение
Обязательные требования:
+ Минимальная версия C#: 7.3.
+ Минимальная версия C# 7.3;
Поддерживает:
+ NativeAOT;
Опционально:
+ Поддержка NativeAOT
+ Игровые движки с C#: Unity, Godot, MonoGame и т.д.
Протестировано на:
* **Unity:** Минимальная версия 2021.2.0.
Протестировано:
+ **Unity:** Минимальная версия 2020.3.0;
## Установка для Unity
> Рекомендуется так же установить расширение [Интеграция с движком Unity](https://github.com/DCFApixels/DragonECS-Unity)
@ -105,9 +109,9 @@ https://github.com/DCFApixels/DragonECS.git
```
* ### В виде исходников
Можно так же напрямую скопировать исходники фреймворка в проект.
Можно так же напрямую скопировать в проект исходники фреймворка.
</br>
# Расширения
* Интеграции:
@ -121,20 +125,19 @@ https://github.com/DCFApixels/DragonECS.git
* [Графы](https://github.com/DCFApixels/DragonECS-Graphs)
* Утилиты:
* [Упрощенный синтаксис](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)
* [EcsRefPool](https://gist.github.com/DCFApixels/73e392ccabdd98b3d4a517017d8a3f22)
* [Таймеры](https://gist.github.com/DCFApixels/71a416275660c465ece76242290400df)
* [Однокадровые компоненты](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60)
* [Шаблоны кода IDE](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) и [для Unity](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
> *Твое расширение? Если разрабатываешь расширение для DragonECS, пиши [сюда](#обратная-связь).
</br>
# Основные концепции
## Entity
**Сущности** - это то, к чему крепятся данные. Для доступа к сущности используются идентификаторы двух типов:
**Сущности** - это то к чему крепятся данные. Для доступа к сущности используются идентификаторы двух типов:
* `int` - однократный идентификатор, применяется в пределах одного тика. Не рекомендуется использовать для хранения;
* `entlong` - долговременный идентификатор, содержит метку поколения, что делает его уникальным навсегда. Подходит для хранения.
```c#
``` c#
// Создание новой сущности в мире.
int entityID = _world.NewEntity();
@ -152,7 +155,7 @@ int newEntityID = _world.CloneEntity(entityID);
<summary>Работа с entlong</summary>
```c#
``` c#
// Конвертация int в entlong.
entlong entity = _world.GetEntityLong(entityID);
// упрощенная версия
@ -212,7 +215,7 @@ class SomeSystem : IEcsPreInit, IEcsInit, IEcsRun, IEcsDestroy
<summary>Пример системы</summary>
```c#
``` c#
// Система
class DamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
{
@ -250,7 +253,7 @@ public struct Health : IEcsComponent
</details>
</br>
# Концепции фреймворка
## Пайплайн
@ -281,9 +284,9 @@ class SomeSystem : IEcsRun, IEcsPipelineMember
```
> Для одновременного построения и инициализации есть метод Builder.BuildAndInit();
### Внедрение зависимостей
Фреймворк реализует внедрение зависимостей для систем. Это процесс, который запускается вместе с инициализацией пайплайна и внедряет данные, переданные в Builder.
Фреймворк реализует внедрение зависимостей для систем. это процесс который запускается вместе с инициализацией пайплайна и внедряет данные переданные в Builder.
```c#
``` c#
class SomeDataA { /* ... */ }
class SomeDataB : SomeDataA { /* ... */ }
@ -321,7 +324,7 @@ class SomeSystem : IEcsInject<SomeDataA>, IEcsRun
### Модули
Группы систем реализующие общую фичу можно объединять в модули, и просто добавлять модули в Pipeline.
```c#
``` c#
using DCFApixels.DragonECS;
class Module1 : IEcsModule
{
@ -334,7 +337,7 @@ class Module1 : IEcsModule
}
}
```
```c#
``` c#
EcsPipeline pipeline = EcsPipeline.New()
// ...
.AddModule(new Module1())
@ -343,32 +346,29 @@ EcsPipeline pipeline = EcsPipeline.New()
```
### Сортировка
Для управления расположением систем в пайплайне, вне зависимости от порядка добавления, есть 2 способа: Слои и Порядок сортировки.
Дла управления расположением систем в пайплайне, вне зависимости от порядка добавления, есть 2 способа: Слои и Порядок сортировки.
#### Слои
Слой определяет место в пайплайне для вставки систем. Например, если необходимо чтобы система была вставлена в конце пайплайна, эту систему можно добавить в слой `EcsConsts.END_LAYER`.
```c#
``` c#
const string SOME_LAYER = nameof(SOME_LAYER);
const string SOME_LAYER_2 = nameof(SOME_LAYER_2);
EcsPipeline pipeline = EcsPipeline.New()
// ...
// Вставляет новый слой перед конечным слоем EcsConsts.END_LAYER
.Layers.Add(SOME_LAYER).Before(EcsConsts.END_LAYER)
// Вставляет SOME_LAYER_2 слой в позицию после EcsConsts.BASIC_LAYER
.Layers.Add(SOME_LAYER).After(EcsConsts.BASIC_LAYER)
.Layers.Insert(EcsConsts.END_LAYER, SOME_LAYER)
// Система SomeSystem будет вставлена в слой SOME_LAYER
.Add(New SomeSystem(), SOME_LAYER)
// ...
.BuildAndInit();
```
Встроенные слои расположены в следующем порядке:
* `EcsConsts.PRE_BEGIN_LAYER`
* `EcsConsts.BEGIN_LAYER`
* `EcsConsts.BASIC_LAYER` (По умолчанию системы добавляются сюда)
* `EcsConsts.END_LAYER`
* `EcsConsts.POST_END_LAYER`
* `EcsConst.PRE_BEGIN_LAYER`
* `EcsConst.BEGIN_LAYER`
* `EcsConst.BASIC_LAYER` (По умолчанию системы добавляются сюда)
* `EcsConst.END_LAYER`
* `EcsConst.POST_END_LAYER`
#### Порядок сортировки
Для сортировки систем в рамках слоя используется int значение порядка сортировки. По умолчанию системы добавляются с `sortOrder = 0`.
```c#
``` c#
EcsPipeline pipeline = EcsPipeline.New()
// ...
// Система SomeSystem будет вставлена в слой EcsConsts.BEGIN_LAYER
@ -394,7 +394,7 @@ EcsPipeline pipeline = EcsPipeline.New()
<summary>Пользовательские процессы</summary>
Для добавления нового процесса создайте интерфейс наследованный от `IEcsProcess` и создайте раннер для него. Раннер это класс реализующий интерфейс запускаемого процесса и наследуемый от `EcsRunner<TInterface>`. Пример:
```c#
``` c#
// Интерфейс процесса.
interface IDoSomethingProcess : IEcsProcess
{
@ -420,10 +420,10 @@ _pipeline = EcsPipeline.New()
.BuildAndInit();
// Запуск раннера если раннер был добавлен.
_pipeline.GetRunner<IDoSomethingProcess>().Do();
_pipeline.GetRunner<IDoSomethingProcess>.Do()
// Или если раннер не был добавлен (вызов GetRunnerInstance также добавит раннер в пайплайн).
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>().Do();
// Или если раннер не был добавлен(Вызов GetRunnerInstance так же добавит раннер в пайплайн).
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>.Do()
```
<details>
@ -540,9 +540,9 @@ poses.Del(entityID);
Обычно маски не используются в чистом виде, а являются частью [Аспекта](#Аспект).
```c#
// Создание маски с условием наличия компонентов SomeCmp1 и SomeCmp2,
// и с условием отсутствия компонента SomeCmp3.
``` c#
// Создание маски с улосвием наличия компонентов SomeCmp1 и SomeCmp2,
// и с улосвием отсутсвия компонента SomeCmp3.
EcsMask mask = EcsMask.New(_world)
// Inc - Условие наличия компонента.
.Inc<SomeCmp1>()
@ -612,9 +612,9 @@ class Aspect : EcsAspect
public EcsPool<Velocity> velocities;
protected override void Init(Builder b)
{
poses = b.Inc<Pose>();
velocities = b.Inc<Velocity>();
b.Exc<FreezedTag>();
poses = b.IncludePool<Pose>();
velocities = b.IncludePool<Velocity>();
b.ExcludePool<FreezedTag>();
}
}
```
@ -638,8 +638,8 @@ class Aspect : EcsAspect
otherAspect1 = b.Combine<OtherAspect1>(1);
// Хотя для OtherAspect1 метод Combine был вызван раньше, сначала будет скомбинирован с OtherAspect2, так как по умолчанию order = 0.
otherAspect2 = b.Combine<OtherAspect2>();
// Если в OtherAspect1 или в OtherAspect2 было условие b.Exc<Pose>() тут оно будет заменено на b.Inc<Pose>().
poses = b.Inc<Pose>();
// Если в OtherAspect1 или в OtherAspect2 было условие b.Exclude<Pose>() тут оно будет заменено на b.Include<Pose>().
poses = b.Include<Pose>();
}
}
```
@ -703,7 +703,7 @@ public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
``` c#
// Запрос Where возвращает сущности в виде EcsSpan.
EcsSpan es = _world.Where(out Aspect a);
// Итерироваться можно с помощью `foreach` и `for`.
// Итерироваться можно по foreach и for.
foreach (var e in es)
{
// ...
@ -736,7 +736,7 @@ group.Remove(entityID);
``` c#
// Запрос WhereToGroup возвращает сущности в виде группы только для чтения EcsReadonlyGroup.
EcsReadonlyGroup group = _world.WhereToGroup(out Aspect a);
// Итерироваться можно с помощью `foreach` и `for`.
// Итерироваться можно по foreach и for.
foreach (var e in group)
{
// ...
@ -913,40 +913,6 @@ var tags = typeMeta.Tags; // [MetaTags]
```
> Для автоматической генерации уникальных идентификаторов MetaID есть метод `MetaID.GenerateNewUniqueID()` и [Браузерный генератор](https://dcfapixels.github.io/DragonECS-MetaID_Generator_Online/)
<details>
<summary>[MetaProxy]</summary>
`[MetaProxy(typeof(MetaProxyType))]` атрибут для гибкой настройки метаданных. Он позволяет обходить ограничения обычных атрибутов (например, невозможность передавать в них неконстантные данные) и задавать метаданные программно. Кроме того, метаданные, полученные через `MetaProxy`, наследуются.
API напоминает `[DebuggerTypeProxy]`, но вместо экземпляра объекта принимает тип `Type`. А класс для `MetaProxy` должен наследовать `MetaProxyBase`.
Пример использования:
``` c#
// Применение атрибута
[MetaProxy(typeof(UnityComponent<>.MetaProxy))]
// В данном примере UnityComponent оборачивает компонент Unity
public struct UnityComponent<T> where T : Component
{
// ...
// Реализация MetaProxyBase. MetaProxy копирует метаданные оборачиваемого типа,
// чтобы в инспекторе Unity отображалась его мета.
private class MetaProxy : MetaProxyBase
{
protected TypeMeta Meta = typeof(T).GetMeta();
public override string Name { get { return Meta?.Name; } }
public override MetaColor? Color { get { return Meta != null && Meta.IsCustomColor ? Meta.Color : null; } }
public override MetaGroup Group { get { return Meta?.Group; } }
public override MetaDescription Description { get { return Meta?.Description; } }
public override IEnumerable<string> Tags { get { return Meta?.Tags; } }
public MetaProxy(Type type) : base(type) { }
}
}
```
</details>
## EcsDebug
Вспомогательный тип с набором методов для отладки и логирования. Реализован как статический класс вызывающий методы Debug-сервисов. Debug-сервисы - это посредники между EcsDebug и инструментами отладки среды. Такая реализация позволяет не изменяя отладочный код, менять его поведение или переносить проект в другие среды, достаточно только реализовать соответствующий Debug-сервис.
@ -996,7 +962,8 @@ using (_marker.Auto())
+ `DRAGONECS_STABILITY_MODE` - включает опускаемые в релизном билде проверки.
+ `DRAGONECS_DISABLE_CATH_EXCEPTIONS` - Выключает поведение по умолчанию по обработке исключений. По умолчанию фреймворк будет ловить исключения с выводом информации из исключений через EcsDebug и продолжать работу.
+ `REFLECTION_DISABLED` - Полностью ограничивает работу фреймворка с Reflection.
+ `DISABLE_DEBUG` - Для среды, где не поддерживается ручное отключение DEBUG, например Unity.
+ `DISABLE_DEBUG` - Для среды где не поддерживается ручное отключение DEBUG, например Unity.
+ `ENABLE_DUMMY_SPAN` - На случай если в среде не поддерживаются Span типы, включает его замену.
</br>
@ -1010,7 +977,7 @@ using (_marker.Auto())
var configs = new ConfigContainer()
.Set(new EcsWorldConfig(entitiesCapacity: 2000, poolsCapacity: 2000)
.Set(new SomeDataA(/* ... */))
.Set(new SomeDataB(/* ... */));
.Set(new SomeDataB(/* ... */)));
EcsDefaultWorld _world = new EcsDefaultWorld(configs);
// ...
var _someDataA = _world.Configs.Get<SomeDataA>();
@ -1105,7 +1072,7 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
<td align="center">
<a href="https://github.com/DCFApixels/3D-Platformer-DragonECS-Demo">
3D Platformer (Example)
<img src="https://github.com/user-attachments/assets/6aba814d-a70b-432f-a905-84d1b6872581" />
<img src="https://github.com/user-attachments/assets/c593dba7-eeaa-4706-a043-946f132f3f83" alt="screenshot">
</a>
</td>
<td align="center">
@ -1115,21 +1082,6 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
</a>
</td>
</tr>
<tr></tr>
<tr>
<td align="center">
<a href="https://github.com/Evileptic/Arkanoid">
Arkanoid
<img src="https://github.com/user-attachments/assets/bbdb4a7f-2f59-4a3a-ab51-a3e4fe0ad35e" alt="screenshot">
</a>
</td>
<td align="center">
_____________
<img tabindex="-1" src="https://github.com/user-attachments/assets/3fa1ca6d-29f6-43e6-aafe-cc9648d20490" alt="screenshot">
</td>
</tr>
</table>
## Опубликованные проекты:
@ -1171,8 +1123,15 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
# FAQ
## 'ReadOnlySpan<>' could not be found
В версии Unity 2020.1.х в консоли может выпадать ошибка:
```
The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing a using directive or an assembly reference?)
```
Чтобы починить добавьте директиву `ENABLE_DUMMY_SPAN` в `Project Settings/Player/Other Settings/Scripting Define Symbols`.
## Как Выключать/Включать системы?
Напрямую — никак.
Напрямую - никак. </br>
Обычно потребность выключить/включить систему появляется когда поменялось общее состояние игры, это может так же значить что нужно переключить сразу группу систем, все это в совокупности можно рассматривать как изменения процессов. Есть 2 решения:</br>
+ Если изменения процесса глобальные, то создать новый `EcsPipeline` и в цикле обновления движка запускать соответствующий пайплайн.
+ Разделить `IEcsRun` на несколько процессов и в цикле обновления движка запускать соответствующий процесс. Для этого создайте новый интерфейс процесса, раннер для запуска этого интерфейса и получайте раннер через `EcsPipeline.GetRunner<T>()`.
@ -1183,11 +1142,6 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
+ Discord (RU-EN) [https://discord.gg/kqmJjExuCf](https://discord.gg/kqmJjExuCf)
+ QQ (中文) [949562781](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=IbDcH43vhfArb30luGMP1TMXB3GCHzxm&authKey=s%2FJfqvv46PswFq68irnGhkLrMR6y9tf%2FUn2mogYizSOGiS%2BmB%2B8Ar9I%2Fnr%2Bs4oS%2B&noverify=0&group_code=949562781)
</br>
# Лицензия
MIT Лицензия: [Открыть](LICENSE.md)
</br></br></br>
</br></br></br>
</br></br></br>

View File

@ -20,19 +20,19 @@
<tr>
<td nowrap width="100">
<a href="README-RU.md">
<img src="https://github.com/user-attachments/assets/7bc29394-46d6-44a3-bace-0a3bae65d755"><br/>
<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">
<img src="https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930"><br/>
<img src="https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930"></br>
<span>English</span>
</a>
</td>
<td nowrap width="100">
<a href="README-ZH.md">
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"><br/>
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"></br>
<span>中文</span>
</a>
</td>
@ -43,12 +43,15 @@
DragonECS 是一个[实体组件系统](https://www.imooc.com/article/331544)框架。专注于提升便利性、模块性、可扩展性和动态实体修改性能。 用纯C#开发的,没有依赖和代码生成。灵感来自于[LeoEcs Lite](https://github.com/Leopotam/ecslite)。
> [!Note]
> 有疑问或想了解开发进度?欢迎加入我们的社区! -> [Feedback](#Feedback)
> [!WARNING]
> 该框架是预发布版本,因此 API 可能会有变化。在 `main` 分支中是当前的工作版本。
>
> 最新版本的 README 是[俄文版](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md)。
>
> 如果有不清楚的地方,可以在这里提问 [反馈](#反馈)。
## 目录
- [安装](#安装)
- [扩展](#扩展)
- [基础概念](#基础概念)
- [实体](#实体)
- [组件](#组件)
@ -68,7 +71,7 @@ DragonECS 是一个[实体组件系统](https://www.imooc.com/article/331544)框
- [集合](#集合)
- [ECS入口](#ECS入口)
- [Debug](#debug)
- [Meta Attributes](#meta-attributes)
- [元属性](#元属性)
- [EcsDebug](#ecsdebug)
- [性能分析](#性能分析)
- [Define Symbols](#define-symbols)
@ -76,65 +79,43 @@ DragonECS 是一个[实体组件系统](https://www.imooc.com/article/331544)框
- [世界组件](#世界组件)
- [配置](#配置)
- [使用DragonECS的项目](#使用dragonecs的项目)
- [扩展](#扩展)
- [FAQ](#faq)
- [反馈](#反馈)
- [License](#license)
</br>
# 安装
版本的语义 [[打开]](https://gist.github.com/DCFApixels/af79284955bf40e9476cdcac79d7b098#file-dcfapixels_versioning-md)
版本的语义 [[打开]](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
## 环境
必备要求:
* 最低 C# 版本7.3。
+ C# 7.3 的最低版本;
支持
* NativeAOT
* 使用 C# 的游戏引擎Unity、Godot、MonoGame 等。
可选要求
+ 支持NativeAOT
+ 使用 C# 的游戏引擎Unity、Godot、MonoGame等。
已测试
* **Unity:** 最低版本 2021.2.0。
已测试:
+ **Unity:** 最低版本 2020.1.0
## 为 Unity 安装
> 还建议安装[Unity 引擎集成](https://github.com/DCFApixels/DragonECS-Unity)扩展。
* ### Unity 软件包
支持以 Unity 软件包的形式安装。可以通过在 PackageManager 中添加 Git URL参见 [教程](https://docs.unity3d.com/cn/2023.2/Manual/upm-ui-giturl.html)或手动将条目添加到 `Packages/manifest.json`
## 为Unity安装
> 还建议安装[Unity引擎集成](https://github.com/DCFApixels/DragonECS-Unity)扩展。
* ### Unity-软件包
支持以Unity软件包的形式安装。可以通过[git-url添加到PackageManager](https://docs.unity3d.com/cn/2023.2/Manual/upm-ui-giturl.html)或手动添加到`Packages/manifest.json`
```
https://github.com/DCFApixels/DragonECS.git
```
或者在 `Packages/manifest.json` 中添加包条目:
```
"com.dcfa_pixels.dragonecs": "https://github.com/DCFApixels/DragonECS.git",
```
* ### 作为源代码
也可以通过将框架的源代码直接复制到项目中来使用该框架。
# 扩展
* Integrations:
* [Unity](https://github.com/DCFApixels/DragonECS-Unity)
* [Godot](https://gitlab.com/InauniusOwn/Libraries/DraGodot)
* Packages:
* [自动依赖注入](https://github.com/DCFApixels/DragonECS-AutoInjections)
* [经典C#多线程](https://github.com/DCFApixels/DragonECS-ClassicThreads)
* [Recursivity](https://github.com/DCFApixels/DragonECS-Recursivity)
* [Hybrid](https://github.com/DCFApixels/DragonECS-Hybrid)
* [Graphs](https://github.com/DCFApixels/DragonECS-Graphs)
* Utilities:
* [简单语法](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)
* [EcsRefPool](https://gist.github.com/DCFApixels/73e392ccabdd98b3d4a517017d8a3f22)
* [计时器](https://gist.github.com/DCFApixels/71a416275660c465ece76242290400df)
* [单帧组件](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60)
* [IDE代码模板](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) 和 [Unity 模板](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
> *你的扩展?如果你正在开发 DragonECS 的扩展,可以[在此处发布](#反馈).
框架也可以通过复制源代码添加到项目中。
</br>
# 基础概念
## 实体
**实体**是附加数据的基础。有两种用于引用实体的标识符
* `int` - 短期标识符仅在单次更新tick内有效。不建议用于长期存储
* `entlong` - 长期标识符包含一个世代标记generation tag使其在实体的整个生命周期内保持唯一。适合长期保存和存储使用
```c#
**实体**是附加数据的基础。它们以标识符的形式实现,有两种类型:
* `int` - 是在单个更新中使用的一次性标识符。不建议存储`int`标识符,而应使用 `entlong`
* `entlong` - 是一个长期标识符,包含一整套用于明确识别的信息;
``` c#
// 在世界中创建一个新实体。
int entityID = _world.NewEntity();
@ -151,7 +132,7 @@ int newEntityID = _world.CloneEntity(entityID);
<details>
<summary>entlong使用</summary>
```c#
``` c#
// int 转换为 entlong。
entlong entity = _world.GetEntityLong(entityID);
// 或者
@ -239,7 +220,7 @@ class SomeSystem : IEcsRun, IEcsPipelineMember
### 依赖注入
框架具有向系统注入依赖的功能。这是一个与管线初始化一起运行的流程并注入传递给Builder的数据。
> 内置依赖注入的使用是可选的。
```c#
``` c#
class SomeDataA { /* ... */ }
class SomeDataB : SomeDataA { /* ... */ }
@ -270,15 +251,9 @@ class SomeSystem : IEcsInject<SomeDataA>, IEcsRun
}
```
静态标记的含义:
* `Inc` — 组件必须存在(包含条件)并缓存该池。
* `Exc` — 组件不得存在(排除条件)并缓存该池。
* `Opt` — 组件可以存在,但不影响过滤(仅缓存池以便访问)。
* `Any` — 至少要存在被 `Any` 标记的组件之一;同时缓存该池。
### 模块
实现一个共同特性的系统组可以组合成模块,模块也可以简单地添加到管线中。
```c#
``` c#
using DCFApixels.DragonECS;
class Module1 : IEcsModule
{
@ -308,20 +283,18 @@ const string SOME_LAYER = nameof(SOME_LAYER);
EcsPipeline pipeline = EcsPipeline.New()
// ...
// 在最终的 EcsConsts.END_LAYER 层前面插入一个新 SOME_LAYER 层。
.Layers.Add(SOME_LAYER).Before(EcsConsts.END_LAYER)
// 将 SOME_LAYER_2 层级入到 EcsConsts.BASIC_LAYER 之后的位置
.Layers.Add(SOME_LAYER).After(EcsConsts.BASIC_LAYER)
.Layers.Insert(EcsConsts.END_LAYER, SOME_LAYER)
// SomeSystem 系统将插入 SAME_LAYER 层。
.Add(New SomeSystem(), SOME_LAYER)
// ...
.BuildAndInit();
```
嵌入层按以下顺序排列:
* `EcsConsts.PRE_BEGIN_LAYER`
* `EcsConsts.BEGIN_LAYER`
* `EcsConsts.BASIC_LAYER`(默认情况下,系统添加到此层)
* `EcsConsts.END_LAYER`
* `EcsConsts.POST_END_LAYER`
* `EcsConst.PRE_BEGIN_LAYER`
* `EcsConst.BEGIN_LAYER`
* `EcsConst.BASIC_LAYER`(默认情况下,系统添加到此层)
* `EcsConst.END_LAYER`
* `EcsConst.POST_END_LAYER`
#### 排序顺序
在同一层内,可以使用 `int` 类型的排序值来排序系统。默认情况下,系统的排序值为 `sortOrder = 0`
```c#
@ -349,7 +322,7 @@ EcsPipeline pipeline = EcsPipeline.New()
<details>
<summary>用户流程</summary>
要添加新的流程,请创建一个继承自 `IEcsProcess` 的接口,并为其创建一个 Runner。Runner 是一个实现该流程接口并继承自 `EcsRunner<TInterface>` 的类。示例:
Для добавления нового процесса создайте интерфейс наследованный от `IEcsProcess` и создайте раннер для него. Раннер это класс реализующий интерфейс запускаемого процесса и наследуемый от `EcsRunner<TInterface>`. Пример:
``` c#
// 流程接口。
interface IDoSomethingProcess : IEcsProcess
@ -374,10 +347,10 @@ _pipeline = EcsPipeline.New()
.BuildAndInit();
// 如果启动器已经添加,运行它。
_pipeline.GetRunner<IDoSomethingProcess>().Do();
_pipeline.GetRunner<IDoSomethingProcess>.Do()
// 如果启动器尚未添加,使用 GetRunnerInstance 将其添加并运行
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>().Do();
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>.Do()
```
<details>
@ -464,7 +437,7 @@ poses.Del(entityID);
> 可以实现用户池。稍后将介绍这一功能。
## 掩码
用于根据组件的存在与否来过滤实体。通常掩码不单独使用,而是作为 `EcsAspect` 的一部分,用于查询时过滤实体。
用于根据组件的存在与否来过滤实体。
``` c#
// 创建一个掩码,检查实体是否具有组件
// SomeCmp1 和 SomeCmp2但没有组件 SomeCmp3。
@ -499,11 +472,7 @@ EcsMask mask = _staticMask.ToMask(_world);
</details>
## 方面
方面是继承自 `EcsAspect` 的用户自定义类,用于描述系统要处理的一组组件。方面同时承担两个职责:
- 掩码 — 初始化并保存一个 `EcsMask`,使其可在查询中用于过滤实体。
- 池缓存 — 提供对组件池的快速访问。
简而言之,方面是描述“我处理哪些实体以及如何访问它们的组件”的便捷方式。
这些是继承自 EcsAspect 的用户类,用于与实体进行交互。方面同时充当池的缓存和实体组件的过滤掩码。可以把方面视为系统处理哪些实体的描述。
简化语法:
``` c#
@ -534,9 +503,9 @@ class Aspect : EcsAspect
public EcsPool<Velocity> velocities;
protected override void Init(Builder b)
{
poses = b.Inc<Pose>();
velocities = b.Inc<Velocity>();
b.Exc<FreezedTag>();
poses = b.Include<Pose>();
velocities = b.Include<Velocity>();
b.Exclude<FreezedTag>();
}
}
```
@ -560,13 +529,13 @@ class Aspect : EcsAspect
otherAspect1 = b.Combine<OtherAspect1>(1);
// 即使对 OtherAspect1 调用 Combine 方法更早Aspect 会首先与 OtherAspect2 进行组合,因为默认情况下 order = 0。
otherAspect2 = b.Combine<OtherAspect2>();
// 如果 OtherAspect1 或 OtherAspect2 中有 b.Exc<Pose>() 的限制条件,这里将被替换为 b.Inc<Pose>().
poses = b.Inc<Pose>();
// 如果 OtherAspect1 或 OtherAspect2 中有 b.Exclude<Pose>() 的限制条件,这里将被替换为 b.Include<Pose>()。
poses = b.Include<Pose>();
}
}
```
如果组合的方面存在冲突的限制条件,则新的限制条件将替换先前添加的限制条件。根方面的限制条件始终会替换添加的方面中的限制条件。限制条件组合的视觉示例:
| | cmp1 | cmp2 | cmp3 | cmp4 | cmp5 | 冲突限制的解析 |
| | cmp1 | cmp2 | cmp3 | cmp4 | cmp5 | разрешение конфликтных ограничений|
| :--- | :--- | :--- | :--- | :--- | :--- |:--- |
| OtherAspect2 | :heavy_check_mark: | :x: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | |
| OtherAspect1 | :heavy_minus_sign: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_minus_sign: | 对于 `cmp2` 将选择 :heavy_check_mark: |
@ -796,7 +765,7 @@ public class EcsRoot
# Debug
该框架提供了额外的调试和日志记录工具,不依赖于环境此外,许多类型都有自己的 DebuggerProxy以便在 IDE 中更详细地显示信息。
## Meta Attributes
## 元属性
默认情况下,元属性没有用处,在与引擎集成时用于指定在调试工具和编辑器中的显示方式。还可以用于生成自动文档。
``` c#
using DCFApixels.DragonECS;
@ -835,40 +804,6 @@ var tags = typeMeta.Tags; // [MetaTags]
```
> 为了自动生成唯一的标识符 MetaID可以使用 `MetaID.GenerateNewUniqueID()` 方法和 [浏览器生成器](https://dcfapixels.github.io/DragonECS-MetaID_Generator_Online/)。
<details>
<summary>MetaProxy</summary>
`[MetaProxy(typeof(MetaProxyType))]` 特性用于灵活配置元数据。它允许绕过常规特性的限制(例如无法向其传递非常量数据),并以编程方式设置元数据。此外,通过 `MetaProxy` 获得的元数据是可继承的。
该 API 类似于 `[DebuggerTypeProxy]`,但不同之处在于它接受的是 `Type` 类型,而不是对象实例。`MetaProxy` 的类必须继承自 `MetaProxyBase`
使用示例:
``` c#
// 应用特性
[MetaProxy(typeof(UnityComponent<>.MetaProxy))]
// 在此示例中UnityComponent 包装了一个 Unity 组件
public struct UnityComponent<T> where T : Component
{
// ...
// MetaProxyBase 的实现。MetaProxy 复制被包装类型的元数据,
// 以便在 Unity 检查器中显示其元数据。
private class MetaProxy : MetaProxyBase
{
protected TypeMeta Meta = typeof(T).GetMeta();
public override string Name { get { return Meta?.Name; } }
public override MetaColor? Color { get { return Meta != null && Meta.IsCustomColor ? Meta.Color : null; } }
public override MetaGroup Group { get { return Meta?.Group; } }
public override MetaDescription Description { get { return Meta?.Description; } }
public override IEnumerable<string> Tags { get { return Meta?.Tags; } }
public MetaProxy(Type type) : base(type) { }
}
}
```
</details>
## EcsDebug
具有调试和日志记录方法集. 实现为一个静态类,调用 DebugService 的方法. DebugService 是环境调试系统与 EcsDebug 之间的中介. 这使得可以将项目移植到其他引擎上,而无需修改项目的调试代码,只需要实现特定的 DebugService 即可。
@ -1028,7 +963,7 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
<td align="center">
<a href="https://github.com/DCFApixels/3D-Platformer-DragonECS-Demo">
3D Platformer (Example)
<img src="https://github.com/user-attachments/assets/6aba814d-a70b-432f-a905-84d1b6872581" />
<img src="https://github.com/user-attachments/assets/c593dba7-eeaa-4706-a043-946f132f3f83" alt="screenshot">
</a>
</td>
<td align="center">
@ -1038,43 +973,11 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
</a>
</td>
</tr>
<tr></tr>
<tr>
<td align="center">
<a href="https://github.com/Evileptic/Arkanoid">
Arkanoid
<img src="https://github.com/user-attachments/assets/bbdb4a7f-2f59-4a3a-ab51-a3e4fe0ad35e" alt="screenshot">
</a>
</td>
<td align="center">
_____________
<img tabindex="-1" src="https://github.com/user-attachments/assets/3fa1ca6d-29f6-43e6-aafe-cc9648d20490" alt="screenshot">
</td>
</tr>
</table>
## Released games:
<table>
<tr>
<td align="center">
<a href="https://dcfapixels.github.io/Project8.html">
Crystal Siege
<img src="https://github.com/user-attachments/assets/1aa60a50-2668-4919-aca9-d6d2b980c3dd">
</a>
</td>
<td align="center">
<a href="https://play.google.com/store/apps/details?id=com.ZlodeyStudios.OrdersMatter">
Order matters
<img src="https://github.com/user-attachments/assets/c55b2647-9b6e-4145-98ff-c3d094600fa1">
</a>
</td>
</tr>
<tr></tr>
<tr>
<td align="center">
<a href="https://yandex.ru/games/app/206024?utm_source=game_popup_menu">
@ -1083,38 +986,46 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
</a>
</td>
<td align="center">
_____________
<span>
<img tabindex="-1" src="https://github.com/user-attachments/assets/3fa1ca6d-29f6-43e6-aafe-cc9648d20490" alt="screenshot">
</span>
</td>
</tr>
</table>
</br>
# 扩展
* Packages:
* [Unity集成](https://github.com/DCFApixels/DragonECS-Unity)
* [自动依赖注入](https://github.com/DCFApixels/DragonECS-AutoInjections)
* [经典C#多线程](https://github.com/DCFApixels/DragonECS-ClassicThreads)
* [Recursivity](https://github.com/DCFApixels/DragonECS-Recursivity)
* [Hybrid](https://github.com/DCFApixels/DragonECS-Hybrid)
* [Graphs](https://github.com/DCFApixels/DragonECS-Graphs)
* Utilities:
* [简单语法](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)
* [单帧组件](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60)
* [IDE代码模板](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) и [Unity代码模板](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
> *你的扩展?如果你正在开发 DragonECS 的扩展,可以[在此处发布](#反馈).
</br>
# FAQ
## 如何启用/禁用系统?
直接——不能。
通常当游戏的整体状态发生变化时会需要启用或禁用系统有时需要切换一组系统。从概念上讲这可以视为流程process的变更。这里有两种解决方案
- 如果流程变更是全局性的,可以创建一个新的 `EcsPipeline`并在引擎的更新循环中运行相应的管线pipeline
- 将 `IEcsRun` 拆分为多个流程,并在引擎更新循环中运行所需的流程。为此,创建一个新的流程接口,为其实现一个 Runner并通过 `EcsPipeline.GetRunner<T>()` 获取该 Runner。
## 建议清单: [DragonECS-Vault](https://github.com/DCFApixels/DragonECS-Vault)
## 'ReadOnlySpan<>' could not be found
在Unity 2020.1.x版本中控制台可能会出现以下错误
```
The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing a using directive or an assembly reference?)
```
要解决这个问题,需要在`Project Settings/Player/Other Settings/Scripting Define Symbols`中添加`ENABLE_DUMMY_SPAN`指令.
</br>
# 反馈
+ Discord (RU-EN) [https://discord.gg/kqmJjExuCf](https://discord.gg/kqmJjExuCf)
+ QQ (中文) [949562781](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=IbDcH43vhfArb30luGMP1TMXB3GCHzxm&authKey=s%2FJfqvv46PswFq68irnGhkLrMR6y9tf%2FUn2mogYizSOGiS%2BmB%2B8Ar9I%2Fnr%2Bs4oS%2B&noverify=0&group_code=949562781)
</br>
# License
The MIT License: [Open](LICENSE.md)
</br></br></br>
</br></br></br>
</br></br></br>

440
README.md
View File

@ -22,33 +22,38 @@
<tr>
<td nowrap width="100">
<a href="README-RU.md">
<img src="https://github.com/user-attachments/assets/7bc29394-46d6-44a3-bace-0a3bae65d755"><br/>
<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">
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"><br/>
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"></br>
<span>English</span>
</a>
</td>
<td nowrap width="100">
<a href="README-ZH.md">
<img src="https://github.com/user-attachments/assets/8e598a9a-826c-4a1f-b842-0c56301d2927"><br/>
<img src="https://github.com/user-attachments/assets/8e598a9a-826c-4a1f-b842-0c56301d2927"></br>
<span>中文</span>
</a>
</td>
</tr>
</table>
The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims to maximize usability, modularity, extensibility and performance for dynamic entity changes, without code generation or external dependencies. Inspired by [LeoEcs Lite](https://github.com/Leopotam/ecslite).
</br>
> [!Note]
> Have questions or want to stay updated on development? Join our community! -> [Feedback](#Feedback)
The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims to maximize usability, modularity, extensibility and performance of dynamic entity changes. Without code generation and dependencies. Inspired by [LeoEcs Lite](https://github.com/Leopotam/ecslite).
> [!WARNING]
> The project is a work in progress, API may change.
>
> The most current version of the README is in [Russian version](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md).
>
> If there are unclear points, you can ask questions here [Feedback](#Feedback)
## Table of Contents
- [Installation](#installation)
- [Extensions](#extensions)
- [Basic Concepts](#basic-concepts)
- [Entity](#entity)
- [Component](#component)
@ -79,62 +84,39 @@ The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims
- [Extensions](#extensions)
- [FAQ](#faq)
- [Feedback](#feedback)
- [License](#license)
</br>
# Installation
Versioning semantics - [Open](https://gist.github.com/DCFApixels/af79284955bf40e9476cdcac79d7b098#file-dcfapixels_versioning-md)
Versioning semantics - [Open](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
## Environment
Requirements:
* Minimum C# version: 7.3.
Supported:
* NativeAOT;
* Game engines with C#: Unity, Godot, MonoGame, etc.
+ Minimum version of C# 7.3;
Optional:
+ Support for NativeAOT
+ Game engines with C#: Unity, Godot, MonoGame, etc.
Tested with:
* **Unity:** Minimum version 2021.2.0.
+ **Unity:** Minimum version 2020.1.0;
## Unity Installation
> It is also recommended to install the Unity engine integration extension [Unity integration](https://github.com/DCFApixels/DragonECS-Unity)
* ### Unity package
Installation as a Unity package is supported by adding the Git URL [in PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html):
* ### 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`:
```
https://github.com/DCFApixels/DragonECS.git
```
Or add the package entry to `Packages/manifest.json`:
```
"com.dcfa_pixels.dragonecs": "https://github.com/DCFApixels/DragonECS.git",
```
* ### Source Code
The framework can also be added to the project as source code.
* ### As source code
The framework sources can be copied directly into the project.
# Extensions
* Integrations:
* [Unity](https://github.com/DCFApixels/DragonECS-Unity)
* [Godot](https://gitlab.com/InauniusOwn/Libraries/DraGodot)
* Packages:
* [Dependency autoinjections](https://github.com/DCFApixels/DragonECS-AutoInjections)
* [Classic C# multithreading](https://github.com/DCFApixels/DragonECS-ClassicThreads)
* [Recursivity](https://github.com/DCFApixels/DragonECS-Recursivity)
* [Hybrid](https://github.com/DCFApixels/DragonECS-Hybrid)
* [Graphs](https://github.com/DCFApixels/DragonECS-Graphs)
* Utilities:
* [Simple syntax](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)
* [EcsRefPool](https://gist.github.com/DCFApixels/73e392ccabdd98b3d4a517017d8a3f22)
* [Timers](https://gist.github.com/DCFApixels/71a416275660c465ece76242290400df)
* [One-Frame Components](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60)
* [Code Templates for IDE](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) and [for Unity](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
> *Your extension? Extensions for DragonECS are welcome, you can share it [here](#feedback).
</br>
# Basic Concepts
## Entity
Container for components. Two identifier types are used to reference entities:
* `int` - short-lived identifier, valid within a single tick. Not recommended for long-term storage;
* `entlong` - long-term identifier that includes a generation tag, making it unique across entity lifetimes. Suitable for long-term storage.
```c#
Сontainer for components. They are implemented as identifiers, of which there are two types:
* `int` - a short-term identifier used within a single tick. Storing `int` identifiers is not recommended, use `entlong` instead;
* `entlong` - long-term identifier, contains a full set of information for unique identification;
``` c#
// Creating a new entity in the world.
int entityID = _world.NewEntity();
@ -151,7 +133,7 @@ int newEntityID = _world.CloneEntity(entityID);
<details>
<summary>Working with entlong</summary>
```c#
``` c#
// Convert int to entlong.
entlong entity = _world.GetEntityLong(entityID);
// or
@ -171,7 +153,7 @@ if (entity.TryGetID(out int entityID)) { }
</details>
> Entities cannot exist without components. Removing the last component automatically deletes the entity along with it.
> Entities cannot exist without components, empty entities will be automatically deleted immediately after the last component is deleted.
## Component
Data for entities.
@ -187,7 +169,7 @@ struct PlayerTag : IEcsTagComponent {}
```
## System
Represents the core logic defining entity behaviors. Systems are implemented as user-defined classes that implement one or more process interfaces. Key processes include:
Represent the core logic defining entity behaviors. They are implemented as user-defined classes that implement at least one of the process interfaces. Key processes include:
```c#
class SomeSystem : IEcsPreInit, IEcsInit, IEcsRun, IEcsDestroy
{
@ -210,11 +192,11 @@ class SomeSystem : IEcsPreInit, IEcsInit, IEcsRun, IEcsDestroy
# Framework Concepts
## Pipeline
Container and engine of systems. Responsible for setting up the system call queue, providing mechanisms for communication between systems, and dependency injection. Implemented as the `EcsPipeline` class.
Container and engine of systems. Responsible for setting up the system call queue, provides mechanisms for communication between systems, and dependency injection. Implemented as the `EcsPipeline` class.
### Building
Builder is responsible for building the pipeline. Systems are added to the Builder, and the pipeline is built at the end. Example:
Builder is responsible for building the pipeline. Systems are added to the Builder and at the end, the pipeline is built. Example:
```c#
EcsPipeline pipeline = EcsPipeline.New() // Creates a pipeline builder.
EcsPipeline pipeline = EcsPipeline.New() // Создает Builder пайплайна.
// Adds System1 to the systems queue.
.Add(new System1())
// Adds System2 to the queue after System1.
@ -235,12 +217,12 @@ class SomeSystem : IEcsRun, IEcsPipelineMember
public void Run () { }
}
```
> For simultaneous building and initialization, use `Builder.BuildAndInit()`.
> For simultaneous building and initialization, there is the method `Builder.BuildAndInit();`
### Dependency Injection
The framework implements dependency injection for systems. Injection process occurs during pipeline initialization and injects data passed to the Builder.
> Built-in Dependency injection is optional.
```c#
The framework implements dependency injection for systems. This process begins during pipeline initialization and injects data passed to the Builder.
> Using built-in dependency injection is optional.
``` c#
class SomeDataA { /* ... */ }
class SomeDataB : SomeDataA { /* ... */ }
@ -262,7 +244,7 @@ EcsPipeline pipeline = EcsPipeline.New()
// Injection uses the interface IEcsInject<T> and its method Inject(T obj).
class SomeSystem : IEcsInject<SomeDataA>, IEcsRun
{
SomeDataA _someDataA;
SomeDataA _someDataA
// obj will be an instance of type SomeDataB.
public void Inject(SomeDataA obj) => _someDataA = obj;
@ -274,8 +256,8 @@ class SomeSystem : IEcsInject<SomeDataA>, IEcsRun
```
### Modules
Groups of systems that implement a common feature can be grouped into modules and added to the Pipeline.
```c#
Groups of systems that implement a common feature can be grouped into modules and easily added to the Pipeline.
``` c#
using DCFApixels.DragonECS;
class Module1 : IEcsModule
{
@ -297,64 +279,62 @@ EcsPipeline pipeline = EcsPipeline.New()
```
### Sorting
To manage system order in the pipeline regardless of addition order, use Layers and Sorting Order.
To manage the position of systems in the pipeline, regardless of the order in which they are added, there are two methods: Layers and Sorting Order.
#### Layers
Queues of systems can be segmented into layers. A layer defines a position in the queue for inserting systems. For example, to insert a system at the end of the queue regardless of where it is added, add it to the `EcsConsts.END_LAYER` layer.
Queues in the system can be segmented into layers. A layer defines a position in the queue for inserting systems. For example, if a system needs to be inserted at the end of the queue regardless of where it is added, you can add this system to the `EcsConsts.END_LAYER` layer.
``` c#
const string SOME_LAYER = nameof(SOME_LAYER);
EcsPipeline pipeline = EcsPipeline.New()
// ...
// Inserts a new layer before the end layer EcsConsts.END_LAYER
.Layers.Add(SOME_LAYER).Before(EcsConsts.END_LAYER)
// Inserts SOME_LAYER_2 layer at the position after EcsConsts.BASIC_LAYER
.Layers.Add(SOME_LAYER).After(EcsConsts.BASIC_LAYER)
.Layers.Insert(EcsConsts.END_LAYER, SOME_LAYER)
// System SomeSystem will be added to the SOME_LAYER layer
.Add(new SomeSystem(), SOME_LAYER)
.Add(New SomeSystem(), SOME_LAYER)
// ...
.BuildAndInit();
```
The built-in layers are arranged in the following order:
* `EcsConsts.PRE_BEGIN_LAYER`
* `EcsConsts.BEGIN_LAYER`
* `EcsConsts.BASIC_LAYER` (Systems are added here if no layer is specified during addition)
* `EcsConsts.END_LAYER`
* `EcsConsts.POST_END_LAYER`
* `EcsConst.PRE_BEGIN_LAYER`
* `EcsConst.BEGIN_LAYER`
* `EcsConst.BASIC_LAYER` (Systems are added here if no layer is specified during addition)
* `EcsConst.END_LAYER`
* `EcsConst.POST_END_LAYER`
#### Sorting Order
The sort order integer value is used to sort systems within a layer. By default, systems are added with `sortOrder = 0`.
The sort order int value is used to sort systems within a layer. By default, systems are added with `sortOrder = 0`.
```c#
EcsPipeline pipeline = EcsPipeline.New()
// ...
// System SomeSystem will be inserted into the layer EcsConsts.BEGIN_LAYER
// and placed after systems with sortOrder less than 10.
.Add(new SomeSystem(), EcsConsts.BEGIN_LAYER, 10)
.Add(New SomeSystem(), EcsConsts.BEGIN_LAYER, 10)
// ...
.BuildAndInit();
```
## Processes
Processes are queues of systems that implement a common interface, such as `IEcsRun`. Runners are used to start processes. Built-in processes are started automatically; custom processes can be implemented.
Processes are queues of systems that implement a common interface, such as `IEcsRun`. Runners are used to start processes. Built-in processes are started automatically. It is possible to implement custom processes.
<details>
<summary>Built-in processes</summary>
* `IEcsPreInit`, `IEcsInit`, `IEcsRun`, `IEcsDestroy` - lifecycle processes of `EcsPipeline`.
* `IEcsInject<T>` - [Dependency Injection](#Dependency-Injection) processes.
* `IOnInitInjectionComplete` - Similar to the [Dependency Injection](#Dependency-Injection) process, but signals completion of initialization injection.
* `IOnInitInjectionComplete` - Similar to the [Dependency Injection](#Dependency-Injection) process, but signals the completion of initialization injection.
</details>
<details>
<summary>Custom Processes</summary>
To add a new process, create an interface inheriting from `IEcsProcess` and create a runner for it. A runner is a class that implements the process interface and inherits from `EcsRunner<TInterface>`. Example:
To add a new process, create an interface inherited from `IEcsProcess` and create a runner for it. A runner is a class that implements the interface of the process to be run and inherits from `EcsRunner<TInterface>`. Example:
``` c#
// Process interface.
interface IDoSomethingProcess : IEcsProcess
{
void Do();
}
// Runner implementation. An example of implementation can also be seen in built-in processes.
// Implementation of a runner. An example of implementation can also be seen in built-in processes.
sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>, IDoSomethingProcess
{
public void Do()
@ -372,10 +352,10 @@ _pipeline = EcsPipeline.New()
.BuildAndInit();
// Running the runner if it was added
_pipeline.GetRunner<IDoSomethingProcess>().Do();
_pipeline.GetRunner<IDoSomethingProcess>.Do()
// or if the runner was not added (calling GetRunnerInstance will also add the runner to the pipeline).
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>().Do();
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>.Do()
```
<details>
@ -387,9 +367,10 @@ internal sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>,
// RunHelper simplifies the implementation similar to the built-in processes implementation.
// It automatically triggers the profiler marker and also includes a try-catch block.
private RunHelper _helper;
private RunHelper _helper;
protected override void OnSetup()
{
// The second argument specifies the name of the marker; if not specified, the name will be chosen automatically.
// The second argument specifies the name of the marker, if not specified, the name will be chosen automatically.
_helper = new RunHelper(this, nameof(Do));
}
public void Do()
@ -401,15 +382,17 @@ internal sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>,
</details>
> Runner requirements:
> Runners have several implementation requirements:
> * Inheritance from `EcsRunner<T>` must be direct.
> * A runner may only contain one interface (excluding `IEcsProcess`).
> * The inheriting class of `EcsRunner<T>` must also implement the `T` interface.
> * Runner can only contain one interface (except `IEcsProcess`);
> * The inheriting class of `EcsRunner<T>,` must also implement the `T` interface;
> It's not recommended to call `GetRunner` in a loop; instead, cache the retrieved runner instance.
</details>
## World
A container for entities and components.
Is a container for entities and components.
``` c#
// Creating an instance of the world.
_world = new EcsDefaultWorld();
@ -417,10 +400,10 @@ _world = new EcsDefaultWorld();
var e = _world.NewEntity();
_world.DelEntity(e);
```
> **NOTICE:** Call `EcsWorld.Destroy()` on the world instance when it is no longer needed to release resources. Otherwise it will remain in memory.
> **NOTICE:** It's necessary to call EcsWorld.Destroy() on the world instance when it's no longer needed, otherwise it will remain in memory.
### World Configuration
To initialize the world with a required size upfront and reduce warm-up time, pass an `EcsWorldConfig` instance to the constructor.
To initialize the world with a required size upfront and reduce warm-up time, you can pass an `EcsWorldConfig` instance to the constructor.
``` c#
EcsWorldConfig config = new EcsWorldConfig(
@ -432,11 +415,11 @@ _world = new EcsDefaultWorld(config);
```
## Pool
Stash of components, providing methods for adding, reading, editing, and removing components on entities. Several pool types are available:
* `EcsPool` - universal pool for struct components implementing `IEcsComponent`;
* `EcsTagPool` - pool optimized for tag components implementing `IEcsTagComponent`;
Stash of components, providing methods for adding, reading, editing, and removing components on entities. There are several types of pools designed for different purposes:
* `EcsPool` - universal pool, stores struct components implementing the `IEcsComponent` interface;
* `EcsTagPool` - special pool optimized for tag components, stores struct-components with `IEcsTagComponent`;
Pools provide 5 common methods and their variations:
Pools have 5 main methods and their variations:
``` c#
// One way to get a pool from the world.
EcsPool<Pose> poses = _world.GetPool<Pose>();
@ -444,8 +427,8 @@ EcsPool<Pose> poses = _world.GetPool<Pose>();
// Adds component to entity, throws an exception if the entity already has the component.
ref var addedPose = ref poses.Add(entityID);
// Returns existing component, throws an exception if the entity does not have this component.
ref var gottenPose = ref poses.Get(entityID);
// Returns exist component, throws an exception if the entity does not have this component.
ref var gettedPose = ref poses.Get(entityID);
// Returns a read-only component, throwing an exception if the entity does not have this component.
ref readonly var readonlyPose = ref poses.Read(entityID);
@ -456,33 +439,12 @@ if (poses.Has(entityID)) { /* ... */ }
// Removes component from entity, throws an exception if the entity does not have this component.
poses.Del(entityID);
```
> [!WARNING]
> Exceptions are disabled in the `Release` build.
> There are "Safe" methods that first check presence or absence of a component are prefixed with `Try`.
<details>
<summary>Custom Pools</summary>
A pool can be any type that implements the IEcsPoolImplementation<T> interface and has a parameterless constructor.
Key points when implementing a pool:
* For an example of a pool implementation, reference can be made to the implementation of the built-in EcsPool<T>.
* The IEcsPoolImplementation interface and its members are not intended for public use; the interface members should be implemented explicitly.
* The type T substituted in the IEcsPoolImplementation<T> interface and the type returned by the ComponentType and ComponentTypeID properties must match.
* All pool changes must be registered with the EcsWorld.PoolsMediator instance passed in the OnInit method.
* EcsWorld.PoolsMediator is intended for use only inside the pool.
* The DISABLE_POOLS_EVENTS define disables the implemented AddListener and RemoveListener methods.
* The static class EcsPoolThrowHelper defines throwing of the most common exception types.
* The OnReleaseDelEntityBuffer method handles cleanup of deleted entities.
* It is recommended to define an interface that marks components intended for the new pool. Based on this interface, extension methods such as GetPool<T>() can be implemented to simplify access to pools.
* Pools must implement locking. Pool locking works only in Debug mode and should throw exceptions when attempting to add or remove a component.
</details>
> There are "safe" methods that first perform a check for the presence or absence of a component. Such methods are prefixed with `Try`.
> It is possible to implement a user pool. This feature will be described shortly.
## Mask
Used to filter entities by presence or absence of components. Usually masks are not used standalone, they are part of `EcsAspect` and used by queries to filter entities.
Used to filter entities by the presence or absence of components.
``` c#
// Creating a mask that checks if entities have components
// SomeCmp1 and SomeCmp2, but do not have component SomeCmp3.
@ -498,7 +460,7 @@ EcsMask mask = EcsMask.New(_world)
<details>
<summary>Static Mask</summary>
`EcsMask` is tied to specific world instances, which need to be passed to `EcsMask.New(world)`. `EcsStaticMask` can be created without a world reference.
`EcsMask` is tied to specific world instances, which need to be passed to `EcsMask.New(world)`, but there is also `EcsStaticMask`, which can be created without being tied to a world.
``` c#
class SomeSystem : IEcsRun
@ -517,11 +479,7 @@ EcsMask mask = _staticMask.ToMask(_world);
</details>
## Aspect
User-defined classes that inherit from `EcsAspect` and describe sets of components a system works with. An aspect serves two purposes:
- Mask — initializes and holds an `EcsMask` used by queries for filtering entities.
- Pool cache — provides fast access to component pools.
In short, an aspect is a convenient way to declare "which entities I work with and how to access their components."
These are custom classes inherited from `EcsAspect` and used to interact with entities. Aspects are both a pool cache and a component mask for filtering entities. You can think of aspects as a description of what entities the system is working with.
Simplified syntax:
``` c#
@ -538,20 +496,11 @@ class Aspect : EcsAspect
// During queries, it checks for the presence of components
// in the inclusive constraint and absence in the exclusive constraint.
// Opt only caches the pool without affecting the mask.
// There is also Opt - it only caches the pool without affecting the mask.
}
```
Purpose of the static markers:
* `Inc` — component must be present (inclusive) and caches the pool.
* `Exc` — component must NOT be present (exclusive) and caches the pool.
* `Opt` — component may be present; does not affect filtering (only caches the pool).
* `Any` — at least one of the components marked with `Any` must be present; caches the pool.
<details>
<summary>Initialization via a method (the result is identical to the example above):</summary>
Explicit syntax (equivalent result):
Explicit syntax (the result is identical to the example above):
``` c#
using DCFApixels.DragonECS;
// ...
@ -561,19 +510,17 @@ class Aspect : EcsAspect
public EcsPool<Velocity> velocities;
protected override void Init(Builder b)
{
poses = b.Inc<Pose>();
velocities = b.Inc<Velocity>();
b.Exc<FreezedTag>();
poses = b.Include<Pose>();
velocities = b.Include<Velocity>();
b.Exclude<FreezedTag>();
}
}
```
</details>
<details>
<summary>Combining aspects</summary>
Aspects can be combined by adding other aspects; constraints are merged accordingly.
Aspects can have additional aspects added to them, thus combining them. The constraints will also be combined accordingly.
``` c#
using DCFApixels.DragonECS;
// ...
@ -585,32 +532,31 @@ class Aspect : EcsAspect
protected override void Init(Builder b)
{
// Combines with OtherAspect1.
// Combines with SomeAspect1.
otherAspect1 = b.Combine<OtherAspect1>(1);
// Although Combine was called earlier for OtherAspect1, it will first combine with OtherAspect2 because the default order is 0.
otherAspect2 = b.Combine<OtherAspect2>();
// If b.Exc<Pose>() was specified in OtherAspect1 or OtherAspect2, it will be replaced with b.Inc<Pose>() here.
poses = b.Inc<Pose>();
// If b.Exclude<Pose>() was specified in OtherAspect1 or OtherAspect2, it will be replaced with b.Include<Pose>() here.
poses = b.Include<Pose>();
}
}
```
If conflicting constraints exist between combined aspects, the newer constraints replace earlier ones. Constraints from the root aspect always replace constraints from added aspects. Example of constraint combination:
| | cmp1 | cmp2 | cmp3 | cmp4 | cmp5 | Conflict resolution |
If there are conflicting constraints between the combined aspects, the new constraints will replace those added earlier. Constraints from the root aspect always replace constraints from added aspects. Here's a visual example of constraint combination:
| | cmp1 | cmp2 | cmp3 | cmp4 | cmp5 | разрешение конфликтных ограничений|
| :--- | :--- | :--- | :--- | :--- | :--- |:--- |
| OtherAspect2 | :heavy_check_mark: | :x: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | |
| OtherAspect1 | :heavy_minus_sign: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_minus_sign: | For `cmp2`, :heavy_check_mark: will be chosen. |
| Aspect | :x: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | For `cmp1`, :x: will be chosen. |
| OtherAspect1 | :heavy_minus_sign: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_minus_sign: | For `cmp2` will be chosen. :heavy_check_mark: |
| Aspect | :x: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | For `cmp1` will be chosen. :x: |
| Final Constraints | :x: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_check_mark: | |
</details>
## Queries
Filter entities and return collections of entities that match conditions. The built-in `Where` query filters by component mask and has several overloads:
+ `EcsWorld.Where(EcsMask mask)` - standard filtering by mask;
+ `EcsWorld.Where<TAspect>(out TAspect aspect)` - combines aspect mask filtering and returns the aspect;
Filter entities and return collections of entities that matching conditions. The built-in `Where` query filters by component mask matching and has several overloads:
+ `EcsWorld.Where(EcsMask mask)` - Standard filtering by mask;
+ `EcsWorld.Where<TAspect>(out TAspect aspect)` - Combines filtering by aspect mask and aspect return;
`Where` can be applied to both `EcsWorld` and framework collections (similar to LINQ). Overloads for sorting using `Comparison<int>` are available.
The `Where` query can be applied to both `EcsWorld` and framework collections (in this sense, `Where` is somewhat similar to the one in Linq). There are also overloads for sorting entities using `Comparison<int>`.
Example system:
``` c#
@ -651,7 +597,7 @@ public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
## Collections
### EcsSpan
A `ref struct` collection of entities, available only for reading. Consists of a reference to an array, a length, and a world identifier. Similar to `ReadOnlySpan<int>`.
Collection of entities that is read-only and stack-allocated. It consists of a reference to an array, its length, and the world identifier. Similar to `ReadOnlySpan<int>`.
``` c#
// Where query returns entities as EcsSpan.
EcsSpan es = _world.Where(out Aspect a);
@ -666,13 +612,13 @@ for (int i = 0; i < es.Count; i++)
// ...
}
```
> Although `EcsSpan` is an array reference, duplicate entities are not allowed.
> Although `EcsSpan` is just an array, it does not allow duplicate entities.
### EcsGroup
Sparse Set based auxiliary collection for storing a set of entities with O(1) add/delete/check operations, etc.
``` c#
// Getting a new group. EcsWorld contains a pool of groups,
// a new one will be created or a free one reused.
// Getting a new group. EcsWorld contains pool of groups,
// so a new one will be created or a free one will be reused.
EcsGroup group = EcsGroup.New(_world);
// Release the group.
group.Dispose();
@ -699,36 +645,32 @@ for (int i = 0; i < group.Count; i++)
// ...
}
```
Groups implement the `ISet<int>` interface. Editing methods have two variants: one that writes the result to `groupA`, and another that returns a new group.
Groups are sets and implement the `ISet<int>` interface. The editing methods have two variants: one that writes the result to `groupA`, and another that returns a new group.
``` c#
// Union of groupA and groupB.
groupA.UnionWith(groupB);
EcsGroup newGroup = EcsGroup.Union(groupA, groupB);
```
``` c#
// Intersection of groupA and groupB.
groupA.IntersectWith(groupB);
EcsGroup newGroup = EcsGroup.Intersect(groupA, groupB);
```
``` c#
// Difference of groupA and groupB.
groupA.ExceptWith(groupB);
EcsGroup newGroup = EcsGroup.Except(groupA, groupB);
```
``` c#
// Symmetric difference of groupA and groupB.
groupA.SymmetricExceptWith(groupB);
EcsGroup newGroup = EcsGroup.SymmetricExcept(groupA, groupB);
```
``` c#
// Difference of all entities in world and groupA.
groupA.Inverse();
EcsGroup newGroup = EcsGroup.Inverse(groupA);
```
## ECS Root
A custom class that serves as the entry point for ECS. Its main purpose is to initialize systems, run them on each engine update, and release resources when no longer needed.
This is a custom class that is the entry point for ECS. Its main purpose is to initialize, start systems on each engine Update and release resources when no longer needed.
### Example for Unity
``` c#
using DCFApixels.DragonECS;
@ -782,7 +724,7 @@ public class EcsRoot
{
private EcsPipeline _pipeline;
private EcsDefaultWorld _world;
// Engine initialization.
// Engine initialization .
public void Init()
{
// Creating world for entities and components.
@ -794,7 +736,7 @@ public class EcsRoot
// .Add(new SomeSystem2())
// .Add(new SomeSystem3())
// Injecting world into systems.
// Внедрение мира в системы.
.Inject(_world)
// Other injections.
// .Inject(SomeData)
@ -829,7 +771,7 @@ public class EcsRoot
</br>
# Debug
The framework provides tools for debugging and logging, independent of the environment. Many types have DebuggerProxy implementations for more informative display in IDEs.
The framework provides additional tools for debugging and logging, independent of the environment. Also many types have their own DebuggerProxy for more informative display in IDE.
## Meta Attributes
By default, meta-attributes have no use, but are used in integrations with engines to specify display in debugging tools and editors. And can also be used to generate automatic documentation.
``` c#
@ -867,47 +809,12 @@ var description = typeMeta.Description; // [MetaDescription]
var metaID = typeMeta.MetaID; // [MetaID]
var tags = typeMeta.Tags; // [MetaTags]
```
> To simplify generate unique MetaID values, use `MetaID.GenerateNewUniqueID()` or the [Browser Generator](https://dcfapixels.github.io/DragonECS-MetaID_Generator_Online/).
<details>
<summary>[MetaProxy]</summary>
The `[MetaProxy(typeof(MetaProxyType))]` attribute enables flexible metadata configuration. It allows bypassing the limitations of regular attributes (for example, the inability to pass non-constant data into them) and setting metadata programmatically. Furthermore, metadata obtained via `MetaProxy` is inherited.
The API resembles `[DebuggerTypeProxy]`, but instead of an object instance, it accepts a Type. The class for `MetaProxy` must inherit from `MetaProxyBase`.
Usage example:
``` c#
// Applying the attribute
[MetaProxy(typeof(UnityComponent<>.MetaProxy))]
// In this example, UnityComponent wraps a Unity component
public struct UnityComponent<T> where T : Component
{
// ...
// Implementation of MetaProxyBase. MetaProxy copies the metadata of the wrapped type
// so that its metadata is displayed in the Unity inspector.
private class MetaProxy : MetaProxyBase
{
protected TypeMeta Meta = typeof(T).GetMeta();
public override string Name { get { return Meta?.Name; } }
public override MetaColor? Color { get { return Meta != null && Meta.IsCustomColor ? Meta.Color : null; } }
public override MetaGroup Group { get { return Meta?.Group; } }
public override MetaDescription Description { get { return Meta?.Description; } }
public override IEnumerable<string> Tags { get { return Meta?.Tags; } }
public MetaProxy(Type type) : base(type) { }
}
}
```
</details>
> To automatically generate unique identifiers MetaID, there is the method `MetaID.GenerateNewUniqueID()` and the [Browser Generator](https://dcfapixels.github.io/DragonECS-MetaID_Generator_Online/).
## EcsDebug
Provides methods for debugging and logging. Implemented as a static class that forwards calls to a Debug service. Debug services act as intermediaries between environment-specific debugging systems and EcsDebug, enabling portability.
Has a set of methods for debugging and logging. It is implemented as a static class calling methods of Debug services. Debug services are intermediaries between the debugging systems of the environment and EcsDebug. This allows projects to be ported to other engines without modifying the debug code, by implementing the corresponding Debug service.
By default, `DefaultDebugService` outputs logs to the console. To implement a custom service, inherit from `DebugService` and implement its abstract members.
By default, `DefaultDebugService` is used, which outputs logs to the console. To implement a custom one, create a class inherited from `DebugService` and implement abstract class members.
``` c#
// Output log.
@ -946,25 +853,26 @@ using (_marker.Auto())
# Define Symbols
+ `DRAGONECS_DISABLE_POOLS_EVENTS` - Disables reactive behavior in pools.
+ `DRAGONECS_ENABLE_DEBUG_SERVICE` - Enables EcsDebug functionality in release builds.
+ `DRAGONECS_STABILITY_MODE` - By default, many exception checks are skipped in release builds for performance. This define replaces checks with error-resilient code to increase stability at the cost of some performance.
+ `DRAGONECS_STABILITY_MODE` - By default, for optimization purposes, the framework skips many exception checks in the release build. This define, instead of disabling checks, replaces them with code that resolves errors. This increases stability but reduces execution speed.
+ `DRAGONECS_DISABLE_CATH_EXCEPTIONS` - Turns off the default exception handling behavior. By default, the framework will catch exceptions with the exception information output via EcsDebug and continue working.
+ `REFLECTION_DISABLED` - Restricts the framework's use of Reflection.
+ `DISABLE_DEBUG` - For environments where manual DEBUG disabling is not supported (e.g., Unity).
+ `REFLECTION_DISABLED` - completely restricts the framework's use of Reflection.
+ `DISABLE_DEBUG` - for environments where manual DEBUG disabling is not supported, e.g., Unity.
+ `ENABLE_DUMMY_SPAN` - For environments where Span types are not supported, enables its replacement.
</br>
# Framework Extension Tools
Additional tools improve framework extensibility.
There are additional tools for greater extensibility of the framework.
## Configs
Constructors of `EcsWorld` and `EcsPipeline` accept config containers implementing `IConfigContainer` or `IConfigContainerWriter`. These containers pass data and dependencies. The built-in container implementation is `ConfigContainer`. Сustom implementations are supported.
Constructors of `EcsWorld` and `EcsPipeline` classes can accept config containers implementing `IConfigContainer` or `IConfigContainerWriter` interface. These containers can be used to pass data and dependencies. The built-in container implementation is `ConfigContainer`, but you can also use your own implementation.</br>
Example of using configs for EcsWorld:
``` c#
var configs = new ConfigContainer()
.Set(new EcsWorldConfig(entitiesCapacity: 2000, poolsCapacity: 2000))
.Set(new EcsWorldConfig(entitiesCapacity: 2000, poolsCapacity: 2000)
.Set(new SomeDataA(/* ... */))
.Set(new SomeDataB(/* ... */));
.Set(new SomeDataB(/* ... */)));
EcsDefaultWorld _world = new EcsDefaultWorld(configs);
// ...
var _someDataA = _world.Configs.Get<SomeDataA>();
@ -972,7 +880,7 @@ var _someDataB = _world.Configs.Get<SomeDataB>();
```
Example of using configs for EcsPipeline:
``` c#
_pipeline = EcsPipeline.New() // similarly _pipeline = EcsPipeline.New(new ConfigContainer())
_pipeline = EcsPipeline.New()// similarly _pipeline = EcsPipeline.New(new ConfigContainer())
.Configs.Set(new SomeDataA(/* ... */))
.Configs.Set(new SomeDataB(/* ... */))
// ...
@ -983,20 +891,20 @@ var _someDataB = _pipeline.Configs.Get<SomeDataB>();
```
## World Components
World components attach additional data to worlds. World components are `struct` types. Access via `Get` is optimized and performs similarly to class field access.
Components can be used to attach additional data to worlds. World components are `struct` types. Access to components via `Get` is optimized, the speed is almost the same as access to class fields.
Get component:
``` c#
ref WorldComponent component = ref _world.Get<WorldComponent>();
```
Component implementation:
Component Implementation:
``` c#
public struct WorldComponent
{
// Data.
}
```
Or with lifecycle callbacks:
Or:
``` c#
public struct WorldComponent : IEcsWorldComponent<WorldComponent>
{
@ -1009,7 +917,7 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
void IEcsWorldComponent<WorldComponent>.OnDestroy(ref WorldComponent component, EcsWorld world)
{
// Actions when EcsWorld.Destroy is called.
// OnDestroy requires manual reset of the component if necessary.
// Calling OnDestroy, obliges the user to manually reset the component if necessary.
component = default;
}
}
@ -1018,12 +926,12 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
<details>
<summary>Example of use</summary>
The `IEcsWorldComponent<T>` interface events can be used to initialize component fields and release resources automatically.
IEcsWorldComponent<T> interface events, can be used to automatically initialize component fields, and release resources.
``` c#
public struct WorldComponent : IEcsWorldComponent<WorldComponent>
{
private SomeClass _object; // Object to be disposed.
private SomeReusedClass _reusedObject; // Object to be reused.
private SomeClass _object; // Объект который будет утилизироваться.
private SomeReusedClass _reusedObject; // Объект который будет переиспользоваться.
public SomeClass Object => _object;
public SomeReusedClass ReusedObject => _reusedObject;
void IEcsWorldComponent<WorldComponent>.Init(ref WorldComponent component, EcsWorld world)
@ -1031,16 +939,18 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
if (component._reusedObject == null)
component._reusedObject = new SomeReusedClass();
component._object = new SomeClass();
// After getting the component via EcsWorld.Get, _reusedObject and _object will be ready.
// Теперь при получении компонента через EcsWorld.Get, _reusedObject и _object уже будут созданы.
}
void IEcsWorldComponent<WorldComponent>.OnDestroy(ref WorldComponent component, EcsWorld world)
{
// Dispose and clear the reference so GC can collect it.
// Утилизируем не нужный объект, и освобождаем ссылку на него, чтобы GC мог его собрать.
component._object.Dispose();
component._object = null;
// Optionally reset the reused object.
// component._reusedObject.Reset();
// If full reset is needed, uncomment the line below.
// Как вариант тут можно сделать сброс значений у переиспользуемого объекта.
//component._reusedObject.Reset();
// Так как в этом примере не нужно полное обнуление компонента, то строчка ниже не нужна.
// component = default;
}
}
@ -1059,7 +969,7 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
<td align="center">
<a href="https://github.com/DCFApixels/3D-Platformer-DragonECS-Demo">
3D Platformer (Example)
<img src="https://github.com/user-attachments/assets/6aba814d-a70b-432f-a905-84d1b6872581" />
<img src="https://github.com/user-attachments/assets/c593dba7-eeaa-4706-a043-946f132f3f83" alt="screenshot">
</a>
</td>
<td align="center">
@ -1069,43 +979,11 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
</a>
</td>
</tr>
<tr></tr>
<tr>
<td align="center">
<a href="https://github.com/Evileptic/Arkanoid">
Arkanoid
<img src="https://github.com/user-attachments/assets/bbdb4a7f-2f59-4a3a-ab51-a3e4fe0ad35e" alt="screenshot">
</a>
</td>
<td align="center">
_____________
<img tabindex="-1" src="https://github.com/user-attachments/assets/3fa1ca6d-29f6-43e6-aafe-cc9648d20490" alt="screenshot">
</td>
</tr>
</table>
## Released games:
<table>
<tr>
<td align="center">
<a href="https://dcfapixels.github.io/Project8.html">
Crystal Siege
<img src="https://github.com/user-attachments/assets/1aa60a50-2668-4919-aca9-d6d2b980c3dd">
</a>
</td>
<td align="center">
<a href="https://play.google.com/store/apps/details?id=com.ZlodeyStudios.OrdersMatter">
Order matters
<img src="https://github.com/user-attachments/assets/c55b2647-9b6e-4145-98ff-c3d094600fa1">
</a>
</td>
</tr>
<tr></tr>
<tr>
<td align="center">
<a href="https://yandex.ru/games/app/206024?utm_source=game_popup_menu">
@ -1114,26 +992,39 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
</a>
</td>
<td align="center">
_____________
<span>
<img tabindex="-1" src="https://github.com/user-attachments/assets/3fa1ca6d-29f6-43e6-aafe-cc9648d20490" alt="screenshot">
</span>
</td>
</tr>
</table>
</br>
# Extensions
* Packages:
* [Unity integration](https://github.com/DCFApixels/DragonECS-Unity)
* [Dependency autoinjections](https://github.com/DCFApixels/DragonECS-AutoInjections)
* [Classic C# multithreading](https://github.com/DCFApixels/DragonECS-ClassicThreads)
* [Recursivity](https://github.com/DCFApixels/DragonECS-Recursivity)
* [Hybrid](https://github.com/DCFApixels/DragonECS-Hybrid)
* [Graphs](https://github.com/DCFApixels/DragonECS-Graphs)
* Utilities:
* [Simple syntax](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)
* [One-Frame Components](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60)
* [Code Templates for IDE](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) and [for Unity](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
> *Your extension? If you are developing an extension for DragonECS, you can share it [here](#feedback).
</br>
# FAQ
## How to enable/disable systems?
Directly — not supported.
The need to enable or disable systems usually appears when the overall game state changes; this may also mean switching a group of systems. Conceptually this is a change of processes. Two solutions are recommended:
- For global process changes, create a new `EcsPipeline` and run the appropriate pipeline in the engine update loop.
- Split `IEcsRun` into multiple processes and run the desired process in the engine update loop. Create a new process interface, implement a runner for it, and obtain the runner via `EcsPipeline.GetRunner<T>()`.
## Recommendations list: [DragonECS-Vault](https://github.com/DCFApixels/DragonECS-Vault)
## 'ReadOnlySpan<>' could not be found
In Unity 2020.1.x, you may encounter this error in the console:
```
The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing a using directive or an assembly reference?)
```
To fix this, add the define symbol `ENABLE_DUMMY_SPAN` to `Project Settings/Player/Other Settings/Scripting Define Symbols`.
</br>
@ -1141,11 +1032,6 @@ The need to enable or disable systems usually appears when the overall game stat
+ Discord (RU-EN) [https://discord.gg/kqmJjExuCf](https://discord.gg/kqmJjExuCf)
+ QQ (中文) [949562781](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=IbDcH43vhfArb30luGMP1TMXB3GCHzxm&authKey=s%2FJfqvv46PswFq68irnGhkLrMR6y9tf%2FUn2mogYizSOGiS%2BmB%2B8Ar9I%2Fnr%2Bs4oS%2B&noverify=0&group_code=949562781)
</br>
# License
The MIT License: [Open](LICENSE.md)
</br></br></br>
</br></br></br>
</br></br></br>

View File

@ -7,8 +7,8 @@
},
"displayName": "DragonECS",
"description": "C# Entity Component System Framework",
"unity": "2021.2",
"version": "1.0.1",
"unity": "2020.3",
"version": "0.9.21",
"repository": {
"type": "git",
"url": "https://github.com/DCFApixels/DragonECS.git"

View File

@ -1,7 +1,7 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.PoolsCore;
namespace DCFApixels.DragonECS
{

View File

@ -1,6 +1,7 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.RunnersCore;
using System;
namespace DCFApixels.DragonECS
@ -59,6 +60,7 @@ namespace DCFApixels.DragonECS.Core.Internal
#endif
[MetaColor(MetaColor.DragonRose)]
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)]
[MetaDescription(EcsConsts.AUTHOR, "...")]
[MetaTags(MetaTags.HIDDEN)]
[MetaID("DragonECS_3273527C9201285BAA0A463F700A50FB")]
internal sealed class EcsPreInitRunner : EcsRunner<IEcsPreInit>, IEcsPreInit
@ -79,6 +81,7 @@ namespace DCFApixels.DragonECS.Core.Internal
#endif
[MetaColor(MetaColor.DragonRose)]
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)]
[MetaDescription(EcsConsts.AUTHOR, "...")]
[MetaTags(MetaTags.HIDDEN)]
[MetaID("DragonECS_ED85527C9201A391AB8EC0B734917859")]
internal sealed class EcsInitRunner : EcsRunner<IEcsInit>, IEcsInit
@ -99,6 +102,7 @@ namespace DCFApixels.DragonECS.Core.Internal
#endif
[MetaColor(MetaColor.DragonRose)]
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)]
[MetaDescription(EcsConsts.AUTHOR, "...")]
[MetaTags(MetaTags.HIDDEN)]
[MetaID("DragonECS_2098527C9201F260C840BFD50BC7E0BA")]
internal sealed class EcsRunRunner : EcsRunner<IEcsRun>, IEcsRun
@ -179,6 +183,7 @@ namespace DCFApixels.DragonECS.Core.Internal
#endif
[MetaColor(MetaColor.DragonRose)]
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)]
[MetaDescription(EcsConsts.AUTHOR, "...")]
[MetaTags(MetaTags.HIDDEN)]
[MetaID("DragonECS_06A6527C92010430ACEB3DA520F272CC")]
internal sealed class EcsDestroyRunner : EcsRunner<IEcsDestroy>, IEcsDestroy

View File

@ -196,7 +196,7 @@ namespace DCFApixels.DragonECS
{
if (_groupSparsePagePoolCount <= 0)
{
return MemoryAllocator.AllocAndInit<int>(EcsGroup.PAGE_SIZE).Ptr;
return MemoryAllocator.AllocAndInit<int>(EcsGroup.PAGE_SIZE).As<int>();
}
var takedPage = _groupSparsePagePool[--_groupSparsePagePoolCount];
_groupSparsePagePool[_groupSparsePagePoolCount] = MemoryAllocator.Handler.Empty;
@ -228,7 +228,7 @@ namespace DCFApixels.DragonECS
for (int i = 0; i < _groupSparsePagePoolCount; i++)
{
ref var page = ref _groupSparsePagePool[i];
if (page.IsCreated)
if (page.IsEmpty == false)
{
MemoryAllocator.FreeAndClear(ref page);
}
@ -291,7 +291,7 @@ namespace DCFApixels.DragonECS
private int _count = 0;
internal bool _isReleased = true;
internal static readonly int* _nullPage = MemoryAllocator.AllocAndInit<int>(PageSlot.SIZE).Ptr;
internal static readonly int* _nullPage = MemoryAllocator.AllocAndInit<int>(PageSlot.SIZE).As<int>();
internal static readonly long _nullPagePtrFake = (long)_nullPage;
#region Properties
@ -344,6 +344,17 @@ namespace DCFApixels.DragonECS
#endif
return _dense[++index];
}
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// set
// {
// // TODO добавить лок енумератора на изменение
//#if DEBUG || DRAGONECS_STABILITY_MODE
// if (index < 0 || index >= Count) { Throw.ArgumentOutOfRange(); }
//#endif
// var oldValue = _dense[index];
// _dense[index] = value;
// _sparse[oldValue] = 0;
// }
}
#endregion
@ -383,7 +394,7 @@ namespace DCFApixels.DragonECS
page.IndexesXOR = 0;
page.Count = 0;
}
_sparsePagesHandler.DisposeAndReset();
_sparsePagesHandler.Dispose();
}
}
public void Dispose()
@ -544,6 +555,7 @@ namespace DCFApixels.DragonECS
ref PageSlot page = ref _sparsePages[i];
if (page.Indexes != _nullPage)
{
//TODO тут надо оптимизировать отчисткой не всего а по dense списку
for (int j = 0; j < PageSlot.SIZE; j++)
{
page.Indexes[j] = 0;

View File

@ -1,7 +1,6 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.Core.Unchecked;
using System;
@ -51,7 +50,7 @@ namespace DCFApixels.DragonECS
public bool IsSourceEntities
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _values == EcsWorld.GetWorld(_worldID).GetCurrentEntities_Internal()._values; }
get { return this == EcsWorld.GetWorld(_worldID).GetCurrentEntities_Internal(); }
}
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.ArrayBoundsChecks, true)]
@ -118,8 +117,8 @@ namespace DCFApixels.DragonECS
#endregion
#region operators
public static bool operator ==(EcsSpan left, EcsSpan right) { return left._values == right._values && left._worldID == right._worldID; }
public static bool operator !=(EcsSpan left, EcsSpan right) { return left._values != right._values || left._worldID != right._worldID; }
public static bool operator ==(EcsSpan left, EcsSpan right) { return left._values == right._values; }
public static bool operator !=(EcsSpan left, EcsSpan right) { return left._values != right._values; }
#endregion
#region Enumerator
@ -128,7 +127,6 @@ namespace DCFApixels.DragonECS
#endregion
#region Other
public ReadOnlySpan<int> AsSystemSpan() { return _values; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int First() { return _values[0]; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -172,7 +170,6 @@ namespace DCFApixels.DragonECS
_worldID = span._worldID;
}
public DebuggerProxy(EcsLongsSpan span) : this(span.ToSpan()) { }
public DebuggerProxy(EcsUnsafeSpan span) : this(span.ToSpan()) { }
}
#endregion
}
@ -319,166 +316,4 @@ namespace DCFApixels.DragonECS
#pragma warning restore CS0809 // Устаревший член переопределяет неустаревший член
#endregion
}
}
namespace DCFApixels.DragonECS.Core
{
#if ENABLE_IL2CPP
using Unity.IL2CPP.CompilerServices;
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
[DebuggerTypeProxy(typeof(EcsSpan.DebuggerProxy))]
public unsafe readonly struct EcsUnsafeSpan
{
private readonly int* _values;
private readonly int _length;
private readonly short _worldID;
#region Properties
public bool IsNull
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _worldID == 0; }
}
public short WorldID
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _worldID; }
}
public EcsWorld World
{
get { return EcsWorld.GetWorld(_worldID); }
}
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _length; }
}
public EcsLongsSpan Longs
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return ToSpan().Longs; }
}
public bool IsSourceEntities
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return ToSpan().IsSourceEntities; }
}
public int this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if DEBUG
if ((uint)index >= (uint)_length || (uint)index < 0)
{
ThrowHelper.ThrowIndexOutOfRangeException();
}
#elif DRAGONECS_STABILITY_MODE
return EcsConsts.NULL_ENTITY_ID;
#endif
return _values[index];
}
}
#endregion
#region Constructors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal EcsUnsafeSpan(short worldID, int* array, int length)
{
_worldID = worldID;
_values = array;
_length = length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal EcsUnsafeSpan(short worldID, int* array, int start, int length)
{
_worldID = worldID;
_values = array + start;
_length = length;
}
#endregion
#region Slice/ToArray
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsUnsafeSpan Slice(int start)
{
if ((uint)start > (uint)_length)
{
ThrowHelper.ThrowArgumentOutOfRangeException();
}
return new EcsUnsafeSpan(_worldID, _values, start, _length - start);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsUnsafeSpan Slice(int start, int length)
{
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
{
ThrowHelper.ThrowArgumentOutOfRangeException();
}
return new EcsUnsafeSpan(_worldID, _values, start, length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsSpan ToSpan() { return new EcsSpan(_worldID, new ReadOnlySpan<int>(_values, _length)); }
public int[] ToArray() { return new ReadOnlySpan<int>(_values, _length).ToArray(); }
public int ToArray(ref int[] dynamicBuffer)
{
if (dynamicBuffer.Length < _length)
{
Array.Resize(ref dynamicBuffer, ArrayUtility.CeilPow2(_length));
}
int i = 0;
foreach (var e in this)
{
dynamicBuffer[i++] = e;
}
return i;
}
public void ToCollection(ICollection<int> collection)
{
foreach (var e in this)
{
collection.Add(e);
}
}
#endregion
#region operators
public static bool operator ==(EcsUnsafeSpan left, EcsUnsafeSpan right) { return left._values == right._values && left._length == right._length && left._worldID == right._worldID; }
public static bool operator !=(EcsUnsafeSpan left, EcsUnsafeSpan right) { return left._values != right._values || left._length != right._length || left._worldID != right._worldID; }
public static implicit operator EcsSpan(EcsUnsafeSpan a) { return a.ToSpan(); }
#endregion
#region Enumerator
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<int>.Enumerator GetEnumerator() { return new ReadOnlySpan<int>(_values, _length).GetEnumerator(); }
#endregion
#region Other
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int First() { return _values[0]; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Last() { return _values[_length - 1]; }
public override string ToString()
{
return CollectionUtility.EntitiesToString(ToArray(), "span");
}
public override bool Equals(object obj)
{
return obj is EcsUnsafeSpan other && other == this;
}
public override int GetHashCode()
{
return *_values ^ _length ^ (_worldID << 16);
}
private static class ThrowHelper
{
public static void ThrowIndexOutOfRangeException() => throw new IndexOutOfRangeException();
public static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException();
public static void ThrowInvalidOperationException() => throw new InvalidOperationException();
}
#endregion
}
}

View File

@ -1,4 +1,6 @@
namespace DCFApixels.DragonECS
using System;
namespace DCFApixels.DragonECS
{
public static class EcsConsts
{
@ -86,6 +88,42 @@
true;
#else
false;
#endif
public const bool ENABLE_DUMMY_SPAN =
#if ENABLE_DUMMY_SPAN
true;
#else
false;
#endif
[Obsolete("DRAGONECS_ENABLE_DEBUG_SERVICE")]
public const bool ENABLE_DRAGONECS_DEBUGGER =
#if ENABLE_DRAGONECS_DEBUGGER
true;
#else
false;
#endif
[Obsolete("DRAGONECS_DISABLE_POOLS_EVENTS")]
public const bool DISABLE_POOLS_EVENTS =
#if DISABLE_POOLS_EVENTS
true;
#else
false;
#endif
[Obsolete("DRAGONECS_DISABLE_CATH_EXCEPTIONS")]
public const bool DISABLE_CATH_EXCEPTIONS =
#if DISABLE_CATH_EXCEPTIONS
true;
#else
false;
#endif
[Obsolete("DRAGONECS_STABILITY_MODE")]
public const bool ENABLE_DRAGONECS_ASSERT_CHEKS =
#if ENABLE_DRAGONECS_ASSERT_CHEKS
true;
#else
false;
#endif
}
}

View File

@ -11,84 +11,63 @@ namespace DCFApixels.DragonECS.Core
void Init(ref T component, EcsWorld world);
void OnDestroy(ref T component, EcsWorld world);
}
public static class EcsWorldComponent<T>
public static class EcsWorldComponentHandler<T>
{
public static readonly IEcsWorldComponent<T> CustomHandler;
public static readonly bool IsCustom;
static EcsWorldComponent()
public static readonly IEcsWorldComponent<T> instance;
public static readonly bool isHasHandler;
static EcsWorldComponentHandler()
{
T raw = default;
if (raw is IEcsWorldComponent<T> handler)
T def = default;
if (def is IEcsWorldComponent<T> intrf)
{
IsCustom = true;
CustomHandler = handler;
isHasHandler = true;
instance = intrf;
}
else
{
IsCustom = false;
CustomHandler = new DummyHandler();
isHasHandler = false;
instance = new DummyHandler();
}
}
private sealed class DummyHandler : IEcsWorldComponent<T>
private class DummyHandler : IEcsWorldComponent<T>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Init(ref T component, EcsWorld world) { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnDestroy(ref T component, EcsWorld world) { }
}
}
#endregion
#region IEcsComponentLifecycle
#region IEcsComponentReset
public interface IEcsComponentLifecycle<T>
{
void OnAdd(ref T component, short worldID, int entityID);
void OnDel(ref T component, short worldID, int entityID);
void Enable(ref T component);
void Disable(ref T component);
}
public static class EcsComponentLifecycle<T> where T : struct
public static class EcsComponentLifecycleHandler<T>
{
public static readonly IEcsComponentLifecycle<T> CustomHandler;
public static readonly bool IsCustom;
static EcsComponentLifecycle()
public static readonly IEcsComponentLifecycle<T> instance;
public static readonly bool isHasHandler;
static EcsComponentLifecycleHandler()
{
T raw = default;
if (raw is IEcsComponentLifecycle<T> handler)
T def = default;
if (def is IEcsComponentLifecycle<T> intrf)
{
IsCustom = true;
CustomHandler = handler;
isHasHandler = true;
instance = intrf;
}
else
{
IsCustom = false;
CustomHandler = new DummyHandler();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void OnAdd(bool isCustom, IEcsComponentLifecycle<T> custom, ref T component, short worldID, int entityID)
{
if (isCustom)
{
custom.OnAdd(ref component, worldID, entityID);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void OnDel(bool isCustom, IEcsComponentLifecycle<T> custom, ref T component, short worldID, int entityID)
{
if (isCustom)
{
custom.OnDel(ref component, worldID, entityID);
}
else
{
component = default;
isHasHandler = false;
instance = new DummyHandler();
}
}
private sealed class DummyHandler : IEcsComponentLifecycle<T>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnAdd(ref T component, short worldID, int entityID) { component = default; }
public void Enable(ref T component) { component = default; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnDel(ref T component, short worldID, int entityID) { component = default; }
public void Disable(ref T component) { component = default; }
}
}
#endregion
@ -98,34 +77,22 @@ namespace DCFApixels.DragonECS.Core
{
void Copy(ref T from, ref T to);
}
public static class EcsComponentCopy<T> where T : struct
public static class EcsComponentCopyHandler<T>
{
public static readonly IEcsComponentCopy<T> CustomHandler;
public static readonly bool IsCustom;
static EcsComponentCopy()
public static readonly IEcsComponentCopy<T> instance;
public static readonly bool isHasHandler;
static EcsComponentCopyHandler()
{
T raw = default;
if (raw is IEcsComponentCopy<T> handler)
T def = default;
if (def is IEcsComponentCopy<T> intrf)
{
IsCustom = true;
CustomHandler = handler;
isHasHandler = true;
instance = intrf;
}
else
{
IsCustom = false;
CustomHandler = new DummyHandler();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Copy(bool isCustom, IEcsComponentCopy<T> custom, ref T from, ref T to)
{
if (isCustom)
{
custom.Copy(ref from, ref to);
}
else
{
to = from;
isHasHandler = false;
instance = new DummyHandler();
}
}
private sealed class DummyHandler : IEcsComponentCopy<T>
@ -135,4 +102,4 @@ namespace DCFApixels.DragonECS.Core
}
}
#endregion
}
}

View File

@ -172,30 +172,6 @@ namespace DCFApixels.DragonECS
#if DEBUG || DRAGONECS_ENABLE_DEBUG_SERVICE
OnPrint(tag, v);
DebugService.CurrentThreadInstance.Print(tag, v);
#endif
}
#if UNITY_2021_3_OR_NEWER
[UnityEngine.HideInCallstack]
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void PrintJson(object v)
{
#if DEBUG || DRAGONECS_ENABLE_DEBUG_SERVICE
string json = JsonDebugger.ToJsonLog(v);
OnPrint(string.Empty, json);
DebugService.CurrentThreadInstance.Print(json);
#endif
}
#if UNITY_2021_3_OR_NEWER
[UnityEngine.HideInCallstack]
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void PrintJson(string tag, object v)
{
#if DEBUG || DRAGONECS_ENABLE_DEBUG_SERVICE
string json = JsonDebugger.ToJsonLog(v);
OnPrint(tag, json);
DebugService.CurrentThreadInstance.Print(tag, json);
#endif
}
#endregion

View File

@ -285,7 +285,7 @@ namespace DCFApixels.DragonECS
public static TypeMeta GetTypeMeta(object obj)
{
if (obj == null) { return TypeMeta.NullTypeMeta; }
return TypeMeta.Get(Type.GetTypeHandle(obj));
return TypeMeta.Get(GetTypeMetaSource(obj).GetType());
}
public static TypeMeta GetTypeMeta<T>()
{
@ -295,9 +295,16 @@ namespace DCFApixels.DragonECS
{
return TypeMeta.Get(type);
}
public static TypeMeta GetTypeMeta(RuntimeTypeHandle typeHandle)
#endregion
#region TypeMetaProvider
public static bool IsTypeMetaProvided(object obj)
{
return TypeMeta.Get(typeHandle);
return obj is IEcsTypeMetaProvider;
}
public static object GetTypeMetaSource(object obj)
{
return obj is IEcsTypeMetaProvider intr ? intr.MetaSource : obj;
}
#endregion
}
@ -311,11 +318,7 @@ namespace DCFApixels.DragonECS
#endif
return EcsDebugUtility.GetTypeMeta(self);
}
public static TypeMeta GetMeta(this Type self)
{
return EcsDebugUtility.GetTypeMeta(self);
}
public static TypeMeta GetMeta(this RuntimeTypeHandle self)
public static TypeMeta ToMeta(this Type self)
{
return EcsDebugUtility.GetTypeMeta(self);
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 50bc53c3762bf6b4ea127004a89a894e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,7 @@
namespace DCFApixels.DragonECS
{
public interface IEcsTypeMetaProvider
{
object MetaSource { get; }
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 2ce533d3befbc0f4fb16b68a1bcce552
guid: 4a642dc8905124247bf83c9d13d8fb13
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -1,304 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace DCFApixels.DragonECS.Core.Internal
{
internal static class JsonDebugger
{
private readonly static List<string> _indentsChache = new List<string>();
internal static string ToJsonLog(object obj)
{
if (obj == null) { return "null"; }
var sb = new StringBuilder();
int linesCounter = 0;
var visited = new Dictionary<object, int>();
ToJsonLog(ref linesCounter, obj, sb, visited, 0, 2);
string json = sb.ToString();
return json;
}
private static string GetIndentString(int count)
{
int newSize = count + 1;
while (newSize > _indentsChache.Count)
{
_indentsChache.Add(new string(' ', _indentsChache.Count));
}
return _indentsChache[count];
}
private static void NewLine(
ref int linesCounter,
StringBuilder sb,
int indent,
int indentStep)
{
sb.AppendLine();
sb.Append(GetIndentString(indent * indentStep));
linesCounter++;
}
private static void ToJsonLog(
ref int linesCounter,
object value,
StringBuilder sb,
Dictionary<object, int> visited,
int indent,
int indentStep)
{
if (value == null)
{
sb.Append("null");
return;
}
Type type = value.GetType();
if (value is IEnumerable<char> rawString)
{
sb.Append('"');
string str = value as string;
if (str == null)
{
str = rawString.ToString();
}
EscapeString(str, sb);
sb.Append('"');
return;
}
if (type == typeof(float))
{
sb.Append(((float)value).ToString(System.Globalization.CultureInfo.InvariantCulture));
return;
}
if(type == typeof(double))
{
sb.Append(((double)value).ToString(System.Globalization.CultureInfo.InvariantCulture));
return;
}
if (type == typeof(decimal))
{
sb.Append(((decimal)value).ToString(System.Globalization.CultureInfo.InvariantCulture));
return;
}
if (type == typeof(bool))
{
sb.Append((bool)value ? "true" : "false");
return;
}
if (type.IsEnum)
{
if(type.TryGetAttribute(out FlagsAttribute _))
{
sb.Append(type.FullName);
sb.Append('.');
sb.Append(value.ToString());
}
else
{
sb.Append(value.ToString());
}
return;
}
if (type == typeof(DateTime))
{
sb.Append('"');
sb.Append(((DateTime)value).ToString("yyyy-MM-ddTHH:mm:ss.fffZ", System.Globalization.CultureInfo.InvariantCulture));
sb.Append('"');
return;
}
if (type == typeof(Guid))
{
sb.Append('"');
sb.Append(value.ToString());
sb.Append('"');
return;
}
if (type.IsPrimitive)
{
sb.Append(value);
return;
}
if (value is Exception e)
{
sb.Append('"');
sb.Append(type.Name);
sb.Append(':').Append(' ');
sb.Append(e.Message);
sb.Append('"');
return;
}
if (value is Type t)
{
sb.Append('"');
sb.Append(t.GetMeta().TypeName);
sb.Append('"');
return;
}
if (type.Namespace == typeof(FieldInfo).Namespace || type.IsPointer)
{
sb.Append('"');
sb.Append(value.ToString());
sb.Append('"');
return;
}
if (value is Delegate del)
{
var list = del.GetInvocationList();
if (list.Length == 0)
{
sb.Append("null");
return;
}
if (list.Length == 1)
{
sb.Append('"');
sb.Append(del.Target.GetType().FullName);
sb.Append('.');
sb.Append(del.Method.Name);
sb.Append('"');
return;
}
ToJsonLog(ref linesCounter, list, sb, visited, indent, indentStep);
return;
}
if (type.IsValueType == false)
{
if (visited.TryGetValue(value, out var line))
{
sb.Append('#');
sb.Append(type.Name);
sb.Append('#');
sb.Append(line);
sb.Append('#');
return;
}
visited.Add(value, linesCounter);
}
// Collections
IEnumerable enumerable = value as IEnumerable;
if(enumerable != null)
{
try
{
enumerable.GetEnumerator();
}
catch (Exception)
{
enumerable = null;
}
}
if (enumerable != null)
{
sb.Append('[');
bool first = true;
foreach (object item in enumerable)
{
if (!first) { sb.Append(','); } else { first = false; }
NewLine(ref linesCounter, sb, indent + 1, indentStep);
ToJsonLog(ref linesCounter, item, sb, visited, indent + 1, indentStep);
}
// перенос строки если были элементы
if (!first)
{
NewLine(ref linesCounter, sb, indent, indentStep);
}
sb.Append(']');
}
else // Object
{
sb.Append('{');
{
NewLine(ref linesCounter, sb, indent + 1, indentStep);
sb.Append("\"Type\": ");
ToJsonLog(ref linesCounter, type, sb, visited, indent + 1, indentStep);
}
// Fields
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var field in fields)
{
if (field.IsStatic) { continue; }
NewLine(ref linesCounter, sb, indent + 1, indentStep);
sb.Append('"');
sb.Append(field.Name);
sb.Append('"');
sb.Append(':').Append(' ');
object fieldValue = field.GetValue(value);
ToJsonLog(ref linesCounter, fieldValue, sb, visited, indent + 1, 2);
}
// Properties
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var prop in properties)
{
if (prop.GetIndexParameters().Length > 0 ||
prop.GetMethod == null ||
prop.GetMethod.IsStatic)
{
continue;
}
NewLine(ref linesCounter, sb, indent + 1, indentStep);
sb.Append('"');
sb.Append(prop.Name);
sb.Append('"');
sb.Append(':').Append(' ');
object propValue;
try
{
propValue = prop.GetValue(value);
}
catch (Exception cathcedE)
{
propValue = cathcedE;
}
ToJsonLog(ref linesCounter, propValue, sb, visited, indent + 1, indentStep);
}
NewLine(ref linesCounter, sb, indent, indentStep);
sb.Append('}');
}
//visited.Remove(value);
}
private static void EscapeString(string s, StringBuilder sb)
{
foreach (char c in s)
{
switch (c)
{
case '"': sb.Append("\\\""); break;
case '\\': sb.Append("\\\\"); break;
case '\b': sb.Append("\\b"); break;
case '\f': sb.Append("\\f"); break;
case '\n': sb.Append("\\n"); break;
case '\r': sb.Append("\\r"); break;
case '\t': sb.Append("\\t"); break;
default:
if (char.IsControl(c))
{
sb.AppendFormat("\\u{0:x4}", (int)c);
}
else
{
sb.Append(c);
}
break;
}
}
}
}
}

View File

@ -5,7 +5,7 @@ using System;
namespace DCFApixels.DragonECS.Core
{
public abstract class DragonMetaAttribute : Attribute { }
public abstract class EcsMetaAttribute : Attribute { }
internal static class EcsMetaAttributeHalper
{

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0f3febd9a6cdfea4fb27039c5f66436f
guid: eb8cc656a6e80f843b8794af9f63faa8
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -23,7 +23,7 @@ namespace DCFApixels.DragonECS
#endregion
}
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
public sealed class MetaColorAttribute : DragonMetaAttribute, IMetaColor
public sealed class MetaColorAttribute : EcsMetaAttribute, IMetaColor
{
public readonly MetaColor color;

View File

@ -7,7 +7,7 @@ using System;
namespace DCFApixels.DragonECS
{
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
public sealed class MetaDescriptionAttribute : DragonMetaAttribute
public sealed class MetaDescriptionAttribute : EcsMetaAttribute
{
public readonly MetaDescription Data;
public MetaDescriptionAttribute(string text)

View File

@ -10,7 +10,7 @@ using System.Text.RegularExpressions;
namespace DCFApixels.DragonECS
{
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
public sealed class MetaGroupAttribute : DragonMetaAttribute
public sealed class MetaGroupAttribute : EcsMetaAttribute
{
public const char SEPARATOR = MetaGroup.SEPARATOR;
public readonly string Name = string.Empty;
@ -73,10 +73,6 @@ namespace DCFApixels.DragonECS
}
return new MetaGroup(name);
}
public static MetaGroup FromName(params string[] path)
{
return FromName(string.Join(SEPARATOR, path));
}
public static MetaGroup FromNameSpace(Type type)
{
if (string.IsNullOrWhiteSpace(type.Namespace))

View File

@ -14,7 +14,7 @@ using System.Text.RegularExpressions;
namespace DCFApixels.DragonECS
{
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
public sealed class MetaIDAttribute : DragonMetaAttribute
public sealed class MetaIDAttribute : EcsMetaAttribute
{
public readonly string ID;
public MetaIDAttribute(string id)

View File

@ -7,7 +7,7 @@ using System;
namespace DCFApixels.DragonECS
{
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
public sealed class MetaNameAttribute : DragonMetaAttribute
public sealed class MetaNameAttribute : EcsMetaAttribute
{
public readonly string name;
public readonly bool isHideGeneric;

View File

@ -1,31 +0,0 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.Core;
using System;
using System.Collections.Generic;
namespace DCFApixels.DragonECS
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
public sealed class MetaProxyAttribute : DragonMetaAttribute
{
public Type Type;
public bool ForDeclaringType;
public MetaProxyAttribute(Type type, bool forDeclaringType = false)
{
Type = type;
ForDeclaringType = forDeclaringType;
}
}
public class MetaProxyBase
{
public static readonly MetaProxyBase EmptyProxy = new MetaProxyBase(typeof(void));
public virtual string Name { get { return null; } }
public virtual MetaColor? Color { get { return null; } }
public virtual MetaDescription Description { get { return null; } }
public virtual MetaGroup Group { get { return null; } }
public virtual IEnumerable<string> Tags { get { return null; } }
public MetaProxyBase(Type type) { }
}
}

View File

@ -8,7 +8,7 @@ using System.Collections.Generic;
namespace DCFApixels.DragonECS
{
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
public sealed class MetaTagsAttribute : DragonMetaAttribute
public sealed class MetaTagsAttribute : EcsMetaAttribute
{
public const char SEPARATOR = ',';
private readonly string[] _tags = Array.Empty<string>();

View File

@ -1,17 +1,15 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
#if DEBUG || !REFLECTION_DISABLED
#define REFLECTION_ENABLED
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.PoolsCore;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
#if REFLECTION_ENABLED
#if DEBUG || !REFLECTION_DISABLED
using System.Reflection;
#endif
@ -48,13 +46,11 @@ namespace DCFApixels.DragonECS
public static readonly TypeMeta NullTypeMeta;
private static readonly object _lock = new object();
private static readonly Dictionary<RuntimeTypeHandle, TypeMeta> _metaCache = new Dictionary<RuntimeTypeHandle, TypeMeta>();
private static readonly Dictionary<Type, TypeMeta> _metaCache = new Dictionary<Type, TypeMeta>();
private static int _increment = 1;
private readonly int _uniqueID;
internal readonly Type _type;
private readonly MetaProxyBase _proxy;
private readonly bool _isSelfProxy;
private bool _isCustomName;
private bool _isCustomColor;
@ -97,70 +93,26 @@ namespace DCFApixels.DragonECS
_initFlags = InitFlag.All,
};
_metaCache.Add(typeof(void).TypeHandle, NullTypeMeta);
_metaCache.Add(typeof(void), NullTypeMeta);
}
public static TypeMeta Get(Type type) { return Get(type.TypeHandle); }
public static TypeMeta Get(RuntimeTypeHandle typeHandle)
public static TypeMeta Get(Type type)
{
lock (_lock)
lock (_lock) //TODO посмотреть можно ли тут убрать лок
{
if (_metaCache.TryGetValue(typeHandle, out TypeMeta result) == false)
if (_metaCache.TryGetValue(type, out TypeMeta result) == false)
{
result = new TypeMeta(Type.GetTypeFromHandle(typeHandle));
_metaCache.Add(typeHandle, result);
result = new TypeMeta(type);
_metaCache.Add(type, result);
}
return result;
}
}
private static Type FindDeclaringType(Type targetPureType, Type currentType)
{
if (currentType == typeof(object)) { return null; }
var pure = currentType.GetPureType();
if (pure == targetPureType)
{
return currentType;
}
return FindDeclaringType(targetPureType, currentType.BaseType);
}
private TypeMeta(Type type)
{
_uniqueID = _increment++;
_type = type;
_proxy = MetaProxyBase.EmptyProxy;
if (type.TryGetAttributeInherited<MetaProxyAttribute>(out var proxyAtr, out var declaringAtrType))
{
#if REFLECTION_ENABLED
#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
#pragma warning disable IL2055 // Either the type on which the MakeGenericType is called can't be statically determined, or the type parameters to be used for generic arguments can't be statically determined.
#pragma warning disable IL2077 // Target parameter argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The source field does not have matching annotations.
Type proxyType = proxyAtr.Type;
if (proxyType.ContainsGenericParameters && proxyType.IsNested)
{
if (declaringAtrType != null && declaringAtrType.ContainsGenericParameters == false)
{
var args = declaringAtrType.GetGenericArguments();
proxyType = proxyType.MakeGenericType(args);
}
}
if (proxyType.ContainsGenericParameters == false)
{
var proxy = Activator.CreateInstance(proxyType, proxyAtr.ForDeclaringType ? declaringAtrType : type) as MetaProxyBase;
if (proxy != null)
{
_proxy = proxy;
_isSelfProxy = declaringAtrType == type;
}
}
#pragma warning restore IL2055
#pragma warning restore IL3050
#pragma warning restore IL2077
#endif
}
}
#endregion
#endregion
#region Type
public Type Type
@ -174,7 +126,7 @@ namespace DCFApixels.DragonECS
{
if (_initFlags.HasFlag(InitFlag.Name) == false)
{
(_name, _isCustomName) = MetaGenerator.GetMetaName(this);
(_name, _isCustomName) = MetaGenerator.GetMetaName(_type);
_typeName = _isCustomName ? MetaGenerator.GetTypeName(_type) : _name;
_initFlags |= InitFlag.Name;
}
@ -239,7 +191,7 @@ namespace DCFApixels.DragonECS
{
if (_initFlags.HasFlag(InitFlag.Description) == false)
{
_description = MetaGenerator.GetDescription(this);
_description = MetaGenerator.GetDescription(_type);
_initFlags |= InitFlag.Description;
}
return _description;
@ -254,7 +206,7 @@ namespace DCFApixels.DragonECS
{
if (_initFlags.HasFlag(InitFlag.Group) == false)
{
_group = MetaGenerator.GetGroup(this);
_group = MetaGenerator.GetGroup(_type);
_initFlags |= InitFlag.Group;
}
return _group;
@ -267,7 +219,7 @@ namespace DCFApixels.DragonECS
{
if (_initFlags.HasFlag(InitFlag.Tags) == false)
{
_tags = MetaGenerator.GetTags(this);
_tags = MetaGenerator.GetTags(_type);
_initFlags |= InitFlag.Tags;
_isHidden = _tags.Contains(MetaTags.HIDDEN);
_isObsolete = _tags.Contains(MetaTags.OBSOLETE);
@ -423,7 +375,7 @@ namespace DCFApixels.DragonECS
lock (_lock)
{
_metaCache.Clear();
_metaCache.Add(typeof(void).TypeHandle, NullTypeMeta);
_metaCache.Add(typeof(void), NullTypeMeta);
}
}
ITypeMeta ITypeMeta.BaseMeta
@ -432,7 +384,7 @@ namespace DCFApixels.DragonECS
}
private static bool CheckEcsMemener(Type checkedType)
{
#if REFLECTION_ENABLED
#if DEBUG || !REFLECTION_DISABLED
return checkedType.IsInterface == false && checkedType.IsAbstract == false && typeof(IEcsMember).IsAssignableFrom(checkedType);
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(TypeMeta)}.{nameof(CheckEcsMemener)} method does not work.");
@ -443,7 +395,7 @@ namespace DCFApixels.DragonECS
{
if (IsHasCustomMeta(type))
{
meta = type.GetMeta();
meta = type.ToMeta();
return true;
}
meta = null;
@ -451,16 +403,16 @@ namespace DCFApixels.DragonECS
}
public static bool IsHasCustomMeta(Type type)
{
#if REFLECTION_ENABLED
return CheckEcsMemener(type) || Attribute.GetCustomAttributes(type, typeof(DragonMetaAttribute), false).Length > 0;
#if DEBUG || !REFLECTION_DISABLED
return CheckEcsMemener(type) || Attribute.GetCustomAttributes(type, typeof(EcsMetaAttribute), false).Length > 0;
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(TypeMeta)}.{nameof(IsHasCustomMeta)} method does not work.");
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(TypeMeta)}.{nameof(IsHasMeta)} method does not work.");
return false;
#endif
}
public static bool IsHasMetaID(Type type)
{
#if REFLECTION_ENABLED
#if DEBUG || !REFLECTION_DISABLED
return TryGetCustomMeta(type, out TypeMeta meta) && meta.IsHasMetaID();
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(TypeMeta)}.{nameof(IsHasMetaID)} method does not work.");
@ -517,6 +469,15 @@ namespace DCFApixels.DragonECS
}
#endregion
#region Obsolete
[Obsolete("Use TryGetCustomMeta(type)")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static bool IsHasMeta(Type type)
{
return IsHasCustomMeta(type);
}
#endregion
#region MetaGenerator
private static class MetaGenerator
{
@ -527,20 +488,15 @@ namespace DCFApixels.DragonECS
{
return EcsDebugUtility.GetGenericTypeName(type, GENERIC_NAME_DEPTH);
}
public static (string, bool) GetMetaName(TypeMeta meta)
public static (string, bool) GetMetaName(Type type)
{
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
if (meta._isSelfProxy && meta._proxy.Name != null)
{
return (meta._proxy.Name, true);
}
var type = meta.Type;
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
bool isCustom = type.TryGetAttribute(out MetaNameAttribute atr) && string.IsNullOrEmpty(atr.name) == false;
if (isCustom)
{
if ((type.IsGenericType && atr.isHideGeneric == false) == false)
{
return (atr.name, true);
return (atr.name, isCustom);
}
string genericParams = "";
Type[] typeParameters = type.GetGenericArguments();
@ -549,16 +505,12 @@ namespace DCFApixels.DragonECS
string paramTypeName = EcsDebugUtility.GetGenericTypeName(typeParameters[i], GENERIC_NAME_DEPTH);
genericParams += (i == 0 ? paramTypeName : $", {paramTypeName}");
}
return ($"{atr.name}<{genericParams}>", true);
return ($"{atr.name}<{genericParams}>", isCustom);
}
if (meta._proxy.Name != null)
{
return (meta._proxy.Name, true);
}
return (EcsDebugUtility.GetGenericTypeName(type, GENERIC_NAME_DEPTH), false);
return (EcsDebugUtility.GetGenericTypeName(type, GENERIC_NAME_DEPTH), isCustom);
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetMetaName)} method does not work.");
return (meta.Type.Name, false);
return (type.Name, false);
#endif
}
#endregion
@ -579,21 +531,9 @@ namespace DCFApixels.DragonECS
}
public static (MetaColor, bool) GetColor(TypeMeta meta)
{
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
if (meta._isSelfProxy && meta._proxy.Color != null)
{
return (meta._proxy.Color.Value, true);
}
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
bool isCustom = meta.Type.TryGetAttribute(out MetaColorAttribute atr);
if (isCustom)
{
return (atr.color, true);
}
if (meta._proxy.Color != null)
{
return (meta._proxy.Color.Value, true);
}
return (AutoColor(meta), false);
return (isCustom ? atr.color : AutoColor(meta), isCustom);
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetColor)} method does not work.");
return (MetaColor.White, false);
@ -602,22 +542,17 @@ namespace DCFApixels.DragonECS
#endregion
#region GetGroup
public static MetaGroup GetGroup(TypeMeta meta)
public static MetaGroup GetGroup(Type type)
{
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
if (meta._isSelfProxy && meta._proxy.Group != null)
{
return meta._proxy.Group;
}
if (meta.Type.TryGetAttribute(out MetaGroupAttribute atr))
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
if (type.TryGetAttribute(out MetaGroupAttribute atr))
{
return MetaGroup.FromName(atr.Name);
}
if (meta._proxy.Group != null)
else
{
return meta._proxy.Group;
return MetaGroup.FromNameSpace(type);
}
return MetaGroup.FromNameSpace(meta.Type);
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetGroup)} method does not work.");
return MetaGroup.Empty;
@ -626,22 +561,11 @@ namespace DCFApixels.DragonECS
#endregion
#region GetDescription
public static MetaDescription GetDescription(TypeMeta meta)
public static MetaDescription GetDescription(Type type)
{
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
if (meta._isSelfProxy && meta._proxy.Description != null)
{
return meta._proxy.Description;
}
if (meta.Type.TryGetAttribute(out MetaDescriptionAttribute atr))
{
return atr.Data;
}
if (meta._proxy.Description != null)
{
return meta._proxy.Description;
}
return MetaDescription.Empty;
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
bool isCustom = type.TryGetAttribute(out MetaDescriptionAttribute atr);
return isCustom ? atr.Data : MetaDescription.Empty;
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetDescription)} method does not work.");
return MetaDescription.Empty;
@ -650,22 +574,11 @@ namespace DCFApixels.DragonECS
#endregion
#region GetTags
public static IReadOnlyList<string> GetTags(TypeMeta meta)
public static IReadOnlyList<string> GetTags(Type type)
{
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
if (meta._isSelfProxy && meta._proxy.Tags != null)
{
return meta._proxy.Tags.ToArray();
}
if (meta.Type.TryGetAttribute(out MetaTagsAttribute atr))
{
return atr.Tags;
}
if (meta._proxy.Tags != null)
{
return meta._proxy.Tags.ToArray();
}
return Array.Empty<string>();
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
var atr = type.GetCustomAttribute<MetaTagsAttribute>();
return atr != null ? atr.Tags : Array.Empty<string>();
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetTags)} method does not work.");
return Array.Empty<string>();
@ -676,7 +589,7 @@ namespace DCFApixels.DragonECS
#region GetMetaID
public static string GetMetaID(Type type)
{
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
var atr = type.GetCustomAttribute<MetaIDAttribute>();
if (atr == null)

View File

@ -3,6 +3,7 @@
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.PoolsCore;
using System;
using System.Collections.Generic;
@ -417,6 +418,52 @@ namespace DCFApixels.DragonECS
EcsMask IComponentMask.ToMask(EcsWorld world) { return _mask; }
#endregion
#region Obsolete
[Obsolete("Use EcsMask.GetIterator()")]
public Iterator GetIterator()
{
return new Iterator(Mask.GetIterator(), _source.Entities);
}
[Obsolete("Use EcsMask.GetIterator().Iterate(span)")]
public Iterator GetIteratorFor(EcsSpan span)
{
return new Iterator(Mask.GetIterator(), span);
}
[Obsolete("Use EcsMaskIterator")]
public ref struct Iterator
{
public readonly short worldID;
public readonly EcsMaskIterator.Enumerable iterator;
public Iterator(EcsMaskIterator iterator, EcsSpan span)
{
worldID = iterator.World.ID;
this.iterator = iterator.Iterate(span);
}
#region CopyTo
public void CopyTo(EcsGroup group)
{
iterator.CopyTo(group);
}
public int CopyTo(ref int[] array)
{
return iterator.CopyTo(ref array);
}
public EcsSpan CopyToSpan(ref int[] array)
{
int count = CopyTo(ref array);
return new EcsSpan(worldID, array, count);
}
#endregion
public EcsMaskIterator.Enumerable.Enumerator GetEnumerator()
{
return iterator.GetEnumerator();
}
}
#endregion
#region Events
public delegate void OnInitApectHandler(object aspect, Builder builder);
public static event OnInitApectHandler OnInit = delegate { };

View File

@ -5,6 +5,7 @@ using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
@ -33,7 +34,6 @@ namespace DCFApixels.DragonECS
{
public readonly int ID;
public readonly short WorldID;
public readonly EcsWorld World;
internal readonly EcsStaticMask _staticMask;
internal readonly EcsMaskChunck[] _incChunckMasks;
@ -51,6 +51,11 @@ namespace DCFApixels.DragonECS
private EcsMaskIterator _iterator;
#region Properties
public EcsWorld World
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return EcsWorld.GetWorld(WorldID); }
}
/// <summary> Sorted set excluding constraints. </summary>
public ReadOnlySpan<int> Incs
{
@ -69,25 +74,6 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _anys; }
}
/// <summary> Sorted set including constraints presented as global type codes. </summary>
public ReadOnlySpan<EcsTypeCode> IncTypeCodes
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return ToStatic().IncTypeCodes; }
}
/// <summary> Sorted set excluding constraints presented as global type codes. </summary>
public ReadOnlySpan<EcsTypeCode> ExcTypeCodes
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return ToStatic().ExcTypeCodes; }
}
/// <summary> Sorted set any constraints presented as global type codes. </summary>
public ReadOnlySpan<EcsTypeCode> AnyTypeCodes
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return ToStatic().AnyTypeCodes; }
}
public EcsMaskFlags Flags
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -130,7 +116,6 @@ namespace DCFApixels.DragonECS
_staticMask = staticMask;
ID = id;
WorldID = worldID;
World = EcsWorld.GetWorld(worldID);
_flags = staticMask.Flags;
EcsWorld world = EcsWorld.GetWorld(worldID);
@ -360,6 +345,8 @@ namespace DCFApixels.DragonECS
internal EcsMask ConvertFromStatic(EcsStaticMask staticMask)
{
if (_staticMasks.TryGetValue(staticMask.ID, out EcsMask result) == false)
{
result = new EcsMask(staticMask, _staticMasks.Count, _world.ID);
@ -380,82 +367,24 @@ namespace DCFApixels.DragonECS
_world = world;
_builder = EcsStaticMask.New();
}
public Builder Inc() { return this; }
public Builder Exc() { return this; }
public Builder Any() { return this; }
public Builder Inc<T>() { _builder.Inc<T>(); return this; }
public Builder Exc<T>() { _builder.Exc<T>(); return this; }
public Builder Any<T>() { _builder.Any<T>(); return this; }
public Builder Inc(Type type) { _builder.Inc(type); return this; }
public Builder Exc(Type type) { _builder.Exc(type); return this; }
public Builder Any(Type type) { _builder.Any(type); return this; }
public Builder Inc(params Type[] types) { _builder.Inc(types); return this; }
public Builder Exc(params Type[] types) { _builder.Exc(types); return this; }
public Builder Any(params Type[] types) { _builder.Any(types); return this; }
public Builder Inc(ReadOnlySpan<Type> types) { _builder.Inc(types); return this; }
public Builder Exc(ReadOnlySpan<Type> types) { _builder.Exc(types); return this; }
public Builder Any(ReadOnlySpan<Type> types) { _builder.Any(types); return this; }
public Builder Inc(IEnumerable<Type> types) { _builder.Inc(types); return this; }
public Builder Exc(IEnumerable<Type> types) { _builder.Exc(types); return this; }
public Builder Any(IEnumerable<Type> types) { _builder.Any(types); return this; }
public Builder Inc(EcsTypeCode typeCode) { _builder.Inc(typeCode); return this; }
public Builder Exc(EcsTypeCode typeCode) { _builder.Exc(typeCode); return this; }
public Builder Any(EcsTypeCode typeCode) { _builder.Any(typeCode); return this; }
public Builder Inc(params EcsTypeCode[] typeCodes) { _builder.Inc(typeCodes); return this; }
public Builder Exc(params EcsTypeCode[] typeCodes) { _builder.Exc(typeCodes); return this; }
public Builder Any(params EcsTypeCode[] typeCodes) { _builder.Any(typeCodes); return this; }
public Builder Inc(ReadOnlySpan<EcsTypeCode> typeCodes) { _builder.Inc(typeCodes); return this; }
public Builder Exc(ReadOnlySpan<EcsTypeCode> typeCodes) { _builder.Exc(typeCodes); return this; }
public Builder Any(ReadOnlySpan<EcsTypeCode> typeCodes) { _builder.Any(typeCodes); return this; }
public Builder Inc(IEnumerable<EcsTypeCode> typeCodes) { _builder.Inc(typeCodes); return this; }
public Builder Exc(IEnumerable<EcsTypeCode> typeCodes) { _builder.Exc(typeCodes); return this; }
public Builder Any(IEnumerable<EcsTypeCode> typeCodes) { _builder.Any(typeCodes); return this; }
public Builder Combine(EcsMask mask) { _builder.Combine(mask._staticMask); return this; }
public Builder Except(EcsMask mask) { _builder.Except(mask._staticMask); return this; }
public EcsMask Build() { return _world.Get<WorldMaskComponent>().ConvertFromStatic(_builder.Build()); }
public static implicit operator EcsMask(Builder a) { return a.Build(); }
}
#endregion
#region Debug utils
private Type[] _incTypes_Debug;
private Type[] _excTypes_Debug;
private Type[] _anyTypes_Debug;
public ReadOnlySpan<Type> GetIncTypes_Debug()
{
if(_incTypes_Debug == null)
{
_incTypes_Debug = GetTypes(IncTypeCodes);
}
return _incTypes_Debug;
}
public ReadOnlySpan<Type> GetExcTypes_Debug()
{
if(_excTypes_Debug == null)
{
_excTypes_Debug = GetTypes(ExcTypeCodes);
}
return _excTypes_Debug;
}
public ReadOnlySpan<Type> GetAnyTypes_Debug()
{
if (_anyTypes_Debug == null)
{
_anyTypes_Debug = GetTypes(AnyTypeCodes);
}
return _anyTypes_Debug;
}
private Type[] GetTypes(ReadOnlySpan<EcsTypeCode> typeCodes)
{
Type[] result = new Type[typeCodes.Length];
for (int i = 0; i < typeCodes.Length; i++)
{
result[i] = EcsTypeCodeManager.FindTypeOfCode(typeCodes[i]).Type;
}
return result;
}
private static string CreateLogString(short worldID, int[] incs, int[] excs, int[] anys)
{
#if DEBUG
@ -473,18 +402,15 @@ namespace DCFApixels.DragonECS
public readonly int ID;
public readonly EcsWorld world;
private readonly short _worldID;
public readonly EcsMaskChunck[] incsChunkMasks;
public readonly EcsMaskChunck[] excsChunkMasks;
public readonly EcsMaskChunck[] anysChunkMasks;
public readonly int[] incs;
public readonly int[] excs;
public readonly int[] anys;
public readonly Type[] incsTypes;
public readonly Type[] excsTypes;
public readonly Type[] anysTypes;
public readonly IEcsPool[] incsPools;
public readonly IEcsPool[] excsPools;
public readonly IEcsPool[] anysPools;
public readonly EcsMaskChunck[] includedChunkMasks;
public readonly EcsMaskChunck[] excludedChunkMasks;
public readonly EcsMaskChunck[] anyChunkMasks;
public readonly int[] included;
public readonly int[] excluded;
public readonly int[] any;
public readonly Type[] includedTypes;
public readonly Type[] excludedTypes;
public readonly Type[] anyTypes;
public bool IsEmpty { get { return _source.IsEmpty; } }
public bool IsBroken { get { return _source.IsBroken; } }
@ -496,26 +422,51 @@ namespace DCFApixels.DragonECS
ID = mask.ID;
world = EcsWorld.GetWorld(mask.WorldID);
_worldID = mask.WorldID;
incsChunkMasks = mask._incChunckMasks;
excsChunkMasks = mask._excChunckMasks;
anysChunkMasks = mask._anyChunckMasks;
incs = mask._incs;
excs = mask._excs;
anys = mask._anys;
IEcsPool converterPool(int o) { return world.FindPoolInstance(o); }
incsTypes = mask.GetIncTypes_Debug().ToArray();
excsTypes = mask.GetExcTypes_Debug().ToArray();
anysTypes = mask.GetAnyTypes_Debug().ToArray();
incsPools = incs.Select(converterPool).ToArray();
excsPools = excs.Select(converterPool).ToArray();
anysPools = anys.Select(converterPool).ToArray();
includedChunkMasks = mask._incChunckMasks;
excludedChunkMasks = mask._excChunckMasks;
anyChunkMasks = mask._anyChunckMasks;
included = mask._incs;
excluded = mask._excs;
any = mask._anys;
Type converter(int o) { return world.GetComponentType(o); }
includedTypes = included.Select(converter).ToArray();
excludedTypes = excluded.Select(converter).ToArray();
anyTypes = any.Select(converter).ToArray();
}
public override string ToString()
{
return CreateLogString(_worldID, incs, excs, anys);
return CreateLogString(_worldID, included, excluded, any);
}
}
#endregion
#region Obsolete
/// <summary> Sorted set including constraints. </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use Incs")]
public ReadOnlySpan<int> Inc
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _incs; }
}
/// <summary> Sorted set excluding constraints. </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use Excs")]
public ReadOnlySpan<int> Exc
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _excs; }
}
public partial struct Builder
{
[EditorBrowsable(EditorBrowsableState.Never)][Obsolete] public Builder Include<T>() { return Inc<T>(); }
[EditorBrowsable(EditorBrowsableState.Never)][Obsolete] public Builder Exclude<T>() { return Exc<T>(); }
[EditorBrowsable(EditorBrowsableState.Never)][Obsolete] public Builder Include(Type type) { return Inc(type); }
[EditorBrowsable(EditorBrowsableState.Never)][Obsolete] public Builder Exclude(Type type) { return Exc(type); }
[EditorBrowsable(EditorBrowsableState.Never)][Obsolete] public Builder Inc(int componentTypeID) { Inc(_world.GetComponentType(componentTypeID)); return this; }
[EditorBrowsable(EditorBrowsableState.Never)][Obsolete] public Builder Exc(int componentTypeID) { Exc(_world.GetComponentType(componentTypeID)); return this; }
}
#endregion
}
[Flags]
@ -656,8 +607,8 @@ namespace DCFApixels.DragonECS
}
private void Cleanup(bool disposing)
{
_bufferHandler.DisposeAndReset();
_chunckBufferHandler.DisposeAndReset();
_bufferHandler.Dispose();
_chunckBufferHandler.Dispose();
}
#endregion
@ -685,7 +636,8 @@ namespace DCFApixels.DragonECS
if (_sortIncChunckBuffer.Length > 1)
{
SortHalper.Sort(sortIncBuffer.AsSpan(), new IncCountComparer(counts));
var comparer = new IncCountComparer(counts);
UnsafeArraySortHalperX<int>.InsertionSort(sortIncBuffer.ptr, sortIncBuffer.Length, ref comparer);
ConvertToChuncks(preSortingBuffer, sortIncBuffer, _sortIncChunckBuffer);
}
if (_sortIncChunckBuffer.Length > 0)
@ -699,7 +651,8 @@ namespace DCFApixels.DragonECS
if (_sortExcChunckBuffer.Length > 1)
{
SortHalper.Sort(sortExcBuffer.AsSpan(), new ExcCountComparer(counts));
ExcCountComparer comparer = new ExcCountComparer(counts);
UnsafeArraySortHalperX<int>.InsertionSort(sortExcBuffer.ptr, sortExcBuffer.Length, ref comparer);
ConvertToChuncks(preSortingBuffer, sortExcBuffer, _sortExcChunckBuffer);
}
// Выражение IncCount < (AllEntitesCount - ExcCount) мало вероятно будет истинным.
@ -709,7 +662,8 @@ namespace DCFApixels.DragonECS
if (_sortAnyChunckBuffer.Length > 1)
{
SortHalper.Sort(sortAnyBuffer.AsSpan(), new ExcCountComparer(counts));
ExcCountComparer comparer = new ExcCountComparer(counts);
UnsafeArraySortHalperX<int>.InsertionSort(sortAnyBuffer.ptr, sortAnyBuffer.Length, ref comparer);
ConvertToChuncks(preSortingBuffer, sortAnyBuffer, _sortAnyChunckBuffer);
}
// Any не влияет на maxEntites если есть Inc и сложно высчитывается если нет Inc
@ -737,13 +691,9 @@ namespace DCFApixels.DragonECS
#endregion
#region IterateTo
public EcsMaskFlags MaskFlags
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _maskFlags; }
}
//TODO Перемеиноваться в CacheTo
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CacheTo(EcsSpan source, EcsGroup group)
public void IterateTo(EcsSpan source, EcsGroup group)
{
switch (_maskFlags)
{
@ -751,7 +701,7 @@ namespace DCFApixels.DragonECS
group.CopyFrom(source);
break;
case EcsMaskFlags.Inc:
IterateOnlyInc(source).CacheTo(group);
IterateOnlyInc(source).CopyTo(group);
break;
case EcsMaskFlags.Exc:
case EcsMaskFlags.Any:
@ -759,7 +709,7 @@ namespace DCFApixels.DragonECS
case EcsMaskFlags.IncAny:
case EcsMaskFlags.ExcAny:
case EcsMaskFlags.IncExcAny:
Iterate(source).CacheTo(group);
Iterate(source).CopyTo(group);
break;
case EcsMaskFlags.Broken:
group.Clear();
@ -770,21 +720,21 @@ namespace DCFApixels.DragonECS
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CacheTo(EcsSpan source, ref int[] array)
public int IterateTo(EcsSpan source, ref int[] array)
{
switch (_maskFlags)
{
case EcsMaskFlags.Empty:
return source.ToArray(ref array);
case EcsMaskFlags.Inc:
return IterateOnlyInc(source).CacheTo(ref array);
return IterateOnlyInc(source).CopyTo(ref array);
case EcsMaskFlags.Exc:
case EcsMaskFlags.Any:
case EcsMaskFlags.IncExc:
case EcsMaskFlags.IncAny:
case EcsMaskFlags.ExcAny:
case EcsMaskFlags.IncExcAny:
return Iterate(source).CacheTo(ref array);
return Iterate(source).CopyTo(ref array);
case EcsMaskFlags.Broken:
return new EcsSpan(World.ID, Array.Empty<int>()).ToArray(ref array);
default:
@ -814,7 +764,7 @@ namespace DCFApixels.DragonECS
#region CopyTo
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CacheTo(EcsGroup group)
public void CopyTo(EcsGroup group)
{
group.Clear();
var enumerator = GetEnumerator();
@ -824,7 +774,7 @@ namespace DCFApixels.DragonECS
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CacheTo(ref int[] array)
public int CopyTo(ref int[] array)
{
int count = 0;
var enumerator = GetEnumerator();
@ -970,7 +920,7 @@ namespace DCFApixels.DragonECS
#region CopyTo
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CacheTo(EcsGroup group)
public void CopyTo(EcsGroup group)
{
group.Clear();
var enumerator = GetEnumerator();
@ -980,7 +930,7 @@ namespace DCFApixels.DragonECS
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CacheTo(ref int[] array)
public int CopyTo(ref int[] array)
{
int count = 0;
var enumerator = GetEnumerator();
@ -1127,7 +1077,7 @@ namespace DCFApixels.DragonECS.Core.Internal
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
internal readonly struct IncCountComparer : IComparer<int>
internal readonly struct IncCountComparer : IStructComparer<int>
{
public readonly EcsWorld.PoolSlot[] counts;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -1146,7 +1096,7 @@ namespace DCFApixels.DragonECS.Core.Internal
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
internal readonly struct ExcCountComparer : IComparer<int>
internal readonly struct ExcCountComparer : IStructComparer<int>
{
public readonly EcsWorld.PoolSlot[] counts;
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@ -3,6 +3,7 @@
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.RunnersCore;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
@ -121,6 +122,7 @@ namespace DCFApixels.DragonECS
}
private void InsertAfterNode_Internal(int insertAfterIndex, IEcsProcess system, string layer, int sortOrder, bool isUnique)
{
//TODO нужно потестить
if (isUnique && _uniqueSystemsSet.Add(system.GetType()) == false)
{
//EcsDebug.PrintWarning($"The pipeline already contains a unique instance of {system.GetType().Name}");
@ -598,6 +600,20 @@ namespace DCFApixels.DragonECS
}
}
#endregion
#region Obsolete
[Obsolete("Use " + nameof(Injections))]
public readonly InitInjectionList Injector;
[Obsolete("Use LayersMap")]
public class LayerList : LayersMap
{
//public LayerList(Builder source, string basicLayerName) : base(source, basicLayerName) { }
//public LayerList(Builder source, string preBeginlayer, string beginlayer, string basicLayer, string endLayer, string postEndLayer) : base(source, preBeginlayer, beginlayer, basicLayer, endLayer, postEndLayer) { }
public LayerList(IDependencyGraph<string> graph, Builder pipelineBuilder) : base(graph, pipelineBuilder)
{
}
}
#endregion
}
}

View File

@ -1,8 +1,8 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.RunnersCore;
using System;
using System.Collections;
using System.Collections.Generic;

View File

@ -1,8 +1,8 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.RunnersCore;
using System;
using System.Linq;
using System.Runtime.CompilerServices;
@ -17,7 +17,7 @@ namespace DCFApixels.DragonECS
[MetaID("DragonECS_EF8A557C9201E6F04D4A76DC670BDE19")]
public interface IEcsProcess : IEcsMember { }
namespace Core
namespace RunnersCore
{
//добавить инъекцию в раннеры
public abstract class EcsRunner
@ -63,23 +63,6 @@ namespace DCFApixels.DragonECS
#endregion
public delegate void ActionWithData<in TProcess, T>(TProcess process, ref T Data);
#region MetaProxy
protected class RunnerMetaProxy : MetaProxyBase
{
private TypeMeta _processMeta;
public override MetaColor? Color => MetaColor.DragonRose;
public override MetaDescription Description => new MetaDescription(EcsConsts.AUTHOR, $"Runner for {_processMeta.TypeName} process.");
public override MetaGroup Group => MetaGroup.FromName(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP);
public RunnerMetaProxy(Type type) : base(type)
{
if (type.IsGenericType)
{
_processMeta = type.GetGenericArguments()[0].GetMeta();
}
}
}
#endregion
}
[MetaColor(MetaColor.DragonRose)]
@ -97,9 +80,9 @@ namespace DCFApixels.DragonECS
[MetaColor(MetaColor.DragonRose)]
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.OTHER_GROUP)]
[MetaDescription(EcsConsts.AUTHOR, "...")]
[MetaTags(MetaTags.HIDDEN)]
[MetaID("DragonECS_7DB3557C9201F85E0E1C17D7B19D9CEE")]
[MetaProxy(typeof(RunnerMetaProxy), true)]
public abstract class EcsRunner<TProcess> : EcsRunner, IEcsRunner, IEcsProcess
where TProcess : IEcsProcess
{
@ -165,7 +148,7 @@ namespace DCFApixels.DragonECS
#region Constructors
public RunHelper(EcsRunner<TProcess> runner) : this(runner,
#if DEBUG
typeof(TProcess).GetMeta().Name)
typeof(TProcess).ToMeta().Name)
#else
string.Empty)
#endif
@ -315,7 +298,7 @@ namespace DCFApixels.DragonECS
#region Constructors
public RunHelperWithFinally(EcsRunner<TProcess> runner) : this(runner,
#if DEBUG
typeof(TProcess).GetMeta().Name)
typeof(TProcess).ToMeta().Name)
#else
string.Empty)
#endif
@ -493,6 +476,8 @@ namespace DCFApixels.DragonECS
#endregion
}
#endregion
//----
}
}

View File

@ -65,7 +65,7 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _excs; }
}
/// <summary> Sorted set any constraints presented as global type codes. </summary>
/// <summary> Sorted set excluding constraints presented as global type codes. </summary>
public ReadOnlySpan<EcsTypeCode> AnyTypeCodes
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -109,15 +109,9 @@ namespace DCFApixels.DragonECS
public static Builder Inc(Type type) { return Builder.New().Inc(type); }
public static Builder Exc(Type type) { return Builder.New().Exc(type); }
public static Builder Any(Type type) { return Builder.New().Any(type); }
public static Builder Inc(params Type[] types) { return Builder.New().Inc(types); }
public static Builder Exc(params Type[] types) { return Builder.New().Exc(types); }
public static Builder Any(params Type[] types) { return Builder.New().Any(types); }
public static Builder Inc(EcsTypeCode typeCode) { return Builder.New().Inc(typeCode); }
public static Builder Exc(EcsTypeCode typeCode) { return Builder.New().Exc(typeCode); }
public static Builder Any(EcsTypeCode typeCode) { return Builder.New().Any(typeCode); }
public static Builder Inc(params EcsTypeCode[] typeCodes) { return Builder.New().Inc(typeCodes); }
public static Builder Exc(params EcsTypeCode[] typeCodes) { return Builder.New().Exc(typeCodes); }
public static Builder Any(params EcsTypeCode[] typeCodes) { return Builder.New().Any(typeCodes); }
private static EcsStaticMask CreateMask(Key key)
{
if (_ids.TryGetValue(key, out EcsStaticMask result) == false)
@ -311,24 +305,12 @@ namespace DCFApixels.DragonECS
#endregion
#region Inc/Exc/Combine/Except
public Builder Inc() { return this; }
public Builder Exc() { return this; }
public Builder Any() { return this; }
public Builder Inc<T>() { return Inc(EcsTypeCodeManager.Get<T>()); }
public Builder Exc<T>() { return Exc(EcsTypeCodeManager.Get<T>()); }
public Builder Any<T>() { return Any(EcsTypeCodeManager.Get<T>()); }
public Builder Inc(Type type) { return Inc(EcsTypeCodeManager.Get(type)); }
public Builder Exc(Type type) { return Exc(EcsTypeCodeManager.Get(type)); }
public Builder Any(Type type) { return Any(EcsTypeCodeManager.Get(type)); }
public Builder Inc(params Type[] types) { foreach (var type in types) { Inc(type); } return this; }
public Builder Exc(params Type[] types) { foreach (var type in types) { Exc(type); } return this; }
public Builder Any(params Type[] types) { foreach (var type in types) { Any(type); } return this; }
public Builder Inc(ReadOnlySpan<Type> types) { foreach (var type in types) { Inc(type); } return this; }
public Builder Exc(ReadOnlySpan<Type> types) { foreach (var type in types) { Exc(type); } return this; }
public Builder Any(ReadOnlySpan<Type> types) { foreach (var type in types) { Any(type); } return this; }
public Builder Inc(IEnumerable<Type> types) { foreach (var type in types) { Inc(type); } return this; }
public Builder Exc(IEnumerable<Type> types) { foreach (var type in types) { Exc(type); } return this; }
public Builder Any(IEnumerable<Type> types) { foreach (var type in types) { Any(type); } return this; }
public Builder Inc(EcsTypeCode typeCode)
{
if (_version != _builder._version) { Throw.CantReuseBuilder(); }
@ -347,15 +329,6 @@ namespace DCFApixels.DragonECS
_builder.Any(typeCode);
return this;
}
public Builder Inc(params EcsTypeCode[] typeCodes) { foreach (var typeCode in typeCodes) { Inc(typeCode); } return this; }
public Builder Exc(params EcsTypeCode[] typeCodes) { foreach (var typeCode in typeCodes) { Exc(typeCode); } return this; }
public Builder Any(params EcsTypeCode[] typeCodes) { foreach (var typeCode in typeCodes) { Any(typeCode); } return this; }
public Builder Inc(ReadOnlySpan<EcsTypeCode> typeCodes) { foreach (var typeCode in typeCodes) { Inc(typeCode); } return this; }
public Builder Exc(ReadOnlySpan<EcsTypeCode> typeCodes) { foreach (var typeCode in typeCodes) { Exc(typeCode); } return this; }
public Builder Any(ReadOnlySpan<EcsTypeCode> typeCodes) { foreach (var typeCode in typeCodes) { Any(typeCode); } return this; }
public Builder Inc(IEnumerable<EcsTypeCode> typeCodes) { foreach (var typeCode in typeCodes) { Inc(typeCode); } return this; }
public Builder Exc(IEnumerable<EcsTypeCode> typeCodes) { foreach (var typeCode in typeCodes) { Exc(typeCode); } return this; }
public Builder Any(IEnumerable<EcsTypeCode> typeCodes) { foreach (var typeCode in typeCodes) { Any(typeCode); } return this; }
public Builder Combine(EcsStaticMask mask)
{
if (_version != _builder._version) { Throw.CantReuseBuilder(); }
@ -381,7 +354,6 @@ namespace DCFApixels.DragonECS
return result;
}
}
public static implicit operator EcsStaticMask(Builder a) { return a.Build(); }
public void Cancel()
{
if (_version != _builder._version) { Throw.CantReuseBuilder(); }

View File

@ -2,6 +2,7 @@
#undef DEBUG
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.PoolsCore;
namespace DCFApixels.DragonECS
{

View File

@ -4,6 +4,7 @@
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.Core.Unchecked;
using DCFApixels.DragonECS.PoolsCore;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -70,7 +71,6 @@ namespace DCFApixels.DragonECS
private int[] _delEntBuffer = Array.Empty<int>();
private int _delEntBufferCount = 0;
private int[] _emptyEntities = Array.Empty<int>();
private int _emptyEntitiesLength = 0;
private int _emptyEntitiesCount = 0;
private bool _isEnableAutoReleaseDelEntBuffer = true;
@ -86,7 +86,6 @@ namespace DCFApixels.DragonECS
private StructList<IEcsWorldEventListener> _listeners = new StructList<IEcsWorldEventListener>(2);
private StructList<IEcsEntityEventListener> _entityListeners = new StructList<IEcsEntityEventListener>(2);
private bool _hasAnyEntityListener = false;
#region Properties
EcsWorld IEntityStorage.World
@ -395,11 +394,8 @@ namespace DCFApixels.DragonECS
{
slot.gen |= GEN_SLEEP_MASK;
}
if (_hasAnyEntityListener)
{
_entityListeners.InvokeOnNewEntity(entityID);
}
MoveToEmptyEntities(entityID, false);
_entityListeners.InvokeOnNewEntity(entityID);
MoveToEmptyEntities(entityID);
}
@ -440,92 +436,25 @@ namespace DCFApixels.DragonECS
_delEntBuffer[_delEntBufferCount++] = entityID;
_entities[entityID].isUsed = false;
_entitiesCount--;
if (_hasAnyEntityListener)
{
_entityListeners.InvokeOnDelEntity(entityID);
}
_entityListeners.InvokeOnDelEntity(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void MoveToEmptyEntities(int entityID, bool readyToRemove)
private void MoveToEmptyEntities(int entityID)
{
if (readyToRemove)
{
entityID |= int.MinValue;
}
_emptyEntities[_emptyEntitiesLength++] = entityID;
_emptyEntitiesCount++;
if (_emptyEntitiesLength == _emptyEntities.Length)
{
ReleaseEmptyEntitiesBuffer_OnlyReadyToRemove();
}
_emptyEntities[_emptyEntitiesCount++] = entityID;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RemoveFromEmptyEntities(int entityID)
private bool RemoveFromEmptyEntities(int entityID)
{
const int THRESHOLD = 16;
_emptyEntitiesCount--;
if (_emptyEntitiesLength < THRESHOLD)
for (int i = _emptyEntitiesCount - 1; i >= 0; i--)
{
for (int i = _emptyEntitiesLength - 1; i >= 0; i--)
if (_emptyEntities[i] == entityID)
{
if ((_emptyEntities[i] & int.MaxValue) == entityID)
{
_emptyEntities[i] = _emptyEntities[--_emptyEntitiesLength];
}
_emptyEntities[i] = _emptyEntities[--_emptyEntitiesCount];
return true;
}
}
#if DRAGONECS_DEEP_DEBUG
if (_emptyEntitiesCount < 0)
{
Throw.DeepDebugException();
}
#endif
if (_emptyEntitiesCount == 0)
{
_emptyEntitiesCount = 0;
_emptyEntitiesLength = 0;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReleaseEmptyEntitiesBuffer()
{
for (int i = 0; i < _emptyEntitiesLength; i++)
{
var entityID = _emptyEntities[i] & int.MaxValue;
if (IsUsed(entityID) && _entities[entityID].componentsCount == 0)
{
DelEntity(entityID);
}
}
_emptyEntitiesCount = 0;
_emptyEntitiesLength = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReleaseEmptyEntitiesBuffer_OnlyReadyToRemove()
{
var newCount = 0;
for (int i = 0; i < _emptyEntitiesLength; i++)
{
var entityID = _emptyEntities[i];
bool isReady = entityID < 0;
entityID &= int.MaxValue;
if (IsUsed(entityID) && _entities[entityID].componentsCount == 0)
{
if (isReady)
{
DelEntity(entityID);
}
else
{
_emptyEntities[newCount++] = entityID;
}
}
}
_emptyEntitiesCount = newCount;
_emptyEntitiesLength = newCount;
return false;
}
#endregion
@ -769,7 +698,7 @@ namespace DCFApixels.DragonECS
}
else
{
poolIdsPtr = MemoryAllocator.Alloc<int>(count).Ptr;
poolIdsPtr = UnmanagedArrayUtility.New<int>(count);
}
UnsafeArray<int> ua = UnsafeArray<int>.Manual(poolIdsPtr, count);
@ -782,7 +711,7 @@ namespace DCFApixels.DragonECS
if (count >= BUFFER_THRESHOLD)
{
MemoryAllocator.Free(poolIdsPtr);
UnmanagedArrayUtility.Free(poolIdsPtr);
}
@ -819,7 +748,7 @@ namespace DCFApixels.DragonECS
}
else
{
poolIdsPtr = MemoryAllocator.Alloc<int>(count).Ptr;
poolIdsPtr = UnmanagedArrayUtility.New<int>(count);
}
GetComponentTypeIDsFor_Internal(fromEntityID, poolIdsPtr, count);
@ -830,7 +759,7 @@ namespace DCFApixels.DragonECS
if (count >= BUFFER_THRESHOLD)
{
MemoryAllocator.Free(poolIdsPtr);
UnmanagedArrayUtility.Free(poolIdsPtr);
}
//foreach (var pool in _pools)
@ -961,12 +890,16 @@ namespace DCFApixels.DragonECS
{
ReleaseDelEntityBuffer(-1);
}
public void ReleaseDelEntityBuffer(int count)
public unsafe void ReleaseDelEntityBuffer(int count)
{
if (_emptyEntitiesLength <= 0 && _delEntBufferCount <= 0) { return; }
if (_emptyEntitiesCount <= 0 && _delEntBufferCount <= 0) { return; }
unchecked { _version++; }
ReleaseEmptyEntitiesBuffer();
for (int i = 0; i < _emptyEntitiesCount; i++)
{
TryDelEntity(_emptyEntities[i]);
}
_emptyEntitiesCount = 0;
if (count < 0)
{
@ -1090,12 +1023,10 @@ namespace DCFApixels.DragonECS
public void AddListener(IEcsEntityEventListener entityEventListener)
{
_entityListeners.Add(entityEventListener);
_hasAnyEntityListener = _entityListeners.Count > 0;
}
public void RemoveListener(IEcsEntityEventListener entityEventListener)
{
_entityListeners.Remove(entityEventListener);
_hasAnyEntityListener = _entityListeners.Count > 0;
}
#endregion
@ -1151,7 +1082,7 @@ namespace DCFApixels.DragonECS
}
else
{
poolIdsPtr = MemoryAllocator.Alloc<int>(count).Ptr;
poolIdsPtr = UnmanagedArrayUtility.New<int>(count);
}
GetComponentTypeIDsFor_Internal(entityID, poolIdsPtr, count);
@ -1174,7 +1105,7 @@ namespace DCFApixels.DragonECS
if (count >= BUFFER_THRESHOLD)
{
MemoryAllocator.Free(poolIdsPtr);
UnmanagedArrayUtility.Free(poolIdsPtr);
}
}
public ReadOnlySpan<object> GetComponentsFor(int entityID)
@ -1421,7 +1352,6 @@ namespace DCFApixels.DragonECS
public interface IEcsEntityEventListener
{
void OnNewEntity(int entityID);
void OnMigrateEntity(int entityID);
void OnDelEntity(int entityID);
}
internal static class WorldEventListExtensions
@ -1459,14 +1389,6 @@ namespace DCFApixels.DragonECS
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnMigrateEntity(this ref StructList<IEcsEntityEventListener> self, int entityID)
{
for (int i = 0, iMax = self.Count; i < iMax; i++)
{
self[i].OnMigrateEntity(entityID);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnDelEntity(this ref StructList<IEcsEntityEventListener> self, int entityID)
{
for (int i = 0, iMax = self.Count; i < iMax; i++)

View File

@ -1,8 +1,8 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.PoolsCore;
using System;
using System.Linq;
using System.Runtime.CompilerServices;
@ -127,6 +127,7 @@ namespace DCFApixels.DragonECS
{
return _cmpTypeCode_2_CmpTypeIDs.Contains((int)EcsTypeCodeManager.Get(componentType));
}
//TODO пересмотреть нейминг или функцию
public bool IsComponentTypeDeclared(int componentTypeID)
{
if (componentTypeID >= 0 && componentTypeID < _pools.Length)
@ -286,10 +287,6 @@ namespace DCFApixels.DragonECS
RemoveFromEmptyEntities(entityID);
}
_entityComponentMasks[(entityID << _entityComponentMaskLengthBitShift) + maskBit.chunkIndex] |= maskBit.mask;
if (_hasAnyEntityListener)
{
_entityListeners.InvokeOnMigrateEntity(entityID);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UnregisterEntityComponent(int entityID, int componentTypeID, EcsMaskChunck maskBit)
@ -300,15 +297,12 @@ namespace DCFApixels.DragonECS
slot.version++;
var count = --_entities[entityID].componentsCount;
_entityComponentMasks[(entityID << _entityComponentMaskLengthBitShift) + maskBit.chunkIndex] &= ~maskBit.mask;
if (count == 0 && IsUsed(entityID))
{
MoveToEmptyEntities(entityID, true);
MoveToEmptyEntities(entityID);
}
CheckUnregisterValid(count, entityID);
if (_hasAnyEntityListener)
{
_entityListeners.InvokeOnMigrateEntity(entityID);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool TryRegisterEntityComponent(int entityID, int componentTypeID, EcsMaskChunck maskBit)
@ -327,10 +321,6 @@ namespace DCFApixels.DragonECS
{
RemoveFromEmptyEntities(entityID);
}
if (_hasAnyEntityListener)
{
_entityListeners.InvokeOnMigrateEntity(entityID);
}
return true;
}
return false;
@ -351,13 +341,9 @@ namespace DCFApixels.DragonECS
if (count == 0 && IsUsed(entityID))
{
MoveToEmptyEntities(entityID, true);
MoveToEmptyEntities(entityID);
}
CheckUnregisterValid(count, entityID);
if (_hasAnyEntityListener)
{
_entityListeners.InvokeOnMigrateEntity(entityID);
}
return true;
}
return false;

View File

@ -5,6 +5,7 @@ using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
@ -25,15 +26,12 @@ namespace DCFApixels.DragonECS
private static EcsWorld[] _worlds = Array.Empty<EcsWorld>();
private static readonly IdDispenser _worldIdDispenser = new IdDispenser(4, 0, n => Array.Resize(ref _worlds, n));
private static StructList<WorldComponentPoolAbstract> _allWorldComponentPools = new StructList<WorldComponentPoolAbstract>(64);
private static readonly object _worldLock = new object();
private StructList<WorldComponentPoolAbstract> _worldComponentPools;
private int _builtinWorldComponentsCount = 0;
public static int AllWorldsCount
{
get { return _worldIdDispenser.Count; }
}
static EcsWorld()
{
_worlds[NULL_WORLD_ID] = new NullWorld();
@ -147,7 +145,7 @@ namespace DCFApixels.DragonECS
private static short _count;
private static short[] _recycledItems = new short[4];
private static short _recycledItemsCount;
private static readonly IEcsWorldComponent<T> _interface = EcsWorldComponent<T>.CustomHandler;
private static readonly IEcsWorldComponent<T> _interface = EcsWorldComponentHandler<T>.instance;
private static readonly Abstract _controller = new Abstract();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -294,8 +292,6 @@ namespace DCFApixels.DragonECS
#endregion
#region NullWorld
[MetaTags(MetaTags.HIDDEN)]
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.OTHER_GROUP)]
private sealed class NullWorld : EcsWorld
{
internal NullWorld() : base(new EcsWorldConfig(4, 4, 4, 4, 4), null, 0) { }
@ -325,5 +321,27 @@ namespace DCFApixels.DragonECS
}
}
#endregion
#region Obsolete
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use EcsWorld.ID")]
public short id
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return ID; }
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("The GetPoolInstance(int componentTypeID) method will be removed in future updates, use FindPoolInstance(Type componentType)")]
public IEcsPool GetPoolInstance(int componentTypeID)
{
return FindPoolInstance(componentTypeID);
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("The GetPoolInstance(Type componentType) method will be removed in future updates, use FindPoolInstance(Type componentType)")]
public IEcsPool GetPoolInstance(Type componentType)
{
return FindPoolInstance(componentType);
}
#endregion
}
}

View File

@ -3,7 +3,6 @@
#endif
using System;
using System.Runtime.CompilerServices;
using static DCFApixels.DragonECS.Core.Internal.MemoryAllocator;
#if ENABLE_IL2CPP
using Unity.IL2CPP.CompilerServices;
#endif
@ -14,13 +13,13 @@ namespace DCFApixels.DragonECS.Core.Internal
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
internal sealed unsafe class EcsWhereExecutor : MaskQueryExecutor
internal sealed class EcsWhereExecutor : MaskQueryExecutor
{
private EcsMaskIterator _iterator;
private HMem<int> _filteredAllEntities = Alloc<int>(32);
private int[] _filteredAllEntities = new int[32];
private int _filteredAllEntitiesCount = 0;
private HMem<int> _filteredEntities = default;
private int[] _filteredEntities = null;
private int _filteredEntitiesCount = 0;
private long _version;
@ -55,14 +54,6 @@ namespace DCFApixels.DragonECS.Core.Internal
protected sealed override void OnDestroy()
{
if (_isDestroyed) { return; }
if (_filteredAllEntities.IsCreated)
{
_filteredAllEntities.DisposeAndReset();
}
if (_filteredEntities.IsCreated)
{
_filteredEntities.DisposeAndReset();
}
_isDestroyed = true;
_versionsChecker.Dispose();
}
@ -76,7 +67,7 @@ namespace DCFApixels.DragonECS.Core.Internal
if (_versionsChecker.CheckAndNext() == false)
{
_version++;
_filteredAllEntitiesCount = _iterator.CacheTo(World.Entities, ref _filteredAllEntities);
_filteredAllEntitiesCount = _iterator.IterateTo(World.Entities, ref _filteredAllEntities);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -86,19 +77,19 @@ namespace DCFApixels.DragonECS.Core.Internal
if (span.IsNull) { Throw.ArgumentNull(nameof(span)); }
if (span.WorldID != World.ID) { Throw.Quiery_ArgumentDifferentWorldsException(); }
#endif
if (_filteredEntities.IsCreated == false)
if (_filteredEntities == null)
{
_filteredEntities = Alloc<int>(32);
_filteredEntities = new int[32];
}
_filteredEntitiesCount = _iterator.CacheTo(span, ref _filteredEntities);
_filteredEntitiesCount = _iterator.IterateTo(span, ref _filteredEntities);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsUnsafeSpan Execute()
public EcsSpan Execute()
{
Execute_Iternal();
#if DEBUG && DRAGONECS_DEEP_DEBUG
var result = new EcsUnsafeSpan(World.ID, _filteredAllEntities.Ptr, _filteredAllEntitiesCount);
var newSpan = new EcsSpan(World.ID, _filteredAllEntities, _filteredAllEntitiesCount);
using (EcsGroup group = EcsGroup.New(World))
{
foreach (var e in World.Entities)
@ -109,20 +100,20 @@ namespace DCFApixels.DragonECS.Core.Internal
}
}
if (group.SetEquals(result.ToSpan()) == false)
if (group.SetEquals(newSpan) == false)
{
int[] array = new int[_filteredAllEntities.Length];
var count = _iterator.CacheTo(World.Entities, ref array);
var count = _iterator.IterateTo(World.Entities, ref array);
EcsDebug.PrintError(result.ToString() + "\r\n" + group.ToSpan().ToString());
EcsDebug.PrintError(newSpan.ToString() + "\r\n" + group.ToSpan().ToString());
Throw.DeepDebugException();
}
}
#endif
return new EcsUnsafeSpan(World.ID, _filteredAllEntities.Ptr, _filteredAllEntitiesCount); ;
return new EcsSpan(World.ID, _filteredAllEntities, _filteredAllEntitiesCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsUnsafeSpan ExecuteFor(EcsSpan span)
public EcsSpan ExecuteFor(EcsSpan span)
{
if (span.IsSourceEntities)
{
@ -130,8 +121,8 @@ namespace DCFApixels.DragonECS.Core.Internal
}
ExecuteFor_Iternal(span);
#if DEBUG && DRAGONECS_DEEP_DEBUG
var result = new EcsUnsafeSpan(World.ID, _filteredEntities.Ptr, _filteredEntitiesCount);
foreach (var e in result)
var newSpan = new EcsSpan(World.ID, _filteredEntities, _filteredEntitiesCount);
foreach (var e in newSpan)
{
if (World.IsMatchesMask(Mask, e) == false)
{
@ -139,28 +130,27 @@ namespace DCFApixels.DragonECS.Core.Internal
}
}
#endif
return new EcsUnsafeSpan(World.ID, _filteredEntities.Ptr, _filteredEntitiesCount); ;
return new EcsSpan(World.ID, _filteredEntities, _filteredEntitiesCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsUnsafeSpan Execute(Comparison<int> comparison)
public EcsSpan Execute(Comparison<int> comparison)
{
Execute_Iternal();
SortHalper.Sort(_filteredAllEntities.AsSpan(_filteredAllEntitiesCount), comparison);
return new EcsUnsafeSpan(World.ID, _filteredAllEntities.Ptr, _filteredAllEntitiesCount);
ArraySortHalperX<int>.Sort(_filteredAllEntities, comparison, _filteredAllEntitiesCount);
return new EcsSpan(World.ID, _filteredAllEntities, _filteredAllEntitiesCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsUnsafeSpan ExecuteFor(EcsSpan source, Comparison<int> comparison)
public EcsSpan ExecuteFor(EcsSpan span, Comparison<int> comparison)
{
if (source.IsSourceEntities)
if (span.IsSourceEntities)
{
return Execute(comparison);
}
ExecuteFor_Iternal(source);
SortHalper.Sort(_filteredEntities.AsSpan(_filteredEntitiesCount), comparison);
return new EcsUnsafeSpan(World.ID, _filteredEntities.Ptr, _filteredEntitiesCount);
ExecuteFor_Iternal(span);
ArraySortHalperX<int>.Sort(_filteredEntities, comparison, _filteredEntitiesCount);
return new EcsSpan(World.ID, _filteredEntities, _filteredEntitiesCount);
}
public override EcsSpan Snapshot() { return Execute(); }
#endregion
}
}

View File

@ -66,7 +66,7 @@ namespace DCFApixels.DragonECS.Core.Internal
if (_versionsChecker.CheckAndNext() == false)
{
_version++;
_iterator.CacheTo(World.Entities, _filteredAllGroup);
_iterator.IterateTo(World.Entities, _filteredAllGroup);
#if DEBUG && DRAGONECS_DEEP_DEBUG
if (_filteredGroup == null)
{
@ -98,7 +98,7 @@ namespace DCFApixels.DragonECS.Core.Internal
{
_filteredGroup = EcsGroup.New(World);
}
_iterator.CacheTo(span, _filteredGroup);
_iterator.IterateTo(span, _filteredGroup);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -117,7 +117,6 @@ namespace DCFApixels.DragonECS.Core.Internal
ExecuteFor_Iternal(span);
return _filteredGroup;
}
public override EcsSpan Snapshot() { return Execute(); }
#endregion
}
}

View File

@ -105,7 +105,6 @@ namespace DCFApixels.DragonECS.Core
}
protected abstract void OnInitialize();
protected abstract void OnDestroy();
public abstract EcsSpan Snapshot();
}
#if ENABLE_IL2CPP

View File

@ -84,76 +84,6 @@ namespace DCFApixels.DragonECS
}
#endregion
#region WhereUnsafe
public static EcsUnsafeSpan WhereUnsafe<TCollection, TAspect>(this TCollection entities, out TAspect aspect)
where TAspect : new()
where TCollection : IEntityStorage
{
return entities.ToSpan().WhereUnsafe(out aspect);
}
public static EcsUnsafeSpan WhereUnsafe<TAspect>(this EcsReadonlyGroup group, out TAspect aspect)
where TAspect : new()
{
return group.ToSpan().WhereUnsafe(out aspect);
}
public static EcsUnsafeSpan WhereUnsafe<TAspect>(this EcsSpan span, out TAspect aspect)
where TAspect : new()
{
span.World.GetQueryCache(out EcsWhereExecutor executor, out aspect);
return executor.ExecuteFor(span);
}
public static EcsUnsafeSpan WhereUnsafe<TCollection>(this TCollection entities, IComponentMask mask)
where TCollection : IEntityStorage
{
return entities.ToSpan().WhereUnsafe(mask);
}
public static EcsUnsafeSpan WhereUnsafe(this EcsReadonlyGroup group, IComponentMask mask)
{
return group.ToSpan().WhereUnsafe(mask);
}
public static EcsUnsafeSpan WhereUnsafe(this EcsSpan span, IComponentMask mask)
{
var executor = span.World.GetExecutorForMask<EcsWhereExecutor>(mask);
return executor.ExecuteFor(span);
}
#endregion
#region WhereUnsafe with sort
public static EcsUnsafeSpan WhereUnsafe<TCollection, TAspect>(this TCollection entities, out TAspect aspect, Comparison<int> comparison)
where TAspect : new()
where TCollection : IEntityStorage
{
return entities.ToSpan().WhereUnsafe(out aspect, comparison);
}
public static EcsUnsafeSpan WhereUnsafe<TAspect>(this EcsReadonlyGroup group, out TAspect aspect, Comparison<int> comparison)
where TAspect : new()
{
return group.ToSpan().WhereUnsafe(out aspect, comparison);
}
public static EcsUnsafeSpan WhereUnsafe<TAspect>(this EcsSpan span, out TAspect aspect, Comparison<int> comparison)
where TAspect : new()
{
span.World.GetQueryCache(out EcsWhereExecutor executor, out aspect);
return executor.ExecuteFor(span, comparison);
}
public static EcsUnsafeSpan WhereUnsafe<TCollection>(this TCollection entities, IComponentMask mask, Comparison<int> comparison)
where TCollection : IEntityStorage
{
return entities.ToSpan().WhereUnsafe(mask, comparison);
}
public static EcsUnsafeSpan WhereUnsafe(this EcsReadonlyGroup group, IComponentMask mask, Comparison<int> comparison)
{
return group.ToSpan().WhereUnsafe(mask, comparison);
}
public static EcsUnsafeSpan WhereUnsafe(this EcsSpan span, IComponentMask mask, Comparison<int> comparison)
{
var executor = span.World.GetExecutorForMask<EcsWhereExecutor>(mask);
return executor.ExecuteFor(span);
}
#endregion
#region WhereToGroup
public static EcsReadonlyGroup WhereToGroup<TCollection, TAspect>(this TCollection entities, out TAspect aspect)
where TAspect : new()

View File

@ -126,7 +126,7 @@ namespace DCFApixels.DragonECS
{
return (T)Extract_Internal(typeof(T));
}
private object Extract_Internal(Type type)
private object Extract_Internal(Type type)//TODO проверить
{
if (_nodes.TryGetValue(type, out InjectionNodeBase node))
{
@ -192,7 +192,7 @@ namespace DCFApixels.DragonECS
FindMonoWorld(obj);
_injections.Add(new Injection<T>(obj));
}
public void Extract<T>(ref T obj)
public void Extract<T>(ref T obj) // TODO проверить
{
Type type = typeof(T);
for (int i = _injections.Count - 1; i >= 0; i--)
@ -272,7 +272,7 @@ namespace DCFApixels.DragonECS
}
public void InjectTo(Injector injector, EcsPipeline pipeline)
{
var monoWorldProcess = pipeline.GetProcess<IMonoWorldInject>();
var monoWorldProcess = pipeline.GetProcess<IMonoWorldInject>(); // TODO Проверить IMonoWorldInject
foreach (var monoWorldSystem in monoWorldProcess)
{
monoWorldSystem.World = _monoWorld;

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f5d8c72b3decc2b488a616932366ff0f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,97 +1,25 @@
using System;
using System.Runtime.CompilerServices;
using static DCFApixels.DragonECS.Core.Internal.MemoryAllocator;
#if ENABLE_IL2CPP
using Unity.IL2CPP.CompilerServices;
#endif
namespace DCFApixels.DragonECS.Core.Internal
{
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
internal static unsafe class AllocatorUtility
internal static class AllocatorUtility
{
public static void ClearAllocatedMemory(IntPtr ptr, int startByte, int lengthInBytes)
public static unsafe void ClearAllocatedMemory(IntPtr ptr, int startByte, int lengthInBytes)
{
ClearAllocatedMemory((byte*)ptr, startByte, lengthInBytes);
}
public static void ClearAllocatedMemory(byte* ptr, int startByte, int lengthInBytes)
public static unsafe void ClearAllocatedMemory(byte* ptr, int startByte, int lengthInBytes)
{
#if ENABLE_DUMMY_SPAN
lengthInBytes += startByte;
for (int i = startByte; i < lengthInBytes; i++)
{
ptr[i] = 0;
}
#else
Span<byte> memorySpan = new Span<byte>(ptr + startByte, lengthInBytes);
memorySpan.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CacheTo(this EcsMaskIterator it, EcsSpan source, ref HMem<int> array)
{
switch (it.MaskFlags)
{
case EcsMaskFlags.Empty:
{
if(array.Length < source.Count)
{
array = Realloc<int>(array, source.Count);
}
source.AsSystemSpan().CopyTo(array.AsSpan());
return source.Count;
}
case EcsMaskFlags.Inc:
{
return it.IterateOnlyInc(source).CacheTo(ref array);
}
case EcsMaskFlags.Exc:
case EcsMaskFlags.Any:
case EcsMaskFlags.IncExc:
case EcsMaskFlags.IncAny:
case EcsMaskFlags.ExcAny:
case EcsMaskFlags.IncExcAny:
{
return it.Iterate(source).CacheTo(ref array);
}
case EcsMaskFlags.Broken:
{
return 0;
}
default:
{
Throw.UndefinedException();
return 0;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CacheTo(this EcsMaskIterator.OnlyIncEnumerable e, ref HMem<int> array)
{
int count = 0;
var enumerator = e.GetEnumerator();
while (enumerator.MoveNext())
{
if (array.Length <= count)
{
array = Realloc<int>(array, array.Length << 1);
}
array.Ptr[count++] = enumerator.Current;
}
return count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CacheTo(this EcsMaskIterator.Enumerable e, ref HMem<int> array)
{
int count = 0;
var enumerator = e.GetEnumerator();
while (enumerator.MoveNext())
{
if (array.Length <= count)
{
array = Realloc<int>(array, array.Length << 1);
}
array.Ptr[count++] = enumerator.Current;
}
return count;
#endif
}
}
}

View File

@ -2,7 +2,6 @@
#undef DEBUG
#endif
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS.Core.Internal
@ -10,7 +9,6 @@ namespace DCFApixels.DragonECS.Core.Internal
internal unsafe static class MemoryAllocator
{
#if DEBUG
private static ulong _inrement = 0;
private static IdDispenser _idDispenser;
private static HandlerDebugInfo[] _debugInfos;
#endif
@ -26,51 +24,32 @@ namespace DCFApixels.DragonECS.Core.Internal
_debugInfos = new HandlerDebugInfo[32];
#endif
}
private static HandlerDebugInfo[] CurrentHandlersList
{
get { return CreateCurrentHandlersList_Debug(); }
}
internal static HandlerDebugInfo[] CreateCurrentHandlersList_Debug()
{
#if DEBUG
var result = new HandlerDebugInfo[_idDispenser.Count];
int i = 0;
foreach (var id in _idDispenser)
{
result[i++] = _debugInfos[id];
}
SortHalper.SortBy<HandlerDebugInfo, ulong>(result, o => o.increment);
return result;
#else
return Array.Empty<HandlerDebugInfo>();
#endif
}
#region AllocAndInit
public static HMem<T> AllocAndInit<T>(int count) where T : unmanaged
public static Handler AllocAndInit<T>(int count) where T : unmanaged
{
return new HMem<T>(AllocAndInit_Internal(Marshal.SizeOf<T>() * count, typeof(T)), count);
return AllocAndInit_Internal(Marshal.SizeOf<T>() * count, typeof(T));
}
public static HMem<byte> AllocAndInit(int byteLength)
public static Handler AllocAndInit(int byteLength)
{
return new HMem<byte>(AllocAndInit_Internal(byteLength, null), byteLength);
return AllocAndInit_Internal(byteLength, null);
}
public static Handler AllocAndInit_Internal(int byteLength, Type type)
{
Handler handler = Alloc_Internal(byteLength, type);
AllocatorUtility.ClearAllocatedMemory(handler.RawPtr, 0, byteLength);
AllocatorUtility.ClearAllocatedMemory(handler.Ptr, 0, byteLength);
return handler;
}
#endregion
#region Alloc
public static HMem<T> Alloc<T>(int count) where T : unmanaged
public static Handler Alloc<T>(int count) where T : unmanaged
{
return new HMem<T>(Alloc_Internal(Marshal.SizeOf<T>() * count, typeof(T)), count);
return Alloc_Internal(Marshal.SizeOf<T>() * count, typeof(T));
}
public static HMem<byte> Alloc(int byteLength)
public static Handler Alloc(int byteLength)
{
return new HMem<byte>(Alloc_Internal(byteLength, null), byteLength); ;
return Alloc_Internal(byteLength, null);
}
public static Handler Alloc_Internal(int byteLength, Type type)
{
@ -96,7 +75,6 @@ namespace DCFApixels.DragonECS.Core.Internal
#if DRAGONECS_DEEP_DEBUG
_debugInfos[id].stackTrace = new System.Diagnostics.StackTrace();
#endif
_debugInfos[id].increment = ++_inrement;
_debugInfos[id].type = type;
_debugInfos[id].handler = handler;
#endif
@ -106,135 +84,69 @@ namespace DCFApixels.DragonECS.Core.Internal
#endregion
#region ReallocAndInit
public static HMem<T> ReallocAndInit<T>(T* target, int oldCount, int newCount) where T : unmanaged
public static Handler ReallocAndInit<T>(void* target, int oldCount, int newCount) where T : unmanaged
{
return ReallocAndInit<T>(Handler.FromDataPtr(target), oldCount, newCount);
}
public static HMem<byte> ReallocAndInit(void* target, int oldByteLength, int newByteLength)
public static Handler ReallocAndInit(void* target, int oldByteLength, int newByteLength)
{
return ReallocAndInit(Handler.FromDataPtr(target), oldByteLength, newByteLength);
}
public static HMem<T> ReallocAndInit<T>(HMem<T> target, int newCount) where T : unmanaged
public static Handler ReallocAndInit<T>(Handler target, int oldCount, int newCount) where T : unmanaged
{
var size = Marshal.SizeOf<T>();
return new HMem<T>(ReallocAndInit_Internal(target, size * target.Length, size * newCount, typeof(T)), newCount);
return ReallocAndInit_Internal(target, size * oldCount, size * newCount, typeof(T));
}
public static HMem<T> ReallocAndInit<T>(Handler target, int oldCount, int newCount) where T : unmanaged
public static Handler ReallocAndInit(Handler target, int oldByteLength, int newByteLength)
{
var size = Marshal.SizeOf<T>();
return new HMem<T>(ReallocAndInit_Internal(target, size * oldCount, size * newCount, typeof(T)), newCount);
}
public static HMem<byte> ReallocAndInit(Handler target, int oldByteLength, int newByteLength)
{
return new HMem<byte>(ReallocAndInit_Internal(target, oldByteLength, newByteLength, null), newByteLength);
return ReallocAndInit_Internal(target, oldByteLength, newByteLength, null);
}
private static Handler ReallocAndInit_Internal(Handler target, int oldByteLength, int newByteLength, Type newType)
{
Handler handler = Realloc_Internal(target, newByteLength, newType);
AllocatorUtility.ClearAllocatedMemory(handler.RawPtr, oldByteLength, newByteLength - oldByteLength);
AllocatorUtility.ClearAllocatedMemory(handler.Ptr, oldByteLength, newByteLength - oldByteLength);
return handler;
}
#endregion
#region Realloc
public static HMem<T> Realloc<T>(T* target, int newCount) where T : unmanaged
public static Handler Realloc<T>(void* target, int newCount) where T : unmanaged
{
return Realloc<T>(Handler.FromDataPtr(target), Marshal.SizeOf<T>() * newCount);
}
public static HMem<byte> Realloc(void* target, int newByteLength)
public static Handler Realloc(void* target, int newByteLength)
{
return new HMem<byte>(Realloc(Handler.FromDataPtr(target), newByteLength), newByteLength);
return Realloc(Handler.FromDataPtr(target), newByteLength);
}
public static HMem<T> Realloc<T>(HMem<T> target, int newCount) where T : unmanaged
public static Handler Realloc<T>(Handler target, int newCount) where T : unmanaged
{
return new HMem<T>(Realloc_Internal(target, Marshal.SizeOf<T>() * newCount, typeof(T)), newCount);
return Realloc_Internal(target, Marshal.SizeOf<T>() * newCount, typeof(T));
}
public static HMem<T> Realloc<T>(Handler target, int newCount) where T : unmanaged
public static Handler Realloc(Handler target, int newByteLength)
{
return new HMem<T>(Realloc_Internal(target, Marshal.SizeOf<T>() * newCount, typeof(T)), newCount);
}
public static HMem<byte> Realloc(Handler target, int newByteLength)
{
return new HMem<byte>(Realloc_Internal(target, newByteLength, null), newByteLength);
return Realloc_Internal(target, newByteLength, null);
}
private static Handler Realloc_Internal(Handler target, int newByteLength, Type newType)
{
newByteLength = newByteLength == 0 ? 1 : newByteLength;
if (target.IsCreated == false)
{
return Alloc_Internal(newByteLength, newType);
}
//#if DEBUG
// int id = 0;
// lock (_idDispenser)
// {
// if (_debugInfos.Length <= _idDispenser.Count)
// {
// Array.Resize(ref _debugInfos, ArrayUtility.NextPow2(_idDispenser.Count));
// }
// id = _idDispenser.UseFree();
// }
//#endif
#if DEBUG
var id = target.GetHandledPtr()->ID;
#endif
Meta* newHandledPtr = (Meta*)Marshal.ReAllocHGlobal(
(IntPtr)target.GetHandledPtr(),
(IntPtr)newByteLength + sizeof(Meta));
Meta* newHandledPtr = (Meta*)Marshal.ReAllocHGlobal((IntPtr)target.GetHandledPtr(), (IntPtr)newByteLength + sizeof(Meta));
Handler handler = Handler.FromHandledPtr(newHandledPtr);
#if DEBUG
newHandledPtr->ID = id;
newHandledPtr->ByteLength = newByteLength;
#if DRAGONECS_DEEP_DEBUG
_debugInfos[id].stackTrace = new System.Diagnostics.StackTrace();
_debugInfos[newHandledPtr->ID].stackTrace = new System.Diagnostics.StackTrace();
#endif
_debugInfos[id].increment = ++_inrement;
_debugInfos[id].type = newType;
_debugInfos[id].handler = handler;
_debugInfos[newHandledPtr->ID].type = newType;
_debugInfos[newHandledPtr->ID].handler = handler;
#endif
return handler;
}
#endregion
#region Clone
public static HMem<T> From<T>(HMem<T> source)
where T : unmanaged
{
var result = Alloc<T>(source.Length);
source.AsSpan().CopyTo(result.AsSpan());
return result;
}
public static HMem<T> From<T>(T* ptr, int length)
where T : unmanaged
{
return From<T>(new ReadOnlySpan<T>(ptr, length));
}
public static HMem<T> From<T>(T[] source)
where T : unmanaged
{
return From(new ReadOnlySpan<T>(source));
}
public static HMem<T> From<T>(ReadOnlySpan<T> source)
where T : unmanaged
{
var result = Alloc<T>(source.Length);
source.CopyTo(result.AsSpan());
return result;
}
#endregion
#region Free
public static void Free(Handler target)
{
Free_Internal(target.GetHandledPtr());
}
public static void FreeAndClear<T>(ref HMem<T> target)
where T : unmanaged
{
Free_Internal(target.Handler.GetHandledPtr());
target = default;
}
public static void FreeAndClear(ref Handler target)
{
Free_Internal(target.GetHandledPtr());
@ -247,10 +159,6 @@ namespace DCFApixels.DragonECS.Core.Internal
private static void Free_Internal(Meta* handledPtr)
{
#if DEBUG
if (handledPtr == null)
{
throw new ArgumentNullException();
}
lock (_idDispenser)
{
_idDispenser.Release(handledPtr->ID);
@ -291,7 +199,6 @@ namespace DCFApixels.DragonECS.Core.Internal
#if DRAGONECS_DEEP_DEBUG
public System.Diagnostics.StackTrace stackTrace;
#endif
public ulong increment;
public Type type;
public Handler handler;
#endif
@ -303,97 +210,11 @@ namespace DCFApixels.DragonECS.Core.Internal
}
#endregion
#region Handlers
public readonly struct HMem<T> : IDisposable, IEquatable<HMem<T>>
where T : unmanaged
{
public readonly T* Ptr;
public readonly int Length;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal HMem(Handler handler, int length)
{
Ptr = handler.As<T>();
Length = length;
}
public bool IsCreated
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return Ptr != null; }
}
public IntPtr RawPtr
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return new IntPtr(Ptr); }
}
public Handler Handler
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return Handler.FromDataPtr(Ptr); }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HMem<U> As<U>() where U : unmanaged
{
if (IsCreated)
{
return default;
}
long totalBytes = (long)Length * sizeof(T);
long newLengthLong = totalBytes / sizeof(U);
#if DEBUG
if (totalBytes % sizeof(U) != 0)
{
throw new InvalidOperationException($"Cannot cast Memory<{typeof(T).Name}> to Memory<{typeof(U).Name}> because the size of the underlying memory ({totalBytes} bytes) is not a multiple of the size of {typeof(U).Name} ({sizeof(U)} bytes).");
}
if (newLengthLong > int.MaxValue)
{
throw new InvalidOperationException($"Resulting length ({newLengthLong}) exceeds int.MaxValue.");
}
#endif
return new HMem<U>(Handler, (int)newLengthLong);
}
public void Dispose()
{
Handler.Dispose();
}
#if DEBUG
public override string ToString() { return Handler.DebuggerDisplay(); }
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() { return RawPtr.GetHashCode(); }
public override bool Equals(object obj) { return obj is Handler h && h == this; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(HMem<T> other) { return other.Ptr == Ptr; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(HMem<T> a, HMem<T> b) { return a.Ptr == b.Ptr; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(HMem<T> a, HMem<T> b) { return a.Ptr != b.Ptr; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan() { return new Span<T>(Ptr, Length); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan(int length)
{
#if DEBUG
if (length > Length) { Throw.UndefinedException(); }
#endif
return new Span<T>(Ptr, length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Handler(HMem<T> memory) { return memory.Handler; }
}
#if DEBUG
[System.Diagnostics.DebuggerDisplay("{DebuggerDisplay()}")]
[System.Diagnostics.DebuggerTypeProxy(typeof(DebuggerProxy))]
#endif
public readonly struct Handler : IDisposable, IEquatable<Handler>
public readonly struct Handler : IDisposable
{
public static readonly Handler Empty = new Handler();
internal readonly Meta* Data; // Data[-1] is meta;
@ -418,39 +239,16 @@ namespace DCFApixels.DragonECS.Core.Internal
#endif
}
public bool IsCreated
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return Data != null; }
}
public IntPtr RawPtr
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return (IntPtr)Data; }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T* As<T>() where T : unmanaged { return (T*)RawPtr; }
public bool IsEmpty { get { return Data == null; } }
public IntPtr Ptr { get { return (IntPtr)Data; } }
public T* As<T>() where T : unmanaged { return (T*)Ptr; }
public void Dispose() { Free((void*)RawPtr); }
#if DEBUG
public override string ToString() { return DebuggerDisplay(); }
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() { return RawPtr.GetHashCode(); }
public override bool Equals(object obj) { return obj is Handler h && h == this; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Handler other) { return other.Data == Data; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Handler a, Handler b) { return a.Data == b.Data; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Handler a, Handler b) { return a.Data != b.Data; }
void IDisposable.Dispose() { Free((void*)Ptr); }
#region Debugger
#if DEBUG
#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
internal string DebuggerDisplay()
internal unsafe string DebuggerDisplay()
{
if (Data == null)
{
@ -484,9 +282,8 @@ namespace DCFApixels.DragonECS.Core.Internal
}
return array;
}
[StructLayout(LayoutKind.Explicit)]
private struct Union
private unsafe struct Union
{
[FieldOffset(0)]
public Array array;
@ -506,13 +303,13 @@ namespace DCFApixels.DragonECS.Core.Internal
public HandlerDebugInfo[] OtherHandlersInfo;
public DebuggerProxy(Handler handler)
public unsafe DebuggerProxy(Handler handler)
{
IsAlive = handler.RawPtr.ToPointer() != null;
IsAlive = handler.Ptr.ToPointer() != null;
if (IsAlive == false) { return; }
Meta = handler.GetHandledPtr()[0];
_data = (byte*)handler.RawPtr;
_data = (byte*)handler.Ptr;
DebugInfo = _debugInfos[Meta.ID];
if (DebugInfo.type == null)
@ -536,20 +333,13 @@ namespace DCFApixels.DragonECS.Core.Internal
#endif
#endregion
}
#endregion
}
internal static class MemoryAllocatorHandlerExtensions
{
public static void DisposeAndReset(this ref MemoryAllocator.Handler self)
public static void Dispose(this ref MemoryAllocator.Handler self)
{
MemoryAllocator.FreeAndClear(ref self);
}
public static void DisposeAndReset<T>(this ref MemoryAllocator.HMem<T> self)
where T : unmanaged
{
self.Dispose();
self = default;
}
}
}

View File

@ -54,7 +54,7 @@ namespace DCFApixels.DragonECS.Core.Internal
{
MemoryAllocator.Free(_ptr);
}
_ptr = MemoryAllocator.Alloc<byte>(byteSize).Ptr;
_ptr = MemoryAllocator.Alloc<byte>(byteSize).As<byte>();
_byteSize = byteSize;
}
return (T*)_ptr;

View File

@ -0,0 +1,272 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
#if ENABLE_IL2CPP
using Unity.IL2CPP.CompilerServices;
#endif
namespace DCFApixels.DragonECS.Core.Internal
{
internal interface IStructComparer<T> : IComparer<T>
{
// a > b = return > 0
// int Compare(T a, T b);
}
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
internal static class ArraySortHalperX<T>
{
private const int IntrosortSizeThreshold = 16;
#region IStructComparer
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SwapIfGreater<TComparer>(T[] items, ref TComparer comparer, int i, int j) where TComparer : IStructComparer<T>
{
if (comparer.Compare(items[i], items[j]) > 0)
{
T key = items[i];
items[i] = items[j];
items[j] = key;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InsertionSort<TComparer>(T[] items, ref TComparer comparer) where TComparer : IStructComparer<T>
{
for (int i = 0; i < items.Length - 1; i++)
{
T t = items[i + 1];
int j = i;
while (j >= 0 && comparer.Compare(t, items[j]) < 0)
{
items[j + 1] = items[j];
j--;
}
items[j + 1] = t;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sort<TComparer>(T[] items, ref TComparer comparer) where TComparer : IStructComparer<T>
{
int length = items.Length;
if (length == 1)
{
return;
}
if (length <= IntrosortSizeThreshold)
{
if (length == 2)
{
SwapIfGreater(items, ref comparer, 0, 1);
return;
}
if (length == 3)
{
SwapIfGreater(items, ref comparer, 0, 1);
SwapIfGreater(items, ref comparer, 0, 2);
SwapIfGreater(items, ref comparer, 1, 2);
return;
}
InsertionSort(items, ref comparer);
return;
}
IStructComparer<T> packed = comparer;
Array.Sort(items, 0, items.Length, packed);
comparer = (TComparer)packed;
}
#endregion
#region Comparison
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SwapIfGreater(T[] items, Comparison<T> comparison, int i, int j)
{
if (comparison(items[i], items[j]) > 0)
{
T key = items[i];
items[i] = items[j];
items[j] = key;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InsertionSort(T[] items, int length, Comparison<T> comparison)
{
for (int i = 0; i < length - 1; i++)
{
T t = items[i + 1];
int j = i;
while (j >= 0 && comparison(t, items[j]) < 0)
{
items[j + 1] = items[j];
j--;
}
items[j + 1] = t;
}
}
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
private class ComparisonHach : IComparer<T>
{
public static readonly ComparisonHach Instance = new ComparisonHach();
public Comparison<T> comparison;
private ComparisonHach() { }
public int Compare(T x, T y) { return comparison(x, y); }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sort(T[] items, Comparison<T> comparison)
{
Sort(items, comparison, items.Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sort(T[] items, Comparison<T> comparison, int length)
{
if (length <= IntrosortSizeThreshold)
{
if (length == 1)
{
return;
}
if (length == 2)
{
SwapIfGreater(items, comparison, 0, 1);
return;
}
if (length == 3)
{
SwapIfGreater(items, comparison, 0, 1);
SwapIfGreater(items, comparison, 0, 2);
SwapIfGreater(items, comparison, 1, 2);
return;
}
InsertionSort(items, length, comparison);
return;
}
ComparisonHach.Instance.comparison = comparison;
Array.Sort(items, 0, length, ComparisonHach.Instance);
}
#endregion
}
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
internal static unsafe class UnsafeArraySortHalperX<T> where T : unmanaged
{
private const int IntrosortSizeThreshold = 16;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SwapIfGreater<TComparer>(T* items, ref TComparer comparer, int i, int j) where TComparer : IStructComparer<T>
{
if (comparer.Compare(items[i], items[j]) > 0)
{
T key = items[i];
items[i] = items[j];
items[j] = key;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InsertionSort_Unchecked<TComparer>(T* items, int length, ref TComparer comparer) where TComparer : IStructComparer<T>
{
for (int i = 0; i < length - 1; i++)
{
T t = items[i + 1];
int j = i;
while (j >= 0 && comparer.Compare(t, items[j]) < 0)
{
items[j + 1] = items[j];
j--;
}
items[j + 1] = t;
}
}
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public static void OptimizedBubbleSort_Unchecked<TComparer>(T* items, int length, ref TComparer comparer) where TComparer : IComparerX<T>
//{
// for (int i = 0, n = length - 1; i < n; i++)
// {
// bool noSwaped = true;
// for (int j = 0; j < n - i;)
// {
// ref T j0 = ref items[j++];
// if(comparer.Compare(j0, items[j]) < 0)
// {
// T tmp = items[j];
// items[j] = j0;
// j0 = tmp;
// noSwaped = false;
// }
// }
// if (noSwaped)
// break;
// }
//}
public static void InsertionSort<TComparer>(T* items, int length, ref TComparer comparer) where TComparer : IStructComparer<T>
{
if (length == 1)
{
return;
}
if (length == 2)
{
SwapIfGreater(items, ref comparer, 0, 1);
return;
}
if (length == 3)
{
SwapIfGreater(items, ref comparer, 0, 1);
SwapIfGreater(items, ref comparer, 0, 2);
SwapIfGreater(items, ref comparer, 1, 2);
return;
}
InsertionSort_Unchecked(items, length, ref comparer);
}
//public static void OptimizedBubbleSort<TComparer>(T* items, int length, ref TComparer comparer) where TComparer : IComparerX<T>
//{
// if (length == 1)
// {
// return;
// }
//
// if (length == 2)
// {
// SwapIfGreater(items, ref comparer, 0, 1);
// return;
// }
//
// if (length == 3)
// {
// SwapIfGreater(items, ref comparer, 0, 1);
// SwapIfGreater(items, ref comparer, 0, 2);
// SwapIfGreater(items, ref comparer, 1, 2);
// return;
// }
//
// OptimizedBubbleSort_Unchecked(items, length, ref comparer);
//}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 7e60048b7c58cdf4ba044b7c832bd6a4
guid: a591de1858028504d819333121bfddd6
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -6,6 +6,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS.Core.Internal
{
@ -269,6 +270,114 @@ namespace DCFApixels.DragonECS.Core.Internal
}
}
internal static unsafe class UnmanagedArrayUtility
{
private static class MetaCache<T>
{
public readonly static int Size;
static MetaCache()
{
T def = default;
Size = Marshal.SizeOf(def);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T* New<T>(int capacity) where T : unmanaged
{
//Console.WriteLine($"{typeof(T).Name} - {Marshal.SizeOf<T>()} - {capacity} - {Marshal.SizeOf<T>() * capacity}");
//return (T*)Marshal.AllocHGlobal(Marshal.SizeOf<T>() * capacity).ToPointer();
return MemoryAllocator.Alloc<T>(capacity).As<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void New<T>(out T* ptr, int capacity) where T : unmanaged
{
//ptr = (T*)Marshal.AllocHGlobal(Marshal.SizeOf<T>() * capacity).ToPointer();
ptr = MemoryAllocator.Alloc<T>(capacity).As<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T* NewAndInit<T>(int capacity) where T : unmanaged
{
//int newSize = MetaCache<T>.Size * capacity;
//byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer();
//
//for (int i = 0; i < newSize; i++)
//{
// *(newPointer + i) = 0;
//}
//
//return (T*)newPointer;
return MemoryAllocator.AllocAndInit<T>(capacity).As<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void NewAndInit<T>(out T* ptr, int capacity) where T : unmanaged
{
//int newSize = MetaCache<T>.Size * capacity;
//byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer();
//
//for (int i = 0; i < newSize; i++)
//{
// *(newPointer + i) = 0;
//}
//
//ptr = (T*)newPointer;
ptr = MemoryAllocator.AllocAndInit<T>(capacity).As<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Free(void* pointer)
{
//Marshal.FreeHGlobal(new IntPtr(pointer));
MemoryAllocator.Free(dataPtr: pointer);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Free<T>(ref T* pointer, ref int length) where T : unmanaged
{
//Marshal.FreeHGlobal(new IntPtr(pointer));
MemoryAllocator.Free(dataPtr: pointer);
pointer = null;
length = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T* Clone<T>(T* sourcePtr, int length) where T : unmanaged
{
T* clone = New<T>(length);
for (int i = 0; i < length; i++)
{
clone[i] = sourcePtr[i];
}
return clone;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T* Resize<T>(void* oldPointer, int newCount) where T : unmanaged
{
//return (T*)(Marshal.ReAllocHGlobal(
// new IntPtr(oldPointer),
// new IntPtr(MetaCache<T>.Size * newCount))).ToPointer();
return MemoryAllocator.Realloc<T>(MemoryAllocator.Handler.FromDataPtr(oldPointer), newCount).As<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T* ResizeAndInit<T>(void* oldPointer, int oldSize, int newSize) where T : unmanaged
{
//int sizeT = MetaCache<T>.Size;
//T* result = (T*)Marshal.ReAllocHGlobal(
// new IntPtr(oldPointer),
// new IntPtr(sizeT * newSize)).ToPointer();
//Init((byte*)result, sizeT * oldSize, sizeT * newSize);
//return result;
return MemoryAllocator.ReallocAndInit<T>(MemoryAllocator.Handler.FromDataPtr(oldPointer), oldSize, newSize).As<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Init(byte* pointer, int startByteIndex, int endByteIndex)
{
for (int i = startByteIndex; i < endByteIndex; i++)
{
*(pointer + i) = 0;
}
}
}
public static class CollectionUtility
{

View File

@ -9,33 +9,6 @@ namespace DCFApixels.DragonECS.Core.Internal
{
internal static class ReflectionUtility
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Type GetPureType(this Type type)
{
if (type.IsGenericType)
{
return type.GetGenericTypeDefinition();
}
return type;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGetAttributeInherited<T>(this Type self, out T attribute, out Type declaringAtrType) where T : Attribute
{
if (self == null || self == typeof(object))
{
attribute = null;
declaringAtrType = null;
return false;
}
attribute = self.GetCustomAttribute<T>();
if (attribute == null)
{
return self.BaseType.TryGetAttributeInherited<T>(out attribute, out declaringAtrType);
}
declaringAtrType = self;
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGetAttribute<T>(this MemberInfo self, out T attribute) where T : Attribute
{

View File

@ -1,392 +0,0 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
#if ENABLE_IL2CPP
using Unity.IL2CPP.CompilerServices;
#endif
namespace DCFApixels.DragonECS.Core.Internal
{
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
internal static unsafe class SortHalper
{
#region OrderBy
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SortBy<T, TKey>(Span<T> span, Func<T, TKey> keySelector)
where TKey : IComparable<TKey>
{
var c = new ComparisonWrapper<T>((a, b) => keySelector(a).CompareTo(keySelector(b)));
SortHalper<T, ComparisonWrapper<T>>.Sort(span, ref c);
}
#endregion
#region Span
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sort<T>(Span<T> span)
{
var c = new ComparerWrapper<T>(Comparer<T>.Default);
SortHalper<T, ComparerWrapper<T>>.Sort(span, ref c);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sort<T>(Span<T> span, bool _ = false)
where T : IComparable<T>
{
var c = new ComparableWrapper<T>();
SortHalper<T, ComparableWrapper<T>>.Sort(span, ref c);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sort<T>(Span<T> span, Comparer<T> comparer)
{
var c = new ComparerWrapper<T>(comparer);
SortHalper<T, ComparerWrapper<T>>.Sort(span, ref c);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sort<T>(Span<T> span, Comparison<T> comparison)
{
var c = new ComparisonWrapper<T>(comparison);
SortHalper<T, ComparisonWrapper<T>>.Sort(span, ref c);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sort<T, TComparer>(Span<T> span, ref TComparer comparer)
where TComparer : struct, IComparer<T>
{
SortHalper<T, TComparer>.Sort(span, ref comparer);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sort<T, TComparer>(Span<T> span, TComparer comparer)
where TComparer : struct, IComparer<T>
{
SortHalper<T, TComparer>.Sort(span, ref comparer);
}
#endregion
#region Utils
// Таблица для De Bruijn-умножения (позиция старшего бита для чисел 0..31)
private const int Log2DeBruijn32_Length = 32;
private static readonly uint* Log2DeBruijn32 = MemoryAllocator.From(new uint[Log2DeBruijn32_Length]
{
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
}).Ptr;
/// <summary>32-битный логарифм по основанию 2 (округление вниз). Для value = 0 возвращает 0.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Log2(uint value)
{
// Для нуля сразу возвращаем 0 (по договорённости, чтобы избежать исключения)
if (value == 0) return 0;
// Заполняем все биты справа от старшего единицей: превращаем число в 2^n - 1
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
// Умножение на константу De Bruijn и сдвиг для получения индекса в таблице
return (int)Log2DeBruijn32[(value * 0x07C4ACDDu) >> 27];
}
/// <summary>64-битный логарифм по основанию 2 (округление вниз). Для value = 0 возвращает 0.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Log2(ulong value)
{
if (value == 0) { return 0; }
uint high = (uint)(value >> 32);
if (high == 0)
{
return Log2((uint)value);
}
else
{
return 32 + Log2(high);
}
}
#endregion
}
// a > b = return > 0
// int Compare(T a, T b);
//IComparer<T> comparer
//if (comparer == null)
//{
// comparer = Comparer<T>.Default;
//}
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
internal static class SortHalper<T, TComparer>
where TComparer : struct, IComparer<T>
{
private const int IntrosortSizeThreshold = 16;
#region Public
public static void Sort(Span<T> keys, ref TComparer comparer)
{
// Add a try block here to detect IComparers (or their
// underlying IComparables, etc) that are bogus.
IntrospectiveSort(keys, ref comparer);
}
public static int BinarySearch(T[] array, int index, int length, T value, ref TComparer comparer)
{
Debug.Assert(array != null, "Check the arguments in the caller!");
Debug.Assert(index >= 0 && length >= 0 && (array.Length - index >= length), "Check the arguments in the caller!");
int lo = index;
int hi = index + length - 1;
while (lo <= hi)
{
int i = lo + ((hi - lo) >> 1);
int order = comparer.Compare(array[i], value);
if (order == 0) return i;
if (order < 0)
{
lo = i + 1;
}
else
{
hi = i - 1;
}
}
return ~lo;
}
#endregion
#region Internal
internal static void IntrospectiveSort(Span<T> keys, ref TComparer comparer)
{
//Debug.Assert(comparer != null);
if (keys.Length > 1)
{
IntroSort(keys, 2 * (SortHalper.Log2((uint)keys.Length) + 1), ref comparer);
}
}
// IntroSort is recursive; block it from being inlined into itself as
// this is currenly not profitable.
[MethodImpl(MethodImplOptions.NoInlining)]
private static void IntroSort(Span<T> keys, int depthLimit, ref TComparer comparer)
{
Debug.Assert(!keys.IsEmpty);
Debug.Assert(depthLimit >= 0);
//Debug.Assert(comparer != null);
int partitionSize = keys.Length;
while (partitionSize > 1)
{
if (partitionSize <= IntrosortSizeThreshold)
{
if (partitionSize == 2)
{
SwapIfGreater(keys, ref comparer, 0, 1);
return;
}
if (partitionSize == 3)
{
SwapIfGreater(keys, ref comparer, 0, 1);
SwapIfGreater(keys, ref comparer, 0, 2);
SwapIfGreater(keys, ref comparer, 1, 2);
return;
}
InsertionSort(keys.Slice(0, partitionSize), ref comparer);
return;
}
if (depthLimit == 0)
{
HeapSort(keys.Slice(0, partitionSize), ref comparer);
return;
}
depthLimit--;
int p = PickPivotAndPartition(keys.Slice(0, partitionSize), ref comparer);
// Note we've already partitioned around the pivot and do not have to move the pivot again.
IntroSort(keys.Slice(p + 1, partitionSize - (p + 1)), depthLimit, ref comparer);
partitionSize = p;
}
}
private static int PickPivotAndPartition(Span<T> keys, ref TComparer comparer)
{
Debug.Assert(keys.Length >= IntrosortSizeThreshold);
//Debug.Assert(comparer != null);
int hi = keys.Length - 1;
// Compute median-of-three. But also partition them, since we've done the comparison.
int middle = hi >> 1;
// Sort lo, mid and hi appropriately, then pick mid as the pivot.
SwapIfGreater(keys, ref comparer, 0, middle); // swap the low with the mid point
SwapIfGreater(keys, ref comparer, 0, hi); // swap the low with the high
SwapIfGreater(keys, ref comparer, middle, hi); // swap the middle with the high
T pivot = keys[middle];
Swap(keys, middle, hi - 1);
int left = 0, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
while (left < right)
{
while (comparer.Compare(keys[++left], pivot) < 0) ;
while (comparer.Compare(pivot, keys[--right]) < 0) ;
if (left >= right)
break;
Swap(keys, left, right);
}
// Put pivot in the right location.
if (left != hi - 1)
{
Swap(keys, left, hi - 1);
}
return left;
}
private static void HeapSort(Span<T> keys, ref TComparer comparer)
{
Debug.Assert(!keys.IsEmpty);
//Debug.Assert(comparer != null);
int n = keys.Length;
for (int i = n >> 1; i >= 1; i--)
{
DownHeap(keys, i, n, ref comparer);
}
for (int i = n; i > 1; i--)
{
Swap(keys, 0, i - 1);
DownHeap(keys, 1, i - 1, ref comparer);
}
}
private static void DownHeap(Span<T> keys, int i, int n, ref TComparer comparer)
{
//Debug.Assert(comparer != null);
T d = keys[i - 1];
while (i <= n >> 1)
{
int child = 2 * i;
if (child < n && comparer.Compare(keys[child - 1], keys[child]) < 0)
{
child++;
}
if (!(comparer.Compare(d, keys[child - 1]) < 0))
break;
keys[i - 1] = keys[child - 1];
i = child;
}
keys[i - 1] = d;
}
private static void InsertionSort(Span<T> keys, ref TComparer comparer)
{
for (int i = 0; i < keys.Length - 1; i++)
{
T t = keys[i + 1];
int j = i;
while (j >= 0 && comparer.Compare(t, keys[j]) < 0)
{
keys[j + 1] = keys[j];
j--;
}
keys[j + 1] = t;
}
}
#endregion
#region Swaps
private static void SwapIfGreater(Span<T> keys, ref TComparer comparer, int i, int j)
{
Debug.Assert(i != j);
if (comparer.Compare(keys[i], keys[j]) > 0)
{
T key = keys[i];
keys[i] = keys[j];
keys[j] = key;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap(Span<T> a, int i, int j)
{
Debug.Assert(i != j);
T t = a[i];
a[i] = a[j];
a[j] = t;
}
#endregion
}
#region Wrappers
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
internal readonly struct ComparableWrapper<T> : IComparer<T>
where T : IComparable<T>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Compare(T x, T y) { return x.CompareTo(y); }
}
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
internal readonly struct ComparisonWrapper<T> : IComparer<T>
{
public readonly Comparison<T> Comparison;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ComparisonWrapper(Comparison<T> comparison)
{
Comparison = comparison;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Compare(T x, T y)
{
return Comparison(x, y);
}
}
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
internal readonly struct ComparerWrapper<T> : IComparer<T>
{
public readonly Comparer<T> Comparer;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ComparerWrapper(Comparer<T> comparer)
{
Comparer = comparer;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Compare(T x, T y)
{
return Comparer.Compare(x, y);
}
}
#endregion
}

View File

@ -38,13 +38,13 @@ namespace DCFApixels.DragonECS.Core.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UnsafeArray(int length)
{
ptr = MemoryAllocator.Alloc<T>(length).Ptr;
UnmanagedArrayUtility.New(out ptr, length);
Length = length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UnsafeArray(int length, bool isInit)
{
ptr = MemoryAllocator.AllocAndInit<T>(length).Ptr;
UnmanagedArrayUtility.NewAndInit(out ptr, length);
Length = length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -90,20 +90,18 @@ namespace DCFApixels.DragonECS.Core.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UnsafeArray<T> Clone()
{
return new UnsafeArray<T>(MemoryAllocator.From(ptr, Length).Ptr, Length);
return new UnsafeArray<T>(UnmanagedArrayUtility.Clone(ptr, Length), Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
MemoryAllocator.Free(ptr);
ptr = default;
Length = default;
UnmanagedArrayUtility.Free(ref ptr, ref Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadonlyDispose()
{
MemoryAllocator.Free(ptr);
UnmanagedArrayUtility.Free(ptr);
}
public override string ToString()
{
@ -116,9 +114,6 @@ namespace DCFApixels.DragonECS.Core.Internal
return CollectionUtility.AutoToString(elements, "ua");
}
public Span<T> AsSpan() { return new Span<T>(ptr, Length); }
public T[] ToArray() { return AsSpan().ToArray(); }
IEnumerator<T> IEnumerable<T>.GetEnumerator() { return GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@ -3,6 +3,7 @@
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.PoolsCore;
using System;
using System.Collections;
using System.Collections.Generic;
@ -25,7 +26,6 @@ namespace DCFApixels.DragonECS
/// <summary>Pool for IEcsComponent components</summary>
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
[MetaColor(MetaColor.DragonRose)]
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)]
@ -35,8 +35,7 @@ namespace DCFApixels.DragonECS
public sealed class EcsPool<T> : IEcsPoolImplementation<T>, IEcsStructPool<T>, IEnumerable<T> //IEnumerable<T> - IntelliSense hack
where T : struct, IEcsComponent
{
private short _worldID;
private EcsWorld _world;
private EcsWorld _source;
private int _componentTypeID;
private EcsMaskChunck _maskBit;
@ -46,14 +45,14 @@ namespace DCFApixels.DragonECS
private int[] _recycledItems;
private int _recycledItemsCount = 0;
private readonly IEcsComponentLifecycle<T> _customLifecycle = EcsComponentLifecycle<T>.CustomHandler;
private readonly bool _isCustomLifecycle = EcsComponentLifecycle<T>.IsCustom;
private readonly IEcsComponentCopy<T> _customCopy = EcsComponentCopy<T>.CustomHandler;
private readonly bool _isCustomCopy = EcsComponentCopy<T>.IsCustom;
private readonly IEcsComponentLifecycle<T> _componentLifecycleHandler = EcsComponentLifecycleHandler<T>.instance;
private readonly bool _isHasComponentLifecycleHandler = EcsComponentLifecycleHandler<T>.isHasHandler;
private readonly IEcsComponentCopy<T> _componentCopyHandler = EcsComponentCopyHandler<T>.instance;
private readonly bool _isHasComponentCopyHandler = EcsComponentCopyHandler<T>.isHasHandler;
#if !DRAGONECS_DISABLE_POOLS_EVENTS
private StructList<IEcsPoolEventListener> _listeners = new StructList<IEcsPoolEventListener>(2);
private bool _hasAnyListener = false;
private int _listenersCachedCount = 0;
#endif
private bool _isLocked;
@ -78,7 +77,7 @@ namespace DCFApixels.DragonECS
}
public EcsWorld World
{
get { return _world; }
get { return _source; }
}
public bool IsReadOnly
{
@ -105,8 +104,7 @@ namespace DCFApixels.DragonECS
}
void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID)
{
_world = world;
_worldID = world.ID;
_source = world;
_mediator = mediator;
_componentTypeID = componentTypeID;
_maskBit = EcsMaskChunck.FromID(componentTypeID);
@ -130,13 +128,13 @@ namespace DCFApixels.DragonECS
{
ref int itemIndex = ref _mapping[entityID];
#if DEBUG
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
if (_world.IsUsed(entityID) == false) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_source, entityID); }
if (_source.IsUsed(entityID) == false) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_source, entityID); }
if (itemIndex > 0) { EcsPoolThrowHelper.ThrowAlreadyHasComponent<T>(entityID); }
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
#elif DRAGONECS_STABILITY_MODE
if (itemIndex > 0) { return ref Get(entityID); }
if (_isLocked | _world.IsUsed(entityID) == false) { return ref _items[0]; }
if (_isLocked | _source.IsUsed(entityID) == false) { return ref _items[0]; }
#endif
if (_recycledItemsCount > 0)
{
@ -153,9 +151,9 @@ namespace DCFApixels.DragonECS
}
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
ref T result = ref _items[itemIndex];
InvokeOnAdd(entityID, ref _items[itemIndex]);
EnableComponent(ref result);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnAddAndGet(entityID); }
_listeners.InvokeOnAddAndGet(entityID, _listenersCachedCount);
#endif
return ref result;
}
@ -166,7 +164,7 @@ namespace DCFApixels.DragonECS
if (!Has(entityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
#endif
#if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnGet(entityID); }
_listeners.InvokeOnGet(entityID, _listenersCachedCount);
#endif
return ref _items[_mapping[entityID]];
}
@ -181,7 +179,7 @@ namespace DCFApixels.DragonECS
public ref T TryAddOrGet(int entityID)
{
#if DEBUG
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_source, entityID); }
#endif
ref int itemIndex = ref _mapping[entityID];
if (itemIndex <= 0)
@ -205,13 +203,13 @@ namespace DCFApixels.DragonECS
}
}
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
InvokeOnAdd(entityID, ref _items[itemIndex]);
EnableComponent(ref _items[itemIndex]);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnAdd(entityID); }
_listeners.InvokeOnAdd(entityID, _listenersCachedCount);
#endif
} //Add block end
#if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnGet(entityID); }
_listeners.InvokeOnGet(entityID, _listenersCachedCount);
#endif
return ref _items[itemIndex];
}
@ -224,14 +222,14 @@ namespace DCFApixels.DragonECS
{
ref int itemIndex = ref _mapping[entityID];
#if DEBUG
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_source, entityID); }
if (itemIndex <= 0) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
#elif DRAGONECS_STABILITY_MODE
if (itemIndex <= 0) { return; }
if (_isLocked) { return; }
#endif
InvokeOnDel(entityID, ref _items[itemIndex]);
DisableComponent(ref _items[itemIndex]);
if (_recycledItemsCount >= _recycledItems.Length)
{
Array.Resize(ref _recycledItems, ArrayUtility.NextPow2Safe(_recycledItemsCount));
@ -241,7 +239,7 @@ namespace DCFApixels.DragonECS
_itemsCount--;
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); }
_listeners.InvokeOnDel(entityID, _listenersCachedCount);
#endif
}
public void TryDel(int entityID)
@ -258,7 +256,7 @@ namespace DCFApixels.DragonECS
#elif DRAGONECS_STABILITY_MODE
if (!Has(fromEntityID)) { return; }
#endif
EcsComponentCopy<T>.Copy(_isCustomCopy, _customCopy, ref Get(fromEntityID), ref TryAddOrGet(toEntityID));
CopyComponent(ref Get(fromEntityID), ref TryAddOrGet(toEntityID));
}
public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID)
{
@ -267,7 +265,7 @@ namespace DCFApixels.DragonECS
#elif DRAGONECS_STABILITY_MODE
if (!Has(fromEntityID)) { return; }
#endif
EcsComponentCopy<T>.Copy(_isCustomCopy, _customCopy, ref Get(fromEntityID), ref toWorld.GetPool<T>().TryAddOrGet(toEntityID));
CopyComponent(ref Get(fromEntityID), ref toWorld.GetPool<T>().TryAddOrGet(toEntityID));
}
public void ClearAll()
@ -279,15 +277,15 @@ namespace DCFApixels.DragonECS
#endif
_recycledItemsCount = 0; // ñïåðåäè ÷òîáû îáíóëÿëîñü, òàê êàê Del íå îáíóëÿåò
if (_itemsCount <= 0) { return; }
var span = _world.Where(out SingleAspect<T> _);
var span = _source.Where(out SingleAspect<T> _);
foreach (var entityID in span)
{
ref int itemIndex = ref _mapping[entityID];
InvokeOnDel(entityID, ref _items[itemIndex]);
DisableComponent(ref _items[itemIndex]);
itemIndex = 0;
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); }
_listeners.InvokeOnDel(entityID, _listenersCachedCount);
#endif
}
_itemsCount = 0;
@ -316,26 +314,6 @@ namespace DCFApixels.DragonECS
#endregion
#region Other
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InvokeOnAdd(int entityID, ref T component)
{
if (_isCustomLifecycle)
{
_customLifecycle.OnAdd(ref component, _worldID, entityID);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InvokeOnDel(int entityID, ref T component)
{
if (_isCustomLifecycle)
{
_customLifecycle.OnDel(ref component, _worldID, entityID);
}
else
{
component = default;
}
}
void IEcsPool.AddEmpty(int entityID) { Add(entityID); }
void IEcsPool.AddRaw(int entityID, object dataRaw)
{
@ -354,19 +332,58 @@ namespace DCFApixels.DragonECS
{
if (listener == null) { EcsPoolThrowHelper.ThrowNullListener(); }
_listeners.Add(listener);
_hasAnyListener = _listeners.Count > 0;
_listenersCachedCount++;
}
public void RemoveListener(IEcsPoolEventListener listener)
{
if (listener == null) { EcsPoolThrowHelper.ThrowNullListener(); }
if (_listeners.RemoveWithOrder(listener))
{
_hasAnyListener = _listeners.Count > 0;
_listenersCachedCount--;
}
}
#endif
#endregion
#region Enable/Disable/Copy
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnableComponent(ref T component)
{
if (_isHasComponentLifecycleHandler)
{
_componentLifecycleHandler.Enable(ref component);
}
else
{
component = default;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DisableComponent(ref T component)
{
if (_isHasComponentLifecycleHandler)
{
_componentLifecycleHandler.Disable(ref component);
}
else
{
component = default;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CopyComponent(ref T from, ref T to)
{
if (_isHasComponentCopyHandler)
{
_componentCopyHandler.Copy(ref from, ref to);
}
else
{
to = from;
}
}
#endregion
#region IEnumerator - IntelliSense hack
IEnumerator<T> IEnumerable<T>.GetEnumerator() { throw new NotImplementedException(); }
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
@ -389,20 +406,12 @@ namespace DCFApixels.DragonECS
{
pool.TryAddOrGet(entityID) = component;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Apply(short worldID, int entityID)
{
return ref EcsWorld.GetPoolInstance<EcsPool<T>>(worldID).TryAddOrGet(entityID);
}
#endregion
}
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
#endif
[MetaTags(MetaTags.HIDDEN)]
[MetaColor(MetaColor.DragonRose)]
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.OTHER_GROUP)]
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly struct ReadonlyEcsPool<T> : IEcsReadonlyPool //IEnumerable<T> - IntelliSense hack
where T : struct, IEcsComponent
@ -475,6 +484,7 @@ namespace DCFApixels.DragonECS
#endregion
}
public static class EcsPoolExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -508,5 +518,29 @@ namespace DCFApixels.DragonECS
{
return self.AnyPool<EcsPool<TComponent>>();
}
#region Obsolete
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Inc) + "<T>()")]
[EditorBrowsable(EditorBrowsableState.Never)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsPool<TComponent> Include<TComponent>(this EcsAspect.Builder self) where TComponent : struct, IEcsComponent
{
return self.IncludePool<EcsPool<TComponent>>();
}
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Exc) + "<T>()")]
[EditorBrowsable(EditorBrowsableState.Never)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsPool<TComponent> Exclude<TComponent>(this EcsAspect.Builder self) where TComponent : struct, IEcsComponent
{
return self.ExcludePool<EcsPool<TComponent>>();
}
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Opt) + "<T>()")]
[EditorBrowsable(EditorBrowsableState.Never)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsPool<TComponent> Optional<TComponent>(this EcsAspect.Builder self) where TComponent : struct, IEcsComponent
{
return self.OptionalPool<EcsPool<TComponent>>();
}
#endregion
}
}
}

View File

@ -1,19 +1,14 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
#if !DRAGONECS_DISABLE_POOLS_EVENTS
#define DRAGONECS_ENABLE_POOLS_EVENTS
#else
#undef DRAGONECS_ENABLE_POOLS_EVENTS
#endif
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.PoolsCore;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS.Core
namespace DCFApixels.DragonECS.PoolsCore
{
/// <summary> Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool<T>. </summary>
public interface IEcsPoolImplementation : IEcsPool
@ -67,12 +62,7 @@ namespace DCFApixels.DragonECS.Core
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowNullListener()
{
throw new ArgumentNullException("Listener is null");
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowNullComponent()
{
throw new ArgumentNullException("Component is null");
throw new ArgumentNullException("listener is null");
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowPoolLocked()
@ -105,7 +95,7 @@ namespace DCFApixels.DragonECS.Core.Internal
#if DEBUG
throw new NullInstanceException();
#else
return EcsWorld.GetWorld(0);
return EcsWorld.GetWorld(0);
#endif
}
}
@ -296,49 +286,72 @@ namespace DCFApixels.DragonECS
/// <summary>Called after deleting an entity from the pool</summary>
void OnDel(int entityID);
}
#if !DRAGONECS_DISABLE_POOLS_EVENTS
public static class PoolEventListExtensions
{
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnAdd(this List<IEcsPoolEventListener> self, int entityID)
{
for (int i = 0; i < self.Count; i++) { self[i].OnAdd(entityID); }
self.InvokeOnAdd(entityID, self.Count);
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnAdd(this List<IEcsPoolEventListener> self, int entityID, int cachedCount)
{
for (int i = 0; i < cachedCount; i++) { self[i].OnAdd(entityID); }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnAddAndGet(this List<IEcsPoolEventListener> self, int entityID)
{
for (int i = 0; i < self.Count; i++)
self.InvokeOnAddAndGet(entityID, self.Count);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnAddAndGet(this List<IEcsPoolEventListener> self, int entityID, int cachedCount)
{
for (int i = 0; i < cachedCount; i++)
{
self[i].OnAdd(entityID);
self[i].OnGet(entityID);
}
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnGet(this List<IEcsPoolEventListener> self, int entityID)
{
for (int i = 1; i < self.Count; i++) { self[i].OnGet(entityID); }
self.InvokeOnGet(entityID, self.Count);
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnGet(this List<IEcsPoolEventListener> self, int entityID, int cachedCount)
{
for (int i = 1; i < cachedCount; i++) { self[i].OnGet(entityID); }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnDel(this List<IEcsPoolEventListener> self, int entityID)
{
for (int i = 0; i < self.Count; i++) { self[i].OnDel(entityID); }
self.InvokeOnDel(entityID, self.Count);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnDel(this List<IEcsPoolEventListener> self, int entityID, int cachedCount)
{
for (int i = 0; i < cachedCount; i++) { self[i].OnDel(entityID); }
}
//
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InvokeOnAdd(this StructList<IEcsPoolEventListener> self, int entityID)
internal static void InvokeOnAdd(ref this StructList<IEcsPoolEventListener> self, int entityID)
{
for (int i = 0; i < self.Count; i++) { self[i].OnAdd(entityID); }
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InvokeOnAddAndGet(this StructList<IEcsPoolEventListener> self, int entityID)
internal static void InvokeOnAdd(ref this StructList<IEcsPoolEventListener> self, int entityID, int cachedCount)
{
for (int i = 0; i < cachedCount; i++) { self[i].OnAdd(entityID); }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InvokeOnAddAndGet(ref this StructList<IEcsPoolEventListener> self, int entityID)
{
for (int i = 0; i < self.Count; i++)
{
@ -346,18 +359,36 @@ namespace DCFApixels.DragonECS
self[i].OnGet(entityID);
}
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InvokeOnGet(this StructList<IEcsPoolEventListener> self, int entityID)
internal static void InvokeOnAddAndGet(ref this StructList<IEcsPoolEventListener> self, int entityID, int cachedCount)
{
for (int i = 0; i < self.Count; i++) { self[i].OnGet(entityID); }
for (int i = 0; i < cachedCount; i++)
{
self[i].OnAdd(entityID);
self[i].OnGet(entityID);
}
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InvokeOnDel(this StructList<IEcsPoolEventListener> self, int entityID)
internal static void InvokeOnGet(ref this StructList<IEcsPoolEventListener> self, int entityID)
{
for (int i = 1; i < self.Count; i++) { self[i].OnGet(entityID); }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InvokeOnGet(ref this StructList<IEcsPoolEventListener> self, int entityID, int cachedCount)
{
for (int i = 1; i < cachedCount; i++) { self[i].OnGet(entityID); }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InvokeOnDel(ref this StructList<IEcsPoolEventListener> self, int entityID)
{
for (int i = 0; i < self.Count; i++) { self[i].OnDel(entityID); }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InvokeOnDel(ref this StructList<IEcsPoolEventListener> self, int entityID, int cachedCount)
{
for (int i = 0; i < cachedCount; i++) { self[i].OnDel(entityID); }
}
}
#endif
#endregion
}
}

View File

@ -3,6 +3,7 @@
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.PoolsCore;
using System;
using System.Collections;
using System.Collections.Generic;
@ -25,7 +26,6 @@ namespace DCFApixels.DragonECS
/// <summary> Pool for IEcsTagComponent components. </summary>
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
[MetaColor(MetaColor.DragonRose)]
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)]
@ -35,7 +35,7 @@ namespace DCFApixels.DragonECS
public sealed class EcsTagPool<T> : IEcsPoolImplementation<T>, IEcsStructPool<T>, IEnumerable<T> //IEnumerable<T> - IntelliSense hack
where T : struct, IEcsTagComponent
{
private EcsWorld _world;
private EcsWorld _source;
private int _componentTypeID;
private EcsMaskChunck _maskBit;
@ -44,7 +44,7 @@ namespace DCFApixels.DragonECS
#if !DRAGONECS_DISABLE_POOLS_EVENTS
private StructList<IEcsPoolEventListener> _listeners = new StructList<IEcsPoolEventListener>(2);
private bool _hasAnyListener = false;
private int _listenersCachedCount = 0;
#endif
private bool _isLocked;
@ -78,7 +78,7 @@ namespace DCFApixels.DragonECS
}
public EcsWorld World
{
get { return _world; }
get { return _source; }
}
public bool IsReadOnly
{
@ -102,7 +102,7 @@ namespace DCFApixels.DragonECS
}
void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID)
{
_world = world;
_source = world;
_mediator = mediator;
_componentTypeID = componentTypeID;
_maskBit = EcsMaskChunck.FromID(componentTypeID);
@ -116,18 +116,17 @@ namespace DCFApixels.DragonECS
public void Add(int entityID)
{
#if DEBUG
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
if (_world.IsUsed(entityID) == false) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
if (_source.IsUsed(entityID) == false) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_source, entityID); }
if (Has(entityID)) { EcsPoolThrowHelper.ThrowAlreadyHasComponent<T>(entityID); }
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
#elif DRAGONECS_STABILITY_MODE
if (Has(entityID) | _world.IsUsed(entityID) == false | _isLocked) { return; }
if (Has(entityID) | _source.IsUsed(entityID) == false | _isLocked) { return; }
#endif
_count++;
_mapping[entityID] = true;
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnAdd(entityID); }
_listeners.InvokeOnAdd(entityID, _listenersCachedCount);
#endif
}
public void TryAdd(int entityID)
@ -145,7 +144,6 @@ namespace DCFApixels.DragonECS
public void Del(int entityID)
{
#if DEBUG
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
if (!Has(entityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
#elif DRAGONECS_STABILITY_MODE
@ -155,7 +153,7 @@ namespace DCFApixels.DragonECS
_count--;
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); }
_listeners.InvokeOnDel(entityID, _listenersCachedCount);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -218,14 +216,14 @@ namespace DCFApixels.DragonECS
if (_isLocked) { return; }
#endif
if (_count <= 0) { return; }
var span = _world.Where(out SingleTagAspect<T> _);
var span = _source.Where(out SingleTagAspect<T> _);
_count = 0;
foreach (var entityID in span)
{
_mapping[entityID] = false;
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); }
_listeners.InvokeOnDel(entityID, _listenersCachedCount);
#endif
}
}
@ -295,14 +293,14 @@ namespace DCFApixels.DragonECS
{
if (listener == null) { EcsPoolThrowHelper.ThrowNullListener(); }
_listeners.Add(listener);
_hasAnyListener = _listeners.Count > 0;
_listenersCachedCount++;
}
public void RemoveListener(IEcsPoolEventListener listener)
{
if (listener == null) { EcsPoolThrowHelper.ThrowNullListener(); }
if (_listeners.RemoveWithOrder(listener))
{
_hasAnyListener = _listeners.Count > 0;
_listenersCachedCount--;
}
}
#endif
@ -330,22 +328,12 @@ namespace DCFApixels.DragonECS
{
pool.TryAdd(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Apply(short worldID, int entityID)
{
var pool = EcsWorld.GetPoolInstance<EcsTagPool<T>>(worldID);
pool.TryAdd(entityID);
return ref pool._fakeComponent;
}
#endregion
}
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
#endif
[MetaTags(MetaTags.HIDDEN)]
[MetaColor(MetaColor.DragonRose)]
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.OTHER_GROUP)]
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly struct ReadonlyEcsTagPool<T> : IEcsReadonlyPool //IEnumerable<T> - IntelliSense hack
where T : struct, IEcsTagComponent
@ -419,6 +407,7 @@ namespace DCFApixels.DragonECS
public static implicit operator ReadonlyEcsTagPool<T>(EcsWorld.GetPoolInstanceMarker a) { return a.GetInstance<EcsTagPool<T>>(); }
#endregion
}
public static class EcsTagPoolExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -452,5 +441,68 @@ namespace DCFApixels.DragonECS
{
return self.OptionalPool<EcsTagPool<TTagComponent>>();
}
#region Obsolete
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Inc) + "<T>()")]
[EditorBrowsable(EditorBrowsableState.Never)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsTagPool<TTagComponent> Include<TTagComponent>(this EcsAspect.Builder self) where TTagComponent : struct, IEcsTagComponent
{
return self.IncludePool<EcsTagPool<TTagComponent>>();
}
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Exc) + "<T>()")]
[EditorBrowsable(EditorBrowsableState.Never)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsTagPool<TTagComponent> Exclude<TTagComponent>(this EcsAspect.Builder self) where TTagComponent : struct, IEcsTagComponent
{
return self.ExcludePool<EcsTagPool<TTagComponent>>();
}
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Opt) + "<T>()")]
[EditorBrowsable(EditorBrowsableState.Never)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsTagPool<TTagComponent> Optional<TTagComponent>(this EcsAspect.Builder self) where TTagComponent : struct, IEcsTagComponent
{
return self.OptionalPool<EcsTagPool<TTagComponent>>();
}
//---------------------------------------------------
[Obsolete("Use " + nameof(EcsWorld) + "." + nameof(GetPool) + "<T>()")]
[EditorBrowsable(EditorBrowsableState.Never)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsTagPool<TTagComponent> GetTagPool<TTagComponent>(this EcsWorld self) where TTagComponent : struct, IEcsTagComponent
{
return self.GetPoolInstance<EcsTagPool<TTagComponent>>();
}
[Obsolete("Use " + nameof(EcsWorld) + "." + nameof(GetPoolUnchecked) + "<T>()")]
[EditorBrowsable(EditorBrowsableState.Never)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsTagPool<TTagComponent> GetTagPoolUnchecked<TTagComponent>(this EcsWorld self) where TTagComponent : struct, IEcsTagComponent
{
return self.GetPoolInstanceUnchecked<EcsTagPool<TTagComponent>>();
}
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Inc) + "<T>()")]
[EditorBrowsable(EditorBrowsableState.Never)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsTagPool<TTagComponent> IncludeTag<TTagComponent>(this EcsAspect.Builder self) where TTagComponent : struct, IEcsTagComponent
{
return self.IncludePool<EcsTagPool<TTagComponent>>();
}
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Exc) + "<T>()")]
[EditorBrowsable(EditorBrowsableState.Never)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsTagPool<TTagComponent> ExcludeTag<TTagComponent>(this EcsAspect.Builder self) where TTagComponent : struct, IEcsTagComponent
{
return self.ExcludePool<EcsTagPool<TTagComponent>>();
}
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Opt) + "<T>()")]
[EditorBrowsable(EditorBrowsableState.Never)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsTagPool<TTagComponent> OptionalTag<TTagComponent>(this EcsAspect.Builder self) where TTagComponent : struct, IEcsTagComponent
{
return self.OptionalPool<EcsTagPool<TTagComponent>>();
}
#endregion
}
}
}

View File

@ -1,560 +0,0 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using static DCFApixels.DragonECS.Core.Internal.MemoryAllocator;
#if ENABLE_IL2CPP
using Unity.IL2CPP.CompilerServices;
#endif
namespace DCFApixels.DragonECS
{
/// <summary>Value type component</summary>
[MetaColor(MetaColor.DragonRose)]
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)]
[MetaDescription(EcsConsts.AUTHOR, "Value type component.")]
[MetaID("DragonECS_B053D6FA9C01208AFD1922E6A1D57D83")]
public interface IEcsValueComponent : IEcsComponentMember { }
/// <summary>Pool for IEcsValueComponent components</summary>
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
[MetaColor(MetaColor.DragonRose)]
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)]
[MetaDescription(EcsConsts.AUTHOR, "Pool for IEcsValueComponent components.")]
[MetaID("DragonECS_5097D6FA9C0109349197EEAC3A0D2858")]
[DebuggerDisplay("Count: {Count} Type: {ComponentType}")]
public sealed unsafe class EcsValuePool<T> : IEcsPoolImplementation<T>, IEcsStructPool<T>, IEnumerable<T> //IEnumerable<T> - IntelliSense hack
where T : unmanaged, IEcsValueComponent
{
private EcsWorld _world;
private short _worldID;
private int _componentTypeID;
private EcsMaskChunck _maskBit;
public int* _mapping; // index = entityID / value = itemIndex;/ value = 0 = no entityID.
public T* _items; // dense; _items[0] - fake component.
public int _itemsLength;
public int _itemsCount;
public int* _recycledItems;
public int _recycledItemsLength;
public int _recycledItemsCount;
private readonly EcsValuePoolSharedStore* _sharedStore;
private HMem<int> _mappingHandler;
private HMem<T> _itemsHandler;
private HMem<int> _recycledItemsHandler;
private readonly IEcsComponentLifecycle<T> _customLifecycle = EcsComponentLifecycle<T>.CustomHandler;
private readonly bool _isCustomLifecycle = EcsComponentLifecycle<T>.IsCustom;
private readonly IEcsComponentCopy<T> _customCopy = EcsComponentCopy<T>.CustomHandler;
private readonly bool _isCustomCopy = EcsComponentCopy<T>.IsCustom;
private bool _isLocked;
private EcsWorld.PoolsMediator _mediator;
#region Properites
public int Count
{
get { return _itemsCount; }
}
public int Capacity
{
get { return _itemsLength; }
}
public int ComponentTypeID
{
get { return _componentTypeID; }
}
public Type ComponentType
{
get { return typeof(T); }
}
public EcsWorld World
{
get { return _world; }
}
public bool IsReadOnly
{
get { return false; }
}
public ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return ref Get(index); }
}
#endregion
#region Constructors/Init/Destroy
public EcsValuePool()
{
if (_sharedStore == null)
{
_sharedStore = Alloc<EcsValuePoolSharedStore>(1).Ptr;
}
}
public EcsValuePool(int capacity, int recycledCapacity = -1)
{
if (_sharedStore == null)
{
_sharedStore = Alloc<EcsValuePoolSharedStore>(1).Ptr;
}
capacity = ArrayUtility.NextPow2(capacity);
if (recycledCapacity < 0)
{
recycledCapacity = capacity / 2;
}
_itemsLength = capacity;
_itemsHandler = Alloc<T>(_itemsLength);
_items = _itemsHandler.Ptr;
_sharedStore->_items = _items;
_recycledItemsLength = recycledCapacity;
_recycledItemsHandler = Alloc<int>(_recycledItemsLength);
_recycledItems = _recycledItemsHandler.Ptr;
}
void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID)
{
_world = world;
_worldID = world.ID;
_mediator = mediator;
_componentTypeID = componentTypeID;
_maskBit = EcsMaskChunck.FromID(componentTypeID);
_sharedStore->_worldID = _worldID;
_sharedStore->_componentTypeID = _componentTypeID;
_mappingHandler = AllocAndInit<int>(world.Capacity);
_mapping = _mappingHandler.Ptr;
_sharedStore->_mapping = _mapping;
var worldConfig = world.Configs.GetWorldConfigOrDefault();
if (_items == null)
{
_itemsLength = ArrayUtility.NextPow2(worldConfig.PoolComponentsCapacity);
_itemsHandler = Alloc<T>(_itemsLength);
_items = _itemsHandler.Ptr;
_sharedStore->_items = _items;
}
if (_recycledItems == null)
{
_recycledItemsLength = worldConfig.PoolRecycledComponentsCapacity;
_recycledItemsHandler = Alloc<int>(_recycledItemsLength);
_recycledItems = _recycledItemsHandler.Ptr;
}
}
void IEcsPoolImplementation.OnWorldDestroy() { }
~EcsValuePool()
{
Free(_mappingHandler);
Free(_itemsHandler);
Free(_recycledItemsHandler);
Free(_sharedStore);
}
#endregion
#region Methods
public NativeEcsValuePool<T> AsNative()
{
return new NativeEcsValuePool<T>(_sharedStore);
}
public ref T Add(int entityID)
{
ref int itemIndex = ref _mapping[entityID];
#if DEBUG
if (entityID == EcsConsts.NULL_ENTITY_ID) { Throw.Ent_ThrowIsNotAlive(_world, entityID); }
if (_world.IsUsed(entityID) == false) { Throw.Ent_ThrowIsNotAlive(_world, entityID); }
if (itemIndex > 0) { EcsPoolThrowHelper.ThrowAlreadyHasComponent<T>(entityID); }
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
#elif DRAGONECS_STABILITY_MODE
if (itemIndex > 0) { return ref Get(entityID); }
if (_isLocked | _world.IsUsed(entityID) == false) { return ref _items[0]; }
#endif
if (_recycledItemsCount > 0)
{
itemIndex = _recycledItems[--_recycledItemsCount];
_itemsCount++;
}
else
{
itemIndex = ++_itemsCount;
if (itemIndex >= _itemsLength)
{
_itemsLength = ArrayUtility.NextPow2(_itemsLength << 1);
_itemsHandler = Realloc(_itemsHandler, _itemsLength);
_items = _itemsHandler.Ptr;
_sharedStore->_items = _items;
}
}
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
ref T result = ref _items[itemIndex];
EcsComponentLifecycle<T>.OnAdd(_isCustomLifecycle, _customLifecycle, ref result, _worldID, entityID);
return ref result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Get(int entityID)
{
#if DEBUG // не нужен STAB_MODE
if (!Has(entityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
#endif
return ref _items[_mapping[entityID]];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entityID)
{
#if DEBUG // не нужен STAB_MODE
if (!Has(entityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
#endif
return ref _items[_mapping[entityID]];
}
public ref T TryAddOrGet(int entityID)
{
#if DEBUG
if (entityID == EcsConsts.NULL_ENTITY_ID) { Throw.Ent_ThrowIsNotAlive(_world, entityID); }
#endif
ref int itemIndex = ref _mapping[entityID];
if (itemIndex <= 0)
{ //Add block
#if DEBUG
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
#elif DRAGONECS_STABILITY_MODE
if (_isLocked) { return ref _items[0]; }
#endif
if (_recycledItemsCount > 0)
{
itemIndex = _recycledItems[--_recycledItemsCount];
_itemsCount++;
}
else
{
itemIndex = ++_itemsCount;
if (itemIndex >= _itemsLength)
{
_itemsLength = ArrayUtility.NextPow2(_itemsLength << 1);
_itemsHandler = Realloc(_itemsHandler, _itemsLength);
_items = _itemsHandler.Ptr;
_sharedStore->_items = _items;
}
}
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
EcsComponentLifecycle<T>.OnAdd(_isCustomLifecycle, _customLifecycle, ref _items[itemIndex], _worldID, entityID);
} //Add block end
return ref _items[itemIndex];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(int entityID)
{
return _mapping[entityID] != 0;
}
public void Del(int entityID)
{
ref int itemIndex = ref _mapping[entityID];
#if DEBUG
if (entityID == EcsConsts.NULL_ENTITY_ID) { Throw.Ent_ThrowIsNotAlive(_world, entityID); }
if (itemIndex <= 0) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
#elif DRAGONECS_STABILITY_MODE
if (itemIndex <= 0) { return; }
if (_isLocked) { return; }
#endif
EcsComponentLifecycle<T>.OnDel( _isCustomLifecycle, _customLifecycle, ref ((T*)_items)[itemIndex], _worldID, entityID);
if (_recycledItemsCount >= _recycledItemsLength)
{
_recycledItemsLength = ArrayUtility.NextPow2(_recycledItemsLength << 1);
_recycledItemsHandler = Realloc(_recycledItemsHandler, _recycledItemsLength);
_recycledItems = _recycledItemsHandler.Ptr;
}
_recycledItems[_recycledItemsCount++] = itemIndex;
itemIndex = 0;
_itemsCount--;
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
}
public void TryDel(int entityID)
{
if (Has(entityID))
{
Del(entityID);
}
}
public void Copy(int fromEntityID, int toEntityID)
{
#if DEBUG
if (!Has(fromEntityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(fromEntityID); }
#elif DRAGONECS_STABILITY_MODE
if (!Has(fromEntityID)) { return; }
#endif
EcsComponentCopy<T>.Copy(_isCustomCopy, _customCopy, ref Get(fromEntityID), ref TryAddOrGet(toEntityID));
}
public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID)
{
#if DEBUG
if (!Has(fromEntityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(fromEntityID); }
#elif DRAGONECS_STABILITY_MODE
if (!Has(fromEntityID)) { return; }
#endif
EcsComponentCopy<T>.Copy(_isCustomCopy, _customCopy, ref Get(fromEntityID), ref toWorld.GetPool<T>().TryAddOrGet(toEntityID));
}
public void ClearAll()
{
#if DEBUG
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
#elif DRAGONECS_STABILITY_MODE
if (_isLocked) { return; }
#endif
_recycledItemsCount = 0; // спереди чтобы обнулялось, так как Del не обнуляет
if (_itemsCount <= 0) { return; }
var span = _world.Where(out SinglePoolAspect<EcsValuePool<T>> _);
foreach (var entityID in span)
{
ref int itemIndex = ref _mapping[entityID];
EcsComponentLifecycle<T>.OnDel(_isCustomLifecycle, _customLifecycle, ref ((T*)_items)[itemIndex], _worldID, entityID);
itemIndex = 0;
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
}
_itemsCount = 0;
_recycledItemsCount = 0;
}
#endregion
#region Callbacks
void IEcsPoolImplementation.OnWorldResize(int newSize)
{
_mappingHandler = ReallocAndInit<int>(_mappingHandler, newSize);
_mapping = _mappingHandler.Ptr;
_sharedStore->_mapping = _mapping;
}
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
{
if (_itemsCount <= 0)
{
return;
}
foreach (var entityID in buffer)
{
TryDel(entityID);
}
}
void IEcsPoolImplementation.OnLockedChanged_Debug(bool locked) { _isLocked = locked; }
#endregion
#region Other
void IEcsPool.AddEmpty(int entityID) { Add(entityID); }
void IEcsPool.AddRaw(int entityID, object dataRaw)
{
Add(entityID) = dataRaw == null ? default : (T)dataRaw;
}
object IEcsReadonlyPool.GetRaw(int entityID) { return Read(entityID); }
void IEcsPool.SetRaw(int entityID, object dataRaw)
{
Get(entityID) = dataRaw == null ? default : (T)dataRaw;
}
#endregion
#region Listeners
#if !DRAGONECS_DISABLE_POOLS_EVENTS
public void AddListener(IEcsPoolEventListener listener)
{
EcsDebug.PrintWarning($"the {nameof(AddListener)} method is not supported for the {nameof(EcsValuePool<T>)}.");
}
public void RemoveListener(IEcsPoolEventListener listener)
{
EcsDebug.PrintWarning($"the {nameof(RemoveListener)} method is not supported for the {nameof(EcsValuePool<T>)}.");
}
#endif
#endregion
#region IEnumerator - IntelliSense hack
IEnumerator<T> IEnumerable<T>.GetEnumerator() { throw new NotImplementedException(); }
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
#endregion
#region Convertors
public static implicit operator EcsValuePool<T>(IncludeMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
public static implicit operator EcsValuePool<T>(ExcludeMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
public static implicit operator EcsValuePool<T>(AnyMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
public static implicit operator EcsValuePool<T>(OptionalMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
public static implicit operator EcsValuePool<T>(EcsWorld.GetPoolInstanceMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
#endregion
#region Apply
public static void Apply(ref T component, int entityID, short worldID)
{
EcsWorld.GetPoolInstance<EcsValuePool<T>>(worldID).TryAddOrGet(entityID) = component;
}
public static void Apply(ref T component, int entityID, EcsValuePool<T> pool)
{
pool.TryAddOrGet(entityID) = component;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Apply(short worldID, int entityID)
{
return ref EcsWorld.GetPoolInstance<EcsValuePool<T>>(worldID).TryAddOrGet(entityID);
}
#endregion
}
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
#endif
[MetaTags(MetaTags.HIDDEN)]
[MetaColor(MetaColor.DragonRose)]
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.OTHER_GROUP)]
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly struct ReadonlyEcsValuePool<T> : IEcsReadonlyPool //IEnumerable<T> - IntelliSense hack
where T : unmanaged, IEcsValueComponent
{
private readonly EcsValuePool<T> _pool;
#region Properties
public int ComponentTypeID
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _pool.ComponentTypeID; }
}
public Type ComponentType
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _pool.ComponentType; }
}
public EcsWorld World
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _pool.World; }
}
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _pool.Count; }
}
public bool IsReadOnly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _pool.IsReadOnly; }
}
public ref readonly T this[int entityID]
{
get { return ref _pool.Read(entityID); }
}
#endregion
#region Constructors
internal ReadonlyEcsValuePool(EcsValuePool<T> pool)
{
_pool = pool;
}
#endregion
#region Methods
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(int entityID) { return _pool.Has(entityID); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Get(int entityID) { return ref _pool.Read(entityID); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entityID) { return ref _pool.Read(entityID); }
object IEcsReadonlyPool.GetRaw(int entityID) { return _pool.Read(entityID); }
#if !DRAGONECS_DISABLE_POOLS_EVENTS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddListener(IEcsPoolEventListener listener) { _pool.AddListener(listener); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveListener(IEcsPoolEventListener listener) { _pool.AddListener(listener); }
#endif
#endregion
#region Convertors
public static implicit operator ReadonlyEcsValuePool<T>(EcsValuePool<T> a) { return new ReadonlyEcsValuePool<T>(a); }
public static implicit operator ReadonlyEcsValuePool<T>(IncludeMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
public static implicit operator ReadonlyEcsValuePool<T>(ExcludeMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
public static implicit operator ReadonlyEcsValuePool<T>(AnyMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
public static implicit operator ReadonlyEcsValuePool<T>(OptionalMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
public static implicit operator ReadonlyEcsValuePool<T>(EcsWorld.GetPoolInstanceMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
#endregion
}
internal unsafe struct EcsValuePoolSharedStore
{
public short _worldID;
public int _componentTypeID;
public int* _mapping;
public void* _items;
}
public readonly unsafe struct NativeEcsValuePool<T>
where T : unmanaged, IEcsValueComponent
{
private readonly EcsValuePoolSharedStore* _store;
public short WorldID
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _store->_worldID; }
}
public int ComponentTypeID
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _store->_componentTypeID; }
}
public ref T this[int entityID]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return ref Get(entityID); }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal NativeEcsValuePool(EcsValuePoolSharedStore* store)
{
_store = store;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(int entityID) { return _store->_mapping[entityID] != 0; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Get(int entityID) { return ref ((T*)_store->_items)[_store->_mapping[entityID]]; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entityID) { return ref Get(entityID); }
}
public static class EcsValuePoolExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsValuePool<TComponent> GetPool<TComponent>(this EcsWorld self) where TComponent : unmanaged, IEcsValueComponent
{
return self.GetPoolInstance<EcsValuePool<TComponent>>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsValuePool<TComponent> GetPoolUnchecked<TComponent>(this EcsWorld self) where TComponent : unmanaged, IEcsValueComponent
{
return self.GetPoolInstanceUnchecked<EcsValuePool<TComponent>>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsValuePool<TComponent> Inc<TComponent>(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent
{
return self.IncludePool<EcsValuePool<TComponent>>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsValuePool<TComponent> Exc<TComponent>(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent
{
return self.ExcludePool<EcsValuePool<TComponent>>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsValuePool<TComponent> Opt<TComponent>(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent
{
return self.OptionalPool<EcsValuePool<TComponent>>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsValuePool<TComponent> Any<TComponent>(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent
{
return self.AnyPool<EcsValuePool<TComponent>>();
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ffb282acdd5b51e4da44dc5cef296d50
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -37,7 +37,7 @@ namespace DCFApixels.DragonECS
return;
}
}
throw new InvalidOperationException($"Using component {componentType.GetMeta().TypeName} is not allowed in the {worldType.GetMeta().TypeName} world.");
throw new InvalidOperationException($"Using component {componentType.ToMeta().TypeName} is not allowed in the {worldType.ToMeta().TypeName} world.");
}
}
}

View File

@ -139,12 +139,12 @@ namespace DCFApixels.DragonECS.Core.Internal
[MethodImpl(MethodImplOptions.NoInlining)]
internal static void Quiery_ArgumentDifferentWorldsException()
{
ArgumentDifferentWorldsException();
throw new ArgumentException("The groups belong to different worlds.");
}
[MethodImpl(MethodImplOptions.NoInlining)]
internal static void ArgumentDifferentWorldsException()
{
throw new ArgumentException("World ID mismatch: the expected and actual world identifiers do not match.");
throw new ArgumentException("The groups belong to different worlds.");
}
[MethodImpl(MethodImplOptions.NoInlining)]

View File

@ -252,5 +252,74 @@ namespace DCFApixels.DragonECS.Core
return _graph.Sort();
}
#endregion
#region Obsolete
[Obsolete("Use " + nameof(LayersMap) + ".Add(layer).Before(targetLayer).Back;")]
public EcsPipeline.Builder Insert(string targetLayer, string newLayer)
{
Add(newLayer).Before(targetLayer);
return _pipelineBuilder;
}
[Obsolete("Use " + nameof(LayersMap) + ".Add(layer).After(targetLayer).Back;")]
public EcsPipeline.Builder InsertAfter(string targetLayer, string newLayer)
{
Add(newLayer).After(targetLayer);
return _pipelineBuilder;
}
[Obsolete("Use " + nameof(LayersMap) + ".Move(layer).Before(targetLayer).Back;")]
public EcsPipeline.Builder Move(string targetLayer, string newLayer)
{
Move(newLayer).Before(targetLayer);
return _pipelineBuilder;
}
[Obsolete("Use " + nameof(LayersMap) + ".Move(layer).After(targetLayer).Back;")]
public EcsPipeline.Builder MoveAfter(string targetLayer, string newLayer)
{
Move(newLayer).After(targetLayer);
return _pipelineBuilder;
}
[Obsolete("Use " + nameof(LayersMap) + ".Add(layers).Before(targetLayer).Back;")]
public EcsPipeline.Builder Insert(string targetLayer, params string[] newLayers)
{
Add(newLayers).Before(targetLayer);
return _pipelineBuilder;
}
[Obsolete("Use " + nameof(LayersMap) + ".Add(layers).After(targetLayer).Back;")]
public EcsPipeline.Builder InsertAfter(string targetLayer, params string[] newLayers)
{
Add(newLayers).After(targetLayer);
return _pipelineBuilder;
}
[Obsolete("Use " + nameof(LayersMap) + ".Move(layers).Before(targetLayer).Back;")]
public EcsPipeline.Builder Move(string targetLayer, params string[] movingLayers)
{
Move(movingLayers).Before(targetLayer);
return _pipelineBuilder;
}
[Obsolete("Use " + nameof(LayersMap) + ".Move(layers).After(targetLayer).Back;")]
public EcsPipeline.Builder MoveAfter(string targetLayer, params string[] movingLayers)
{
Move(movingLayers).After(targetLayer);
return _pipelineBuilder;
}
[Obsolete]
public object this[int index]
{
get
{
int i = 0;
foreach (var item in this)
{
if (i == index)
{
return item;
}
i++;
}
return null;
}
}
#endregion
}
}

View File

@ -0,0 +1,202 @@
#if ENABLE_DUMMY_SPAN
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
namespace DCFApixels.DragonECS
{
internal static class ThrowHelper
{
public static void ThrowIndexOutOfRangeException() => throw new IndexOutOfRangeException();
public static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException();
public static void ThrowInvalidOperationException() => throw new InvalidOperationException();
}
[DebuggerDisplay("{ToString(),raw}")]
public readonly ref struct ReadOnlySpan<T>
{
public static ReadOnlySpan<T> Empty => new ReadOnlySpan<T>(null);
internal readonly T[] _array;
private readonly int _start;
private readonly int _length;
#region Properties
public int Length => _length;
public bool IsEmpty => _length == 0;
public ref readonly T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if ((uint)index >= (uint)_length || (uint)index < 0)
ThrowHelper.ThrowIndexOutOfRangeException();
return ref _array[index + _start];
}
}
#endregion
#region Constructors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan(T[] array)
{
_array = array ?? Array.Empty<T>();
_start = 0;
_length = array.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan(T[] array, int start, int length)
{
if (array == null)
{
if (start != 0 || length != 0)
ThrowHelper.ThrowArgumentOutOfRangeException();
_array = Array.Empty<T>();
_start = 0;
_length = 0;
return;
}
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
_array = array;
_start = start;
_length = length;
}
#endregion
#region Object
#pragma warning disable CS0809 // Óñòàðåâøèé ÷ëåí ïåðåîïðåäåëÿåò íåóñòàðåâøèé ÷ëåí
[Obsolete("Equals() on ReadOnlySpan will always throw an exception. Use the equality operator instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object obj) => throw new NotSupportedException();
[Obsolete("GetHashCode() on ReadOnlySpan will always throw an exception.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() => throw new NotSupportedException();
#pragma warning restore CS0809 // Óñòàðåâøèé ÷ëåí ïåðåîïðåäåëÿåò íåóñòàðåâøèé ÷ëåí
public override string ToString()
{
//if (typeof(T) == typeof(char))
// return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _reference), _length));
return $"System.ReadOnlySpan<{typeof(T).Name}>[{_length}]";
}
#endregion
#region operators
public static bool operator !=(ReadOnlySpan<T> left, ReadOnlySpan<T> right) => !(left == right);
public static implicit operator ReadOnlySpan<T>(T[] array) => new ReadOnlySpan<T>(array);
public static implicit operator ReadOnlySpan<T>(ArraySegment<T> segment) => new ReadOnlySpan<T>(segment.Array, segment.Offset, segment.Count);
public static bool operator ==(ReadOnlySpan<T> left, ReadOnlySpan<T> right) => left._length == right._length && left._array == right._array;
#endregion
#region Enumerator
public Enumerator GetEnumerator() => new Enumerator(this);
public ref struct Enumerator
{
private readonly ReadOnlySpan<T> _span;
private int _index;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(ReadOnlySpan<T> span)
{
_span = span;
_index = span._start - 1;
}
public ref readonly T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _span[_index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
int index = _index + 1;
if (index < _span.Length)
{
_index = index;
return true;
}
return false;
}
}
#endregion
#region Other
[EditorBrowsable(EditorBrowsableState.Never)]
public ref readonly T GetPinnableReference()
{
if (_length != 0) ThrowHelper.ThrowInvalidOperationException();
return ref _array[0];
}
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public void CopyTo(Span<T> destination)
//{
// if ((uint)_length <= (uint)destination.Length)
// {
// Buffer.Memmove(ref destination._reference, ref _reference, (uint)_length);
// }
// else
// {
// ThrowHelper.ThrowArgumentException_DestinationTooShort();
// }
//}
//public bool TryCopyTo(Span<T> destination)
//{
// bool retVal = false;
// if ((uint)_length <= (uint)destination.Length)
// {
// Buffer.Memmove(ref destination._reference, ref _reference, (uint)_length);
// retVal = true;
// }
// return retVal;
//}
public void CopyTo(T[] array)
{
if (_length > array.Length)
{
throw new ArgumentOutOfRangeException();
}
for (int i = 0; i < _length; i++)
{
array[i] = _array[i];
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> Slice(int start)
{
if ((uint)start > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();
return new ReadOnlySpan<T>(_array, _start + start, _length - start);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> Slice(int start, int length)
{
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
return new ReadOnlySpan<T>(_array, _start + start, length);
}
public T[] ToArray()
{
if (_length == 0)
return Array.Empty<T>();
var result = new T[_length];
Array.Copy(_array, _start, result, 0, _length);
return result;
}
#endregion
}
}
#endif

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: fd6afd87df377e5408428ccec3c02685
guid: bfb00ad58c175a84388c316daf9b03ae
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -2,7 +2,7 @@
#undef DEBUG
#endif
namespace DCFApixels.DragonECS.Core.Unchecked
namespace DCFApixels.DragonECS.UncheckedCore
{
public readonly struct EntitiesMatrix
{

View File

@ -5,6 +5,73 @@ using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS.UncheckedCore
{
[Obsolete("Use DCFApixels.DragonECS.Core.UncheckedUtility")]
public static class UncheckedCoreUtility
{
#region CreateEntLong
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static entlong CreateEntLong(int entityID, short gen, short worldID)
{
return new entlong(entityID, gen, worldID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static entlong CreateEntLong(long entityGenWorld)
{
return new entlong(entityGenWorld);
}
#endregion
#region CreateSpan
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsSpan CreateSpan(short worldID, ReadOnlySpan<int> entitesArray)
{
return new EcsSpan(worldID, entitesArray);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsSpan CreateSpan(short worldID, int[] entitesArray, int startIndex, int length)
{
return new EcsSpan(worldID, entitesArray, startIndex, length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsSpan CreateSpan(short worldID, int[] entitesArray, int length)
{
return new EcsSpan(worldID, entitesArray, length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsSpan CreateSpan(short worldID, int[] entitesArray)
{
return new EcsSpan(worldID, entitesArray);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsSpan CreateEmptySpan(short worldID)
{
return new EcsSpan(worldID, Array.Empty<int>());
}
public static bool CheckSpanValideDebug(EcsSpan span)
{
HashSet<int> set = new HashSet<int>();
foreach (var e in span)
{
if (set.Add(e) == false)
{
return false;
}
}
return true;
}
#endregion
#region EcsGroup
public static EcsGroup GetSourceInstance(EcsReadonlyGroup group)
{
return group.GetSource_Internal();
}
#endregion
}
}
namespace DCFApixels.DragonECS.Core.Unchecked
{
public static class UncheckedUtility
@ -62,14 +129,6 @@ namespace DCFApixels.DragonECS.Core.Unchecked
}
#endregion
#region Unsafe Span
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe EcsUnsafeSpan CreateUnsafeSpan(short worldID, int* ptr, int length)
{
return new EcsUnsafeSpan(worldID, ptr, length);
}
#endregion
#region EcsGroup
public static EcsGroup GetSourceInstance(EcsReadonlyGroup group)
{

View File

@ -116,48 +116,6 @@ namespace DCFApixels.DragonECS
#endregion
#region Constructors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public entlong(EcsWorld world, int id) : this()
{
if (world == null)
{
_id = 0;
_gen = 0;
_world = 0;
}
else
{
_id = id;
_gen = world.GetGen(id);
_world = world.ID;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public entlong(int id, EcsWorld world) : this(world, id) { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public entlong(EcsWorld world, entlong entity) : this()
{
#if DEBUG
if (world.ID != entity.WorldID) { Throw.ArgumentDifferentWorldsException(); }
#elif DRAGONECS_STABILITY_MODE
if (world.ID != entity.WorldID) { world = null; }
#endif
if (world == null)
{
_id = 0;
_gen = 0;
_world = 0;
}
else
{
_id = entity._id;
_gen = entity._gen;
_world = entity._world;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public entlong(entlong entity, EcsWorld world) : this(world, entity) { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public entlong(int id, short gen, short world) : this()
{
@ -170,6 +128,12 @@ namespace DCFApixels.DragonECS
{
_full = full;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe entlong NewUnsafe(long id, long gen, long world)
{
long x = id << 48 | gen << 32 | id;
return *(entlong*)&x;
}
#endregion
#region Unpacking Try
@ -191,13 +155,6 @@ namespace DCFApixels.DragonECS
worldID = _world;
return IsAlive;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetGen(out short gen)
{
gen = _gen;
return IsAlive;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryUnpack(out int id)
@ -235,24 +192,17 @@ namespace DCFApixels.DragonECS
id = _id;
return IsAlive;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryUnpack(EcsWorld world)
{
if (world.ID != _world) { return false; }
return world.IsAlive(_id, _gen);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryUnpack(EcsWorld world, out int id)
{
if (world.ID != _world) { id = EcsConsts.NULL_ENTITY_ID; return false; }
if (world.ID != _world) { Throw.ArgumentDifferentWorldsException(); }
id = _id;
return world.IsAlive(_id, _gen);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryUnpack(EcsWorld world, out int id, out short gen)
{
if (world.ID != _world) { gen = 0; id = EcsConsts.NULL_ENTITY_ID; return false; }
if (world.ID != _world) { Throw.ArgumentDifferentWorldsException(); }
gen = _gen;
id = _id;
return world.IsAlive(_id, _gen);
@ -260,14 +210,14 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryUnpack(EcsMask mask, out int id)
{
if (mask.WorldID != _world) { id = EcsConsts.NULL_ENTITY_ID; return false; }
if (mask.WorldID != _world) { Throw.ArgumentDifferentWorldsException(); }
id = _id;
return mask.World.IsAlive(_id, _gen) && mask.World.IsMatchesMask(mask, _id);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryUnpack(EcsMask mask, out int id, out short gen)
{
if (mask.WorldID != _world) { gen = 0; id = EcsConsts.NULL_ENTITY_ID; return false; }
if (mask.WorldID != _world) { Throw.ArgumentDifferentWorldsException(); }
gen = _gen;
id = _id;
return mask.World.IsAlive(_id, _gen) && mask.World.IsMatchesMask(mask, _id);
@ -275,14 +225,14 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryUnpack(EcsAspect aspect, out int id)
{
if (aspect.World.ID != _world) { id = EcsConsts.NULL_ENTITY_ID; return false; }
if (aspect.World.ID != _world) { Throw.ArgumentDifferentWorldsException(); }
id = _id;
return aspect.World.IsAlive(_id, _gen) && aspect.IsMatches(_id);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryUnpack(EcsAspect aspect, out int id, out short gen)
{
if (aspect.World.ID != _world) { gen = 0; id = EcsConsts.NULL_ENTITY_ID; return false; }
if (aspect.World.ID != _world) { Throw.ArgumentDifferentWorldsException(); }
gen = _gen;
id = _id;
return aspect.World.IsAlive(_id, _gen) && aspect.IsMatches(_id);
@ -373,39 +323,9 @@ namespace DCFApixels.DragonECS
id = _id;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Deconstruct(out int id, out short gen, out short worldID)
{
#if DEBUG
if (IsAlive == false) { Throw.Ent_ThrowIsNotAlive(this); }
#elif DRAGONECS_STABILITY_MODE
if (IsAlive == false)
{
worldID = EcsConsts.NULL_WORLD_ID;
gen = default;
id = EcsConsts.NULL_ENTITY_ID;
return;
}
#endif
worldID = _world;
gen = _gen;
id = _id;
}
public void Deconstruct(out int id, out short gen, out short worldID) { Unpack(out id, out gen, out worldID); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Deconstruct(out int id, out EcsWorld world)
{
#if DEBUG
if (IsAlive == false) { Throw.Ent_ThrowIsNotAlive(this); }
#elif DRAGONECS_STABILITY_MODE
if (IsAlive == false)
{
world = EcsWorld.GetWorld(EcsConsts.NULL_WORLD_ID);
id = EcsConsts.NULL_ENTITY_ID;
return;
}
#endif
world = EcsWorld.GetWorld(_world);
id = _id;
}
public void Deconstruct(out int id, out EcsWorld world) { Unpack(out id, out world); }
#endregion
#region Unpacking Unchecked
@ -464,10 +384,16 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator entlong((EcsWorld world, int entityID) a) { return Combine_Internal(a.entityID, a.world); }
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public static implicit operator entlong((entlong entity, EcsWorld world) a) { return Combine_Internal(a.entity._id, a.world); }
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public static implicit operator entlong((EcsWorld world, entlong entity) a) { return Combine_Internal(a.entity._id, a.world); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator entlong((entlong entity, EcsWorld world) a) { return Combine_Internal(a.entity, a.world); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator entlong((EcsWorld world, entlong entity) a) { return Combine_Internal(a.entity, a.world); }
private static entlong Combine_Internal(int entityID, EcsWorld world)
{
return world == null ? new entlong(entityID, 0, 0) : world.GetEntityLong(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator long(entlong a) { return a._full; }
@ -479,21 +405,6 @@ namespace DCFApixels.DragonECS
#region Other
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static entlong Combine_Internal(int entityID, EcsWorld world)
{
return world == null ? new entlong(entityID, 0, 0) : world.GetEntityLong(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static entlong Combine_Internal(entlong entity, EcsWorld world)
{
#if DEBUG
if (world.ID != entity.WorldID) { Throw.ArgumentDifferentWorldsException(); }
#elif DRAGONECS_STABILITY_MODE
if (world.ID != entity.WorldID) { return default; }
#endif
return entity;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private EcsWorld GetWorld_Internal()
{
#if DRAGONECS_STABILITY_MODE
@ -501,7 +412,6 @@ namespace DCFApixels.DragonECS
#endif
return EcsWorld.GetWorld(_world);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() { return unchecked((int)_full) ^ (int)(_full >> 32); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]