Compare commits

...

84 Commits
0.9.21 ... main

Author SHA1 Message Date
Mikhail
4f28618277 up version 1.0.1 2026-04-21 22:18:02 +08:00
Mikhail
2cca65e37e fix 2026-04-21 21:51:05 +08:00
Mikhail
b45e9ea608 Update package.json 2026-04-21 15:15:30 +08:00
Mikhail
c3ff8b0b58 update readme 2026-04-21 14:21:15 +08:00
Mikhail
a37939a620 update readme 2026-04-21 13:59:12 +08:00
Mikhail
2b50fbdb55 Update README-RU.md 2026-04-19 18:36:33 +08:00
Mikhail
acf8c7f89a up version 0.9.23 2026-04-17 17:26:39 +08:00
Mikhail
0ecf5e378f Update readme 2026-04-17 17:17:05 +08:00
Mikhail
91d196e1a9 update 2026-04-16 15:14:15 +08:00
Mikhail
0f8d619468 Update TypeMeta.cs 2026-04-15 15:38:44 +08:00
Mikhail
211f5a657c Update TypeMeta.cs 2026-04-15 15:38:01 +08:00
Mikhail
d3e77026ff fixes 2026-04-15 15:33:28 +08:00
Mikhail
87c98f708e update meta 2026-04-15 14:37:16 +08:00
Mikhail
d52c3031f2 update MetaProxy 2026-04-15 11:13:15 +08:00
Mikhail
9deb8aa3a9 rename MetaPRoxy yto MetaProxyBase 2026-04-14 15:19:26 +08:00
Mikhail
23cee3e4c7 rework namespaces/remove IEcsTypeMetaProvider 2026-04-14 12:52:18 +08:00
Mikhail
371472e851 add MetaProxyAttribute 2026-04-13 23:21:07 +08:00
Mikhail
68d3d2bc4f fix 2026-04-13 23:20:36 +08:00
Mikhail
20875d420a up version to 0.9.22 2026-04-08 15:20:39 +08:00
Mikhail
a71a77989f fix 2026-04-08 15:13:57 +08:00
Mikhail
68059e9487 Create JsonDebugger.cs.meta 2026-04-08 15:07:14 +08:00
Mikhail
8042256c61 Update JsonDebugger.cs 2026-04-08 15:01:46 +08:00
Mikhail
04ba0a3549 Update JsonDebugger.cs 2026-04-08 14:50:30 +08:00
Mikhail
1f263726f9 Update JsonDebugger.cs 2026-04-07 23:38:10 +08:00
Mikhail
f8e35f58c6 add EcsDebug.PrintJson 2026-04-06 23:34:13 +08:00
Mikhail
94aeb54756 Update EcsMask.cs 2026-04-01 16:11:44 +08:00
Mikhail
9d7fcf3d71 Merge branch 'dev' of github.com:DCFApixels/DragonECS into dev 2026-04-01 15:10:17 +08:00
Mikhail
310c01e3ec Update README.md 2026-04-01 15:10:01 +08:00
Mikhail
3127af7371 update pool applay method 2026-04-01 12:31:04 +08:00
Mikhail
3914fca7f7 Update README-RU.md 2026-03-30 20:11:55 +08:00
Mikhail
8647f62335 update readme 2026-03-29 18:07:01 +08:00
Mikhail
59d2f07425 Update README.md 2026-03-29 17:11:17 +08:00
Mikhail
bfac073340 Update README.md 2026-03-29 01:49:35 +08:00
Mikhail
15edd9ea54 update readme 2026-03-28 23:54:47 +08:00
Mikhail
978026e0a1 update readme 2026-03-28 22:54:20 +08:00
Mikhail
b2dace441e Merge branch 'main' into dev 2026-03-28 22:52:29 +08:00
Mikhail
120fa9779d
Update README.md 2026-03-28 22:51:52 +08:00
Mikhail
22e09a7ed2 Update EcsPool.cs 2026-03-28 18:47:45 +08:00
Mikhail
018a0e7d41 update readme 2026-03-28 02:35:16 +08:00
Mikhail
7a8cbf9f0a fix 2026-03-23 16:29:10 +08:00
Mikhail
b01b406e9e fix 2026-03-21 23:56:11 +08:00
Mikhail
b1244e42a0 fix mem allock debugging 2026-03-18 15:44:47 +08:00
Mikhail
1c6edc6e6b fix value pool 2026-03-18 14:39:34 +08:00
Mikhail
c347011452 fix value pool 2026-03-18 13:52:09 +08:00
Mikhail
0d777fd794 Update README-ZH.md 2026-03-18 13:46:23 +08:00
Mikhail
975720ecf3 update readme 2026-03-18 13:41:51 +08:00
Mikhail
8bb2a1cbd8 update readme 2026-03-18 13:36:52 +08:00
Mikhail
5b9e23f852 update readme 2026-03-18 13:25:34 +08:00
Mikhail
cfaed5b6d7 Update EcsValuePool.cs 2026-03-17 16:10:07 +08:00
Mikhail
72729c3222 fix 2026-03-17 16:08:31 +08:00
Mikhail
b101f029ce Update EcsValuePool.cs 2026-03-17 16:02:45 +08:00
Mikhail
3715857f0f Update EcsValuePool.cs 2026-03-17 16:00:18 +08:00
Mikhail
18db9a24b5 update il2cpp atrs 2026-03-17 15:40:04 +08:00
Mikhail
492416aba1 Update EcsValuePool.cs 2026-03-17 15:34:54 +08:00
Mikhail
cc834c4a92 fix mem alloc api 2026-03-17 15:34:31 +08:00
Mikhail
015c6be9f0 Merge branch 'dev' into unsafe_pool 2026-03-17 15:25:02 +08:00
Mikhail
37e692fdda Merge branch 'main' into unsafe_pool 2026-03-17 15:22:22 +08:00
Mikhail
2a0cb1085f optimize cleanup of empty entities during batch creation 2026-03-17 15:15:43 +08:00
Mikhail
56fed70c15 update mask debugger 2026-03-17 14:52:32 +08:00
Mikhail
37718d354a fixes/optimization 2026-03-17 14:52:19 +08:00
Mikhail
f0d9a806ff fix span 2026-03-17 10:28:04 +08:00
Mikhail
b6e43f98fb fix mem alloc 2026-03-17 10:27:48 +08:00
Mikhail
b6953c919c Update README-RU.md 2026-03-17 09:59:46 +08:00
Mikhail
25a987ea58 fix mem alloc 2026-03-17 09:57:50 +08:00
Mikhail
fe660f5500 updtae unity meta 2026-03-16 23:36:20 +08:00
Mikhail
7422d47b35 rename to SortHalper 2026-03-16 23:21:00 +08:00
Mikhail
ee65362a1b update sorting 2026-03-16 23:18:00 +08:00
Mikhail
01985d7ffb Merge branch 'dev' of https://github.com/DCFApixels/DragonECS into dev 2026-03-16 22:58:31 +08:00
Mikhail
e13e332aab Delete ReadOnlySpanDummy.cs.meta 2026-03-16 22:58:18 +08:00
Mikhail
856682b28e fixes 2026-03-16 22:57:16 +08:00
Mikhail
8472280d4b update internal 2026-03-16 22:53:20 +08:00
Mikhail
b686e4ab5f update readme/ remove obsolete 2026-03-16 21:53:00 +08:00
Mikhail
c30d4ec5f0 remove span dummy/update&fix alloc management/add EcsUnsafeSpan & WhereUnsafe 2026-03-16 21:52:45 +08:00
Mikhail
14bf846135 Update README-RU.md 2026-03-16 12:41:53 +08:00
Mikhail
821fd37d85 cleanup Obsolete 2026-03-16 10:35:43 +08:00
Mikhail
bc5278c021 fix 2026-03-15 21:56:48 +08:00
Mikhail
48014f3b73 rename ToMeta to GetMeta 2026-03-15 20:21:16 +08:00
Mikhail
b1192d32d9 fxi tag pool checks/add ThrowNullComponent/add EcsRefPool to readme 2026-03-15 20:20:24 +08:00
Mikhail
47d8e100a1 fix 2026-03-15 17:54:23 +08:00
Mikhail
ec26a1623a Merge branch 'main' into dev 2026-03-15 17:28:19 +08:00
Mikhail
b6d3da46f7 update pools / update masks api/ remove obsolete 2026-03-15 17:28:11 +08:00
Mikhail
5e11f255e6 update entlong 2026-03-14 14:44:07 +08:00
DCFApixels
3c029036c8 Merge branch 'dev' into unsafe_pool 2025-10-13 12:15:40 +08:00
DCFApixels
7da652e78c stash 2025-07-20 16:06:30 +08:00
67 changed files with 3339 additions and 1799 deletions

View File

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

View File

@ -20,33 +20,29 @@
<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>
</br>
DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system) фреймворк, нацеленный на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Разработан на чистом C#, без зависимостей и генерации кода. Вдохновлен [LeoEcs Lite](https://github.com/Leopotam/ecslite).
DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system) фреймворк нацеленный на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Разработан на чистом C#, без зависимостей и генерации кода. Вдохновлен [LeoEcs Lite](https://github.com/Leopotam/ecslite).
> [!WARNING]
> Проект предрелизной версии, поэтому API может меняться. В ветке main актуальная и рабочая версия.
>
> Если есть неясные моменты, вопросы можно задать тут [Обратная связь](#обратная-связь)
> [!Note]
> Есть вопросы или хотите быть в курсе разработки? Присоединяйтесь к нашему комьюнити! -> [Обратная связь](#обратная-связь)
## Оглавление
- [Установка](#установка)
@ -77,24 +73,24 @@ DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system)
- [Расширение фреймворка](#расширение-фреймворка)
- [Компоненты мира](#компоненты-мира)
- [Конфиги](#конфиги)
- [Проекты на DragonECS](#Проекты-на-DragonECS)
- [Проекты на DragonECS](#проекты-на-dragonecs)
- [FAQ](#faq)
- [Обратная связь](#обратная-связь)
- [Лицензия](#лицензия)
</br>
# Установка
Семантика версионирования - [Открыть](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
Семантика версионирования - [Открыть](https://gist.github.com/DCFApixels/af79284955bf40e9476cdcac79d7b098#file-dcfapixels_versioning-md)
## Окружение
Обязательные требования:
+ Минимальная версия C# 7.3;
+ Минимальная версия C#: 7.3.
Опционально:
+ Поддержка NativeAOT
Поддерживает:
+ NativeAOT;
+ Игровые движки с C#: Unity, Godot, MonoGame и т.д.
Протестировано:
+ **Unity:** Минимальная версия 2020.3.0;
Протестировано на:
* **Unity:** Минимальная версия 2021.2.0.
## Установка для Unity
> Рекомендуется так же установить расширение [Интеграция с движком Unity](https://github.com/DCFApixels/DragonECS-Unity)
@ -109,9 +105,9 @@ https://github.com/DCFApixels/DragonECS.git
```
* ### В виде исходников
Можно так же напрямую скопировать в проект исходники фреймворка.
Можно так же напрямую скопировать исходники фреймворка в проект.
</br>
# Расширения
* Интеграции:
@ -125,19 +121,20 @@ 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();
@ -155,7 +152,7 @@ int newEntityID = _world.CloneEntity(entityID);
<summary>Работа с entlong</summary>
``` c#
```c#
// Конвертация int в entlong.
entlong entity = _world.GetEntityLong(entityID);
// упрощенная версия
@ -215,7 +212,7 @@ class SomeSystem : IEcsPreInit, IEcsInit, IEcsRun, IEcsDestroy
<summary>Пример системы</summary>
``` c#
```c#
// Система
class DamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
{
@ -253,7 +250,7 @@ public struct Health : IEcsComponent
</details>
</br>
# Концепции фреймворка
## Пайплайн
@ -284,9 +281,9 @@ class SomeSystem : IEcsRun, IEcsPipelineMember
```
> Для одновременного построения и инициализации есть метод Builder.BuildAndInit();
### Внедрение зависимостей
Фреймворк реализует внедрение зависимостей для систем. это процесс который запускается вместе с инициализацией пайплайна и внедряет данные переданные в Builder.
Фреймворк реализует внедрение зависимостей для систем. Это процесс, который запускается вместе с инициализацией пайплайна и внедряет данные, переданные в Builder.
``` c#
```c#
class SomeDataA { /* ... */ }
class SomeDataB : SomeDataA { /* ... */ }
@ -324,7 +321,7 @@ class SomeSystem : IEcsInject<SomeDataA>, IEcsRun
### Модули
Группы систем реализующие общую фичу можно объединять в модули, и просто добавлять модули в Pipeline.
``` c#
```c#
using DCFApixels.DragonECS;
class Module1 : IEcsModule
{
@ -337,7 +334,7 @@ class Module1 : IEcsModule
}
}
```
``` c#
```c#
EcsPipeline pipeline = EcsPipeline.New()
// ...
.AddModule(new Module1())
@ -346,29 +343,32 @@ 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.Insert(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)
// Система SomeSystem будет вставлена в слой SOME_LAYER
.Add(New SomeSystem(), SOME_LAYER)
// ...
.BuildAndInit();
```
Встроенные слои расположены в следующем порядке:
* `EcsConst.PRE_BEGIN_LAYER`
* `EcsConst.BEGIN_LAYER`
* `EcsConst.BASIC_LAYER` (По умолчанию системы добавляются сюда)
* `EcsConst.END_LAYER`
* `EcsConst.POST_END_LAYER`
* `EcsConsts.PRE_BEGIN_LAYER`
* `EcsConsts.BEGIN_LAYER`
* `EcsConsts.BASIC_LAYER` (По умолчанию системы добавляются сюда)
* `EcsConsts.END_LAYER`
* `EcsConsts.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.IncludePool<Pose>();
velocities = b.IncludePool<Velocity>();
b.ExcludePool<FreezedTag>();
poses = b.Inc<Pose>();
velocities = b.Inc<Velocity>();
b.Exc<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.Exclude<Pose>() тут оно будет заменено на b.Include<Pose>().
poses = b.Include<Pose>();
// Если в OtherAspect1 или в OtherAspect2 было условие b.Exc<Pose>() тут оно будет заменено на b.Inc<Pose>().
poses = b.Inc<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,6 +913,40 @@ 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-сервис.
@ -962,8 +996,7 @@ using (_marker.Auto())
+ `DRAGONECS_STABILITY_MODE` - включает опускаемые в релизном билде проверки.
+ `DRAGONECS_DISABLE_CATH_EXCEPTIONS` - Выключает поведение по умолчанию по обработке исключений. По умолчанию фреймворк будет ловить исключения с выводом информации из исключений через EcsDebug и продолжать работу.
+ `REFLECTION_DISABLED` - Полностью ограничивает работу фреймворка с Reflection.
+ `DISABLE_DEBUG` - Для среды где не поддерживается ручное отключение DEBUG, например Unity.
+ `ENABLE_DUMMY_SPAN` - На случай если в среде не поддерживаются Span типы, включает его замену.
+ `DISABLE_DEBUG` - Для среды, где не поддерживается ручное отключение DEBUG, например Unity.
</br>
@ -977,7 +1010,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>();
@ -1072,7 +1105,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/c593dba7-eeaa-4706-a043-946f132f3f83" alt="screenshot">
<img src="https://github.com/user-attachments/assets/6aba814d-a70b-432f-a905-84d1b6872581" />
</a>
</td>
<td align="center">
@ -1082,6 +1115,21 @@ 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>
## Опубликованные проекты:
@ -1123,15 +1171,8 @@ 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>()`.
@ -1142,6 +1183,11 @@ The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing
+ 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,15 +43,12 @@
DragonECS 是一个[实体组件系统](https://www.imooc.com/article/331544)框架。专注于提升便利性、模块性、可扩展性和动态实体修改性能。 用纯C#开发的,没有依赖和代码生成。灵感来自于[LeoEcs Lite](https://github.com/Leopotam/ecslite)。
> [!WARNING]
> 该框架是预发布版本,因此 API 可能会有变化。在 `main` 分支中是当前的工作版本。
>
> 最新版本的 README 是[俄文版](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md)。
>
> 如果有不清楚的地方,可以在这里提问 [反馈](#反馈)。
> [!Note]
> 有疑问或想了解开发进度?欢迎加入我们的社区! -> [Feedback](#Feedback)
## 目录
- [安装](#安装)
- [扩展](#扩展)
- [基础概念](#基础概念)
- [实体](#实体)
- [组件](#组件)
@ -71,7 +68,7 @@ DragonECS 是一个[实体组件系统](https://www.imooc.com/article/331544)框
- [集合](#集合)
- [ECS入口](#ECS入口)
- [Debug](#debug)
- [元属性](#元属性)
- [Meta Attributes](#meta-attributes)
- [EcsDebug](#ecsdebug)
- [性能分析](#性能分析)
- [Define Symbols](#define-symbols)
@ -79,43 +76,65 @@ DragonECS 是一个[实体组件系统](https://www.imooc.com/article/331544)框
- [世界组件](#世界组件)
- [配置](#配置)
- [使用DragonECS的项目](#使用dragonecs的项目)
- [扩展](#扩展)
- [FAQ](#faq)
- [反馈](#反馈)
- [License](#license)
</br>
# 安装
版本的语义 [[打开]](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
版本的语义 [[打开]](https://gist.github.com/DCFApixels/af79284955bf40e9476cdcac79d7b098#file-dcfapixels_versioning-md)
## 环境
必备要求:
+ C# 7.3 的最低版本;
* 最低 C# 版本7.3。
可选要求
+ 支持NativeAOT
+ 使用 C# 的游戏引擎Unity、Godot、MonoGame等。
支持
* NativeAOT
* 使用 C# 的游戏引擎Unity、Godot、MonoGame 等。
已测试:
+ **Unity:** 最低版本 2020.1.0
已测试
* **Unity:** 最低版本 2021.2.0。
## 为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`
## 为 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`
```
https://github.com/DCFApixels/DragonECS.git
```
或者在 `Packages/manifest.json` 中添加包条目:
```
"com.dcfa_pixels.dragonecs": "https://github.com/DCFApixels/DragonECS.git",
```
* ### 作为源代码
框架也可以通过复制源代码添加到项目中。
</br>
也可以通过将框架的源代码直接复制到项目中来使用该框架。
# 扩展
* 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 的扩展,可以[在此处发布](#反馈).
# 基础概念
## 实体
**实体**是附加数据的基础。它们以标识符的形式实现,有两种类型:
* `int` - 是在单个更新中使用的一次性标识符。不建议存储`int`标识符,而应使用 `entlong`
* `entlong` - 是一个长期标识符,包含一整套用于明确识别的信息;
``` c#
**实体**是附加数据的基础。有两种用于引用实体的标识符
* `int` - 短期标识符仅在单次更新tick内有效。不建议用于长期存储
* `entlong` - 长期标识符包含一个世代标记generation tag使其在实体的整个生命周期内保持唯一。适合长期保存和存储使用
```c#
// 在世界中创建一个新实体。
int entityID = _world.NewEntity();
@ -132,7 +151,7 @@ int newEntityID = _world.CloneEntity(entityID);
<details>
<summary>entlong使用</summary>
``` c#
```c#
// int 转换为 entlong。
entlong entity = _world.GetEntityLong(entityID);
// 或者
@ -220,7 +239,7 @@ class SomeSystem : IEcsRun, IEcsPipelineMember
### 依赖注入
框架具有向系统注入依赖的功能。这是一个与管线初始化一起运行的流程并注入传递给Builder的数据。
> 内置依赖注入的使用是可选的。
``` c#
```c#
class SomeDataA { /* ... */ }
class SomeDataB : SomeDataA { /* ... */ }
@ -251,9 +270,15 @@ class SomeSystem : IEcsInject<SomeDataA>, IEcsRun
}
```
静态标记的含义:
* `Inc` — 组件必须存在(包含条件)并缓存该池。
* `Exc` — 组件不得存在(排除条件)并缓存该池。
* `Opt` — 组件可以存在,但不影响过滤(仅缓存池以便访问)。
* `Any` — 至少要存在被 `Any` 标记的组件之一;同时缓存该池。
### 模块
实现一个共同特性的系统组可以组合成模块,模块也可以简单地添加到管线中。
``` c#
```c#
using DCFApixels.DragonECS;
class Module1 : IEcsModule
{
@ -283,18 +308,20 @@ const string SOME_LAYER = nameof(SOME_LAYER);
EcsPipeline pipeline = EcsPipeline.New()
// ...
// 在最终的 EcsConsts.END_LAYER 层前面插入一个新 SOME_LAYER 层。
.Layers.Insert(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)
// SomeSystem 系统将插入 SAME_LAYER 层。
.Add(New SomeSystem(), SOME_LAYER)
// ...
.BuildAndInit();
```
嵌入层按以下顺序排列:
* `EcsConst.PRE_BEGIN_LAYER`
* `EcsConst.BEGIN_LAYER`
* `EcsConst.BASIC_LAYER`(默认情况下,系统添加到此层)
* `EcsConst.END_LAYER`
* `EcsConst.POST_END_LAYER`
* `EcsConsts.PRE_BEGIN_LAYER`
* `EcsConsts.BEGIN_LAYER`
* `EcsConsts.BASIC_LAYER`(默认情况下,系统添加到此层)
* `EcsConsts.END_LAYER`
* `EcsConsts.POST_END_LAYER`
#### 排序顺序
在同一层内,可以使用 `int` 类型的排序值来排序系统。默认情况下,系统的排序值为 `sortOrder = 0`
```c#
@ -322,7 +349,7 @@ EcsPipeline pipeline = EcsPipeline.New()
<details>
<summary>用户流程</summary>
Для добавления нового процесса создайте интерфейс наследованный от `IEcsProcess` и создайте раннер для него. Раннер это класс реализующий интерфейс запускаемого процесса и наследуемый от `EcsRunner<TInterface>`. Пример:
要添加新的流程,请创建一个继承自 `IEcsProcess` 的接口,并为其创建一个 Runner。Runner 是一个实现该流程接口并继承自 `EcsRunner<TInterface>` 的类。示例:
``` c#
// 流程接口。
interface IDoSomethingProcess : IEcsProcess
@ -347,10 +374,10 @@ _pipeline = EcsPipeline.New()
.BuildAndInit();
// 如果启动器已经添加,运行它。
_pipeline.GetRunner<IDoSomethingProcess>.Do()
_pipeline.GetRunner<IDoSomethingProcess>().Do();
// 如果启动器尚未添加,使用 GetRunnerInstance 将其添加并运行
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>.Do()
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>().Do();
```
<details>
@ -437,7 +464,7 @@ poses.Del(entityID);
> 可以实现用户池。稍后将介绍这一功能。
## 掩码
用于根据组件的存在与否来过滤实体。
用于根据组件的存在与否来过滤实体。通常掩码不单独使用,而是作为 `EcsAspect` 的一部分,用于查询时过滤实体。
``` c#
// 创建一个掩码,检查实体是否具有组件
// SomeCmp1 和 SomeCmp2但没有组件 SomeCmp3。
@ -472,7 +499,11 @@ EcsMask mask = _staticMask.ToMask(_world);
</details>
## 方面
这些是继承自 EcsAspect 的用户类,用于与实体进行交互。方面同时充当池的缓存和实体组件的过滤掩码。可以把方面视为系统处理哪些实体的描述。
方面是继承自 `EcsAspect` 的用户自定义类,用于描述系统要处理的一组组件。方面同时承担两个职责:
- 掩码 — 初始化并保存一个 `EcsMask`,使其可在查询中用于过滤实体。
- 池缓存 — 提供对组件池的快速访问。
简而言之,方面是描述“我处理哪些实体以及如何访问它们的组件”的便捷方式。
简化语法:
``` c#
@ -503,9 +534,9 @@ class Aspect : EcsAspect
public EcsPool<Velocity> velocities;
protected override void Init(Builder b)
{
poses = b.Include<Pose>();
velocities = b.Include<Velocity>();
b.Exclude<FreezedTag>();
poses = b.Inc<Pose>();
velocities = b.Inc<Velocity>();
b.Exc<FreezedTag>();
}
}
```
@ -529,13 +560,13 @@ class Aspect : EcsAspect
otherAspect1 = b.Combine<OtherAspect1>(1);
// 即使对 OtherAspect1 调用 Combine 方法更早Aspect 会首先与 OtherAspect2 进行组合,因为默认情况下 order = 0。
otherAspect2 = b.Combine<OtherAspect2>();
// 如果 OtherAspect1 或 OtherAspect2 中有 b.Exclude<Pose>() 的限制条件,这里将被替换为 b.Include<Pose>()。
poses = b.Include<Pose>();
// 如果 OtherAspect1 或 OtherAspect2 中有 b.Exc<Pose>() 的限制条件,这里将被替换为 b.Inc<Pose>().
poses = b.Inc<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: |
@ -765,7 +796,7 @@ public class EcsRoot
# Debug
该框架提供了额外的调试和日志记录工具,不依赖于环境此外,许多类型都有自己的 DebuggerProxy以便在 IDE 中更详细地显示信息。
## 元属性
## Meta Attributes
默认情况下,元属性没有用处,在与引擎集成时用于指定在调试工具和编辑器中的显示方式。还可以用于生成自动文档。
``` c#
using DCFApixels.DragonECS;
@ -804,6 +835,40 @@ 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 即可。
@ -963,7 +1028,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/c593dba7-eeaa-4706-a043-946f132f3f83" alt="screenshot">
<img src="https://github.com/user-attachments/assets/6aba814d-a70b-432f-a905-84d1b6872581" />
</a>
</td>
<td align="center">
@ -973,11 +1038,43 @@ 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">
@ -986,46 +1083,38 @@ 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
## '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`指令.
## 如何启用/禁用系统?
直接——不能。
通常当游戏的整体状态发生变化时会需要启用或禁用系统有时需要切换一组系统。从概念上讲这可以视为流程process的变更。这里有两种解决方案
- 如果流程变更是全局性的,可以创建一个新的 `EcsPipeline`并在引擎的更新循环中运行相应的管线pipeline
- 将 `IEcsRun` 拆分为多个流程,并在引擎更新循环中运行所需的流程。为此,创建一个新的流程接口,为其实现一个 Runner并通过 `EcsPipeline.GetRunner<T>()` 获取该 Runner。
## 建议清单: [DragonECS-Vault](https://github.com/DCFApixels/DragonECS-Vault)
</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>

434
README.md
View File

@ -22,38 +22,33 @@
<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>
</br>
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).
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)
> [!Note]
> Have questions or want to stay updated on development? Join our community! -> [Feedback](#Feedback)
## Table of Contents
- [Installation](#installation)
- [Extensions](#extensions)
- [Basic Concepts](#basic-concepts)
- [Entity](#entity)
- [Component](#component)
@ -84,39 +79,62 @@ 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/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
Versioning semantics - [Open](https://gist.github.com/DCFApixels/af79284955bf40e9476cdcac79d7b098#file-dcfapixels_versioning-md)
## Environment
Requirements:
+ Minimum version of C# 7.3;
* Minimum C# version: 7.3.
Optional:
+ Support for NativeAOT
+ Game engines with C#: Unity, Godot, MonoGame, etc.
Supported:
* NativeAOT;
* Game engines with C#: Unity, Godot, MonoGame, etc.
Tested with:
+ **Unity:** Minimum version 2020.1.0;
* **Unity:** Minimum version 2021.2.0.
## Unity Installation
* ### 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`:
> 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):
```
https://github.com/DCFApixels/DragonECS.git
```
* ### Source Code
The framework can also be added to the project as source code.
Or add the package entry to `Packages/manifest.json`:
```
"com.dcfa_pixels.dragonecs": "https://github.com/DCFApixels/DragonECS.git",
```
</br>
* ### 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).
# Basic Concepts
## Entity
С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#
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#
// Creating a new entity in the world.
int entityID = _world.NewEntity();
@ -133,7 +151,7 @@ int newEntityID = _world.CloneEntity(entityID);
<details>
<summary>Working with entlong</summary>
``` c#
```c#
// Convert int to entlong.
entlong entity = _world.GetEntityLong(entityID);
// or
@ -153,7 +171,7 @@ if (entity.TryGetID(out int entityID)) { }
</details>
> Entities cannot exist without components, empty entities will be automatically deleted immediately after the last component is deleted.
> Entities cannot exist without components. Removing the last component automatically deletes the entity along with it.
## Component
Data for entities.
@ -169,7 +187,7 @@ struct PlayerTag : IEcsTagComponent {}
```
## System
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:
Represents the core logic defining entity behaviors. Systems are implemented as user-defined classes that implement one or more process interfaces. Key processes include:
```c#
class SomeSystem : IEcsPreInit, IEcsInit, IEcsRun, IEcsDestroy
{
@ -192,11 +210,11 @@ class SomeSystem : IEcsPreInit, IEcsInit, IEcsRun, IEcsDestroy
# Framework Concepts
## Pipeline
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.
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.
### Building
Builder is responsible for building the pipeline. Systems are added to the Builder and at the end, the pipeline is built. Example:
Builder is responsible for building the pipeline. Systems are added to the Builder, and the pipeline is built at the end. Example:
```c#
EcsPipeline pipeline = EcsPipeline.New() // Создает Builder пайплайна.
EcsPipeline pipeline = EcsPipeline.New() // Creates a pipeline builder.
// Adds System1 to the systems queue.
.Add(new System1())
// Adds System2 to the queue after System1.
@ -217,12 +235,12 @@ class SomeSystem : IEcsRun, IEcsPipelineMember
public void Run () { }
}
```
> For simultaneous building and initialization, there is the method `Builder.BuildAndInit();`
> For simultaneous building and initialization, use `Builder.BuildAndInit()`.
### Dependency Injection
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#
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#
class SomeDataA { /* ... */ }
class SomeDataB : SomeDataA { /* ... */ }
@ -244,7 +262,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;
@ -256,8 +274,8 @@ class SomeSystem : IEcsInject<SomeDataA>, IEcsRun
```
### Modules
Groups of systems that implement a common feature can be grouped into modules and easily added to the Pipeline.
``` c#
Groups of systems that implement a common feature can be grouped into modules and added to the Pipeline.
```c#
using DCFApixels.DragonECS;
class Module1 : IEcsModule
{
@ -279,62 +297,64 @@ EcsPipeline pipeline = EcsPipeline.New()
```
### Sorting
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.
To manage system order in the pipeline regardless of addition order, use Layers and Sorting Order.
#### Layers
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.
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.
``` c#
const string SOME_LAYER = nameof(SOME_LAYER);
EcsPipeline pipeline = EcsPipeline.New()
// ...
// Inserts a new layer before the end layer EcsConsts.END_LAYER
.Layers.Insert(EcsConsts.END_LAYER, SOME_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)
// 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:
* `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`
* `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`
#### Sorting Order
The sort order int value is used to sort systems within a layer. By default, systems are added with `sortOrder = 0`.
The sort order integer 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. It is possible to implement custom 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.
<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 the completion of initialization injection.
* `IOnInitInjectionComplete` - Similar to the [Dependency Injection](#Dependency-Injection) process, but signals completion of initialization injection.
</details>
<details>
<summary>Custom Processes</summary>
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:
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:
``` c#
// Process interface.
interface IDoSomethingProcess : IEcsProcess
{
void Do();
}
// Implementation of a runner. An example of implementation can also be seen in built-in processes.
// Runner implementation. An example of implementation can also be seen in built-in processes.
sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>, IDoSomethingProcess
{
public void Do()
@ -352,10 +372,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>
@ -367,10 +387,9 @@ 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()
@ -382,17 +401,15 @@ internal sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>,
</details>
> Runners have several implementation requirements:
> Runner requirements:
> * Inheritance from `EcsRunner<T>` must be direct.
> * 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.
> * A runner may only contain one interface (excluding `IEcsProcess`).
> * The inheriting class of `EcsRunner<T>` must also implement the `T` interface.
</details>
## World
Is a container for entities and components.
A container for entities and components.
``` c#
// Creating an instance of the world.
_world = new EcsDefaultWorld();
@ -400,10 +417,10 @@ _world = new EcsDefaultWorld();
var e = _world.NewEntity();
_world.DelEntity(e);
```
> **NOTICE:** It's necessary to call EcsWorld.Destroy() on the world instance when it's no longer needed, otherwise it will remain in memory.
> **NOTICE:** Call `EcsWorld.Destroy()` on the world instance when it is no longer needed to release resources. Otherwise it will remain in memory.
### World Configuration
To initialize the world with a required size upfront and reduce warm-up time, you can pass an `EcsWorldConfig` instance to the constructor.
To initialize the world with a required size upfront and reduce warm-up time, pass an `EcsWorldConfig` instance to the constructor.
``` c#
EcsWorldConfig config = new EcsWorldConfig(
@ -415,11 +432,11 @@ _world = new EcsDefaultWorld(config);
```
## Pool
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`;
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`;
Pools have 5 main methods and their variations:
Pools provide 5 common methods and their variations:
``` c#
// One way to get a pool from the world.
EcsPool<Pose> poses = _world.GetPool<Pose>();
@ -427,8 +444,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 exist component, throws an exception if the entity does not have this component.
ref var gettedPose = ref poses.Get(entityID);
// Returns existing component, throws an exception if the entity does not have this component.
ref var gottenPose = 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);
@ -439,12 +456,33 @@ if (poses.Has(entityID)) { /* ... */ }
// Removes component from entity, throws an exception if the entity does not have this component.
poses.Del(entityID);
```
> There are "safe" methods that first perform a check for the presence or absence of a component. Such methods are prefixed with `Try`.
> [!WARNING]
> Exceptions are disabled in the `Release` build.
> It is possible to implement a user pool. This feature will be described shortly.
> 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>
## Mask
Used to filter entities by the presence or absence of components.
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.
``` c#
// Creating a mask that checks if entities have components
// SomeCmp1 and SomeCmp2, but do not have component SomeCmp3.
@ -460,7 +498,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)`, but there is also `EcsStaticMask`, which can be created without being tied to a world.
`EcsMask` is tied to specific world instances, which need to be passed to `EcsMask.New(world)`. `EcsStaticMask` can be created without a world reference.
``` c#
class SomeSystem : IEcsRun
@ -479,7 +517,11 @@ EcsMask mask = _staticMask.ToMask(_world);
</details>
## Aspect
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.
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."
Simplified syntax:
``` c#
@ -496,11 +538,20 @@ class Aspect : EcsAspect
// During queries, it checks for the presence of components
// in the inclusive constraint and absence in the exclusive constraint.
// There is also Opt - it only caches the pool without affecting the mask.
// Opt only caches the pool without affecting the mask.
}
```
Explicit syntax (the result is identical to the example above):
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):
``` c#
using DCFApixels.DragonECS;
// ...
@ -510,17 +561,19 @@ class Aspect : EcsAspect
public EcsPool<Velocity> velocities;
protected override void Init(Builder b)
{
poses = b.Include<Pose>();
velocities = b.Include<Velocity>();
b.Exclude<FreezedTag>();
poses = b.Inc<Pose>();
velocities = b.Inc<Velocity>();
b.Exc<FreezedTag>();
}
}
```
</details>
<details>
<summary>Combining aspects</summary>
Aspects can have additional aspects added to them, thus combining them. The constraints will also be combined accordingly.
Aspects can be combined by adding other aspects; constraints are merged accordingly.
``` c#
using DCFApixels.DragonECS;
// ...
@ -532,31 +585,32 @@ class Aspect : EcsAspect
protected override void Init(Builder b)
{
// Combines with SomeAspect1.
// Combines with OtherAspect1.
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.Exclude<Pose>() was specified in OtherAspect1 or OtherAspect2, it will be replaced with b.Include<Pose>() here.
poses = b.Include<Pose>();
// If b.Exc<Pose>() was specified in OtherAspect1 or OtherAspect2, it will be replaced with b.Inc<Pose>() here.
poses = b.Inc<Pose>();
}
}
```
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 | разрешение конфликтных ограничений|
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 |
| :--- | :--- | :--- | :--- | :--- | :--- |:--- |
| 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` 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: |
| 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. |
| Final Constraints | :x: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_check_mark: | |
</details>
## Queries
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;
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;
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>`.
`Where` can be applied to both `EcsWorld` and framework collections (similar to LINQ). Overloads for sorting using `Comparison<int>` are available.
Example system:
``` c#
@ -597,7 +651,7 @@ public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
## Collections
### EcsSpan
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>`.
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>`.
``` c#
// Where query returns entities as EcsSpan.
EcsSpan es = _world.Where(out Aspect a);
@ -612,13 +666,13 @@ for (int i = 0; i < es.Count; i++)
// ...
}
```
> Although `EcsSpan` is just an array, it does not allow duplicate entities.
> Although `EcsSpan` is an array reference, duplicate entities are not allowed.
### 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 pool of groups,
// so a new one will be created or a free one will be reused.
// Getting a new group. EcsWorld contains a pool of groups,
// a new one will be created or a free one reused.
EcsGroup group = EcsGroup.New(_world);
// Release the group.
group.Dispose();
@ -645,32 +699,36 @@ for (int i = 0; i < group.Count; i++)
// ...
}
```
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.
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.
``` 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
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.
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.
### Example for Unity
``` c#
using DCFApixels.DragonECS;
@ -724,7 +782,7 @@ public class EcsRoot
{
private EcsPipeline _pipeline;
private EcsDefaultWorld _world;
// Engine initialization .
// Engine initialization.
public void Init()
{
// Creating world for entities and components.
@ -736,7 +794,7 @@ public class EcsRoot
// .Add(new SomeSystem2())
// .Add(new SomeSystem3())
// Внедрение мира в системы.
// Injecting world into systems.
.Inject(_world)
// Other injections.
// .Inject(SomeData)
@ -771,7 +829,7 @@ public class EcsRoot
</br>
# Debug
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.
The framework provides tools for debugging and logging, independent of the environment. Many types have DebuggerProxy implementations for more informative display in IDEs.
## 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#
@ -809,12 +867,47 @@ var description = typeMeta.Description; // [MetaDescription]
var metaID = typeMeta.MetaID; // [MetaID]
var tags = typeMeta.Tags; // [MetaTags]
```
> To automatically generate unique identifiers MetaID, there is the method `MetaID.GenerateNewUniqueID()` and the [Browser Generator](https://dcfapixels.github.io/DragonECS-MetaID_Generator_Online/).
> 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>
## EcsDebug
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.
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.
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.
By default, `DefaultDebugService` outputs logs to the console. To implement a custom service, inherit from `DebugService` and implement its abstract members.
``` c#
// Output log.
@ -853,26 +946,25 @@ 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, 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_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_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` - 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.
+ `REFLECTION_DISABLED` - Restricts the framework's use of Reflection.
+ `DISABLE_DEBUG` - For environments where manual DEBUG disabling is not supported (e.g., Unity).
</br>
# Framework Extension Tools
There are additional tools for greater extensibility of the framework.
Additional tools improve framework extensibility.
## Configs
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>
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.
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>();
@ -880,7 +972,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(/* ... */))
// ...
@ -891,20 +983,20 @@ var _someDataB = _pipeline.Configs.Get<SomeDataB>();
```
## World Components
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.
World components attach additional data to worlds. World components are `struct` types. Access via `Get` is optimized and performs similarly to class field access.
Get component:
``` c#
ref WorldComponent component = ref _world.Get<WorldComponent>();
```
Component Implementation:
Component implementation:
``` c#
public struct WorldComponent
{
// Data.
}
```
Or:
Or with lifecycle callbacks:
``` c#
public struct WorldComponent : IEcsWorldComponent<WorldComponent>
{
@ -917,7 +1009,7 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
void IEcsWorldComponent<WorldComponent>.OnDestroy(ref WorldComponent component, EcsWorld world)
{
// Actions when EcsWorld.Destroy is called.
// Calling OnDestroy, obliges the user to manually reset the component if necessary.
// OnDestroy requires manual reset of the component if necessary.
component = default;
}
}
@ -926,12 +1018,12 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
<details>
<summary>Example of use</summary>
IEcsWorldComponent<T> interface events, can be used to automatically initialize component fields, and release resources.
The `IEcsWorldComponent<T>` interface events can be used to initialize component fields and release resources automatically.
``` c#
public struct WorldComponent : IEcsWorldComponent<WorldComponent>
{
private SomeClass _object; // Объект который будет утилизироваться.
private SomeReusedClass _reusedObject; // Объект который будет переиспользоваться.
private SomeClass _object; // Object to be disposed.
private SomeReusedClass _reusedObject; // Object to be reused.
public SomeClass Object => _object;
public SomeReusedClass ReusedObject => _reusedObject;
void IEcsWorldComponent<WorldComponent>.Init(ref WorldComponent component, EcsWorld world)
@ -939,18 +1031,16 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
if (component._reusedObject == null)
component._reusedObject = new SomeReusedClass();
component._object = new SomeClass();
// Теперь при получении компонента через EcsWorld.Get, _reusedObject и _object уже будут созданы.
// After getting the component via EcsWorld.Get, _reusedObject and _object will be ready.
}
void IEcsWorldComponent<WorldComponent>.OnDestroy(ref WorldComponent component, EcsWorld world)
{
// Утилизируем не нужный объект, и освобождаем ссылку на него, чтобы GC мог его собрать.
// Dispose and clear the reference so GC can collect it.
component._object.Dispose();
component._object = null;
// Как вариант тут можно сделать сброс значений у переиспользуемого объекта.
//component._reusedObject.Reset();
// Так как в этом примере не нужно полное обнуление компонента, то строчка ниже не нужна.
// Optionally reset the reused object.
// component._reusedObject.Reset();
// If full reset is needed, uncomment the line below.
// component = default;
}
}
@ -969,7 +1059,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/c593dba7-eeaa-4706-a043-946f132f3f83" alt="screenshot">
<img src="https://github.com/user-attachments/assets/6aba814d-a70b-432f-a905-84d1b6872581" />
</a>
</td>
<td align="center">
@ -979,11 +1069,43 @@ 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">
@ -992,39 +1114,26 @@ 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
## '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`.
## 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)
</br>
@ -1032,6 +1141,11 @@ To fix this, add the define symbol `ENABLE_DUMMY_SPAN` to `Project Settings/Play
+ 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": "2020.3",
"version": "0.9.21",
"unity": "2021.2",
"version": "1.0.1",
"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.PoolsCore;
using DCFApixels.DragonECS.Core;
namespace DCFApixels.DragonECS
{

View File

@ -1,7 +1,6 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.RunnersCore;
using System;
namespace DCFApixels.DragonECS
@ -60,7 +59,6 @@ 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
@ -81,7 +79,6 @@ 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
@ -102,7 +99,6 @@ 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
@ -183,7 +179,6 @@ 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).As<int>();
return MemoryAllocator.AllocAndInit<int>(EcsGroup.PAGE_SIZE).Ptr;
}
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.IsEmpty == false)
if (page.IsCreated)
{
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).As<int>();
internal static readonly int* _nullPage = MemoryAllocator.AllocAndInit<int>(PageSlot.SIZE).Ptr;
internal static readonly long _nullPagePtrFake = (long)_nullPage;
#region Properties
@ -344,17 +344,6 @@ 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
@ -394,7 +383,7 @@ namespace DCFApixels.DragonECS
page.IndexesXOR = 0;
page.Count = 0;
}
_sparsePagesHandler.Dispose();
_sparsePagesHandler.DisposeAndReset();
}
}
public void Dispose()
@ -555,7 +544,6 @@ 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,6 +1,7 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.Core.Unchecked;
using System;
@ -50,7 +51,7 @@ namespace DCFApixels.DragonECS
public bool IsSourceEntities
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return this == EcsWorld.GetWorld(_worldID).GetCurrentEntities_Internal(); }
get { return _values == EcsWorld.GetWorld(_worldID).GetCurrentEntities_Internal()._values; }
}
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.ArrayBoundsChecks, true)]
@ -117,8 +118,8 @@ namespace DCFApixels.DragonECS
#endregion
#region operators
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; }
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; }
#endregion
#region Enumerator
@ -127,6 +128,7 @@ namespace DCFApixels.DragonECS
#endregion
#region Other
public ReadOnlySpan<int> AsSystemSpan() { return _values; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int First() { return _values[0]; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -170,6 +172,7 @@ namespace DCFApixels.DragonECS
_worldID = span._worldID;
}
public DebuggerProxy(EcsLongsSpan span) : this(span.ToSpan()) { }
public DebuggerProxy(EcsUnsafeSpan span) : this(span.ToSpan()) { }
}
#endregion
}
@ -317,3 +320,165 @@ namespace DCFApixels.DragonECS
#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,6 +1,4 @@
using System;
namespace DCFApixels.DragonECS
namespace DCFApixels.DragonECS
{
public static class EcsConsts
{
@ -88,42 +86,6 @@ namespace DCFApixels.DragonECS
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,63 +11,84 @@ namespace DCFApixels.DragonECS.Core
void Init(ref T component, EcsWorld world);
void OnDestroy(ref T component, EcsWorld world);
}
public static class EcsWorldComponentHandler<T>
public static class EcsWorldComponent<T>
{
public static readonly IEcsWorldComponent<T> instance;
public static readonly bool isHasHandler;
static EcsWorldComponentHandler()
public static readonly IEcsWorldComponent<T> CustomHandler;
public static readonly bool IsCustom;
static EcsWorldComponent()
{
T def = default;
if (def is IEcsWorldComponent<T> intrf)
T raw = default;
if (raw is IEcsWorldComponent<T> handler)
{
isHasHandler = true;
instance = intrf;
IsCustom = true;
CustomHandler = handler;
}
else
{
isHasHandler = false;
instance = new DummyHandler();
IsCustom = false;
CustomHandler = new DummyHandler();
}
}
private class DummyHandler : IEcsWorldComponent<T>
private sealed 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 IEcsComponentReset
#region IEcsComponentLifecycle
public interface IEcsComponentLifecycle<T>
{
void Enable(ref T component);
void Disable(ref T component);
void OnAdd(ref T component, short worldID, int entityID);
void OnDel(ref T component, short worldID, int entityID);
}
public static class EcsComponentLifecycleHandler<T>
public static class EcsComponentLifecycle<T> where T : struct
{
public static readonly IEcsComponentLifecycle<T> instance;
public static readonly bool isHasHandler;
static EcsComponentLifecycleHandler()
public static readonly IEcsComponentLifecycle<T> CustomHandler;
public static readonly bool IsCustom;
static EcsComponentLifecycle()
{
T def = default;
if (def is IEcsComponentLifecycle<T> intrf)
T raw = default;
if (raw is IEcsComponentLifecycle<T> handler)
{
isHasHandler = true;
instance = intrf;
IsCustom = true;
CustomHandler = handler;
}
else
{
isHasHandler = false;
instance = new DummyHandler();
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;
}
}
private sealed class DummyHandler : IEcsComponentLifecycle<T>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Enable(ref T component) { component = default; }
public void OnAdd(ref T component, short worldID, int entityID) { component = default; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Disable(ref T component) { component = default; }
public void OnDel(ref T component, short worldID, int entityID) { component = default; }
}
}
#endregion
@ -77,22 +98,34 @@ namespace DCFApixels.DragonECS.Core
{
void Copy(ref T from, ref T to);
}
public static class EcsComponentCopyHandler<T>
public static class EcsComponentCopy<T> where T : struct
{
public static readonly IEcsComponentCopy<T> instance;
public static readonly bool isHasHandler;
static EcsComponentCopyHandler()
public static readonly IEcsComponentCopy<T> CustomHandler;
public static readonly bool IsCustom;
static EcsComponentCopy()
{
T def = default;
if (def is IEcsComponentCopy<T> intrf)
T raw = default;
if (raw is IEcsComponentCopy<T> handler)
{
isHasHandler = true;
instance = intrf;
IsCustom = true;
CustomHandler = handler;
}
else
{
isHasHandler = false;
instance = new DummyHandler();
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;
}
}
private sealed class DummyHandler : IEcsComponentCopy<T>

View File

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

View File

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

View File

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

View File

@ -0,0 +1,304 @@
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

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

View File

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

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: eb8cc656a6e80f843b8794af9f63faa8
guid: fd6afd87df377e5408428ccec3c02685
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 : EcsMetaAttribute, IMetaColor
public sealed class MetaColorAttribute : DragonMetaAttribute, 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 : EcsMetaAttribute
public sealed class MetaDescriptionAttribute : DragonMetaAttribute
{
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 : EcsMetaAttribute
public sealed class MetaGroupAttribute : DragonMetaAttribute
{
public const char SEPARATOR = MetaGroup.SEPARATOR;
public readonly string Name = string.Empty;
@ -73,6 +73,10 @@ 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 : EcsMetaAttribute
public sealed class MetaIDAttribute : DragonMetaAttribute
{
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 : EcsMetaAttribute
public sealed class MetaNameAttribute : DragonMetaAttribute
{
public readonly string name;
public readonly bool isHideGeneric;

View File

@ -0,0 +1,31 @@
#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

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

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 : EcsMetaAttribute
public sealed class MetaTagsAttribute : DragonMetaAttribute
{
public const char SEPARATOR = ',';
private readonly string[] _tags = Array.Empty<string>();

View File

@ -1,15 +1,17 @@
#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 DEBUG || !REFLECTION_DISABLED
#if REFLECTION_ENABLED
using System.Reflection;
#endif
@ -46,11 +48,13 @@ namespace DCFApixels.DragonECS
public static readonly TypeMeta NullTypeMeta;
private static readonly object _lock = new object();
private static readonly Dictionary<Type, TypeMeta> _metaCache = new Dictionary<Type, TypeMeta>();
private static readonly Dictionary<RuntimeTypeHandle, TypeMeta> _metaCache = new Dictionary<RuntimeTypeHandle, 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;
@ -93,26 +97,70 @@ namespace DCFApixels.DragonECS
_initFlags = InitFlag.All,
};
_metaCache.Add(typeof(void), NullTypeMeta);
_metaCache.Add(typeof(void).TypeHandle, NullTypeMeta);
}
public static TypeMeta Get(Type type)
public static TypeMeta Get(Type type) { return Get(type.TypeHandle); }
public static TypeMeta Get(RuntimeTypeHandle typeHandle)
{
lock (_lock) //TODO посмотреть можно ли тут убрать лок
lock (_lock)
{
if (_metaCache.TryGetValue(type, out TypeMeta result) == false)
if (_metaCache.TryGetValue(typeHandle, out TypeMeta result) == false)
{
result = new TypeMeta(type);
_metaCache.Add(type, result);
result = new TypeMeta(Type.GetTypeFromHandle(typeHandle));
_metaCache.Add(typeHandle, 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
@ -126,7 +174,7 @@ namespace DCFApixels.DragonECS
{
if (_initFlags.HasFlag(InitFlag.Name) == false)
{
(_name, _isCustomName) = MetaGenerator.GetMetaName(_type);
(_name, _isCustomName) = MetaGenerator.GetMetaName(this);
_typeName = _isCustomName ? MetaGenerator.GetTypeName(_type) : _name;
_initFlags |= InitFlag.Name;
}
@ -191,7 +239,7 @@ namespace DCFApixels.DragonECS
{
if (_initFlags.HasFlag(InitFlag.Description) == false)
{
_description = MetaGenerator.GetDescription(_type);
_description = MetaGenerator.GetDescription(this);
_initFlags |= InitFlag.Description;
}
return _description;
@ -206,7 +254,7 @@ namespace DCFApixels.DragonECS
{
if (_initFlags.HasFlag(InitFlag.Group) == false)
{
_group = MetaGenerator.GetGroup(_type);
_group = MetaGenerator.GetGroup(this);
_initFlags |= InitFlag.Group;
}
return _group;
@ -219,7 +267,7 @@ namespace DCFApixels.DragonECS
{
if (_initFlags.HasFlag(InitFlag.Tags) == false)
{
_tags = MetaGenerator.GetTags(_type);
_tags = MetaGenerator.GetTags(this);
_initFlags |= InitFlag.Tags;
_isHidden = _tags.Contains(MetaTags.HIDDEN);
_isObsolete = _tags.Contains(MetaTags.OBSOLETE);
@ -375,7 +423,7 @@ namespace DCFApixels.DragonECS
lock (_lock)
{
_metaCache.Clear();
_metaCache.Add(typeof(void), NullTypeMeta);
_metaCache.Add(typeof(void).TypeHandle, NullTypeMeta);
}
}
ITypeMeta ITypeMeta.BaseMeta
@ -384,7 +432,7 @@ namespace DCFApixels.DragonECS
}
private static bool CheckEcsMemener(Type checkedType)
{
#if DEBUG || !REFLECTION_DISABLED
#if REFLECTION_ENABLED
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.");
@ -395,7 +443,7 @@ namespace DCFApixels.DragonECS
{
if (IsHasCustomMeta(type))
{
meta = type.ToMeta();
meta = type.GetMeta();
return true;
}
meta = null;
@ -403,16 +451,16 @@ namespace DCFApixels.DragonECS
}
public static bool IsHasCustomMeta(Type type)
{
#if DEBUG || !REFLECTION_DISABLED
return CheckEcsMemener(type) || Attribute.GetCustomAttributes(type, typeof(EcsMetaAttribute), false).Length > 0;
#if REFLECTION_ENABLED
return CheckEcsMemener(type) || Attribute.GetCustomAttributes(type, typeof(DragonMetaAttribute), false).Length > 0;
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(TypeMeta)}.{nameof(IsHasMeta)} method does not work.");
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(TypeMeta)}.{nameof(IsHasCustomMeta)} method does not work.");
return false;
#endif
}
public static bool IsHasMetaID(Type type)
{
#if DEBUG || !REFLECTION_DISABLED
#if REFLECTION_ENABLED
return TryGetCustomMeta(type, out TypeMeta meta) && meta.IsHasMetaID();
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(TypeMeta)}.{nameof(IsHasMetaID)} method does not work.");
@ -469,15 +517,6 @@ 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
{
@ -488,15 +527,20 @@ namespace DCFApixels.DragonECS
{
return EcsDebugUtility.GetGenericTypeName(type, GENERIC_NAME_DEPTH);
}
public static (string, bool) GetMetaName(Type type)
public static (string, bool) GetMetaName(TypeMeta meta)
{
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
if (meta._isSelfProxy && meta._proxy.Name != null)
{
return (meta._proxy.Name, true);
}
var type = meta.Type;
bool isCustom = type.TryGetAttribute(out MetaNameAttribute atr) && string.IsNullOrEmpty(atr.name) == false;
if (isCustom)
{
if ((type.IsGenericType && atr.isHideGeneric == false) == false)
{
return (atr.name, isCustom);
return (atr.name, true);
}
string genericParams = "";
Type[] typeParameters = type.GetGenericArguments();
@ -505,12 +549,16 @@ namespace DCFApixels.DragonECS
string paramTypeName = EcsDebugUtility.GetGenericTypeName(typeParameters[i], GENERIC_NAME_DEPTH);
genericParams += (i == 0 ? paramTypeName : $", {paramTypeName}");
}
return ($"{atr.name}<{genericParams}>", isCustom);
return ($"{atr.name}<{genericParams}>", true);
}
return (EcsDebugUtility.GetGenericTypeName(type, GENERIC_NAME_DEPTH), isCustom);
if (meta._proxy.Name != null)
{
return (meta._proxy.Name, true);
}
return (EcsDebugUtility.GetGenericTypeName(type, GENERIC_NAME_DEPTH), false);
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetMetaName)} method does not work.");
return (type.Name, false);
return (meta.Type.Name, false);
#endif
}
#endregion
@ -531,9 +579,21 @@ namespace DCFApixels.DragonECS
}
public static (MetaColor, bool) GetColor(TypeMeta meta)
{
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
if (meta._isSelfProxy && meta._proxy.Color != null)
{
return (meta._proxy.Color.Value, true);
}
bool isCustom = meta.Type.TryGetAttribute(out MetaColorAttribute atr);
return (isCustom ? atr.color : AutoColor(meta), isCustom);
if (isCustom)
{
return (atr.color, true);
}
if (meta._proxy.Color != null)
{
return (meta._proxy.Color.Value, true);
}
return (AutoColor(meta), false);
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetColor)} method does not work.");
return (MetaColor.White, false);
@ -542,17 +602,22 @@ namespace DCFApixels.DragonECS
#endregion
#region GetGroup
public static MetaGroup GetGroup(Type type)
public static MetaGroup GetGroup(TypeMeta meta)
{
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
if (type.TryGetAttribute(out MetaGroupAttribute atr))
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
if (meta._isSelfProxy && meta._proxy.Group != null)
{
return meta._proxy.Group;
}
if (meta.Type.TryGetAttribute(out MetaGroupAttribute atr))
{
return MetaGroup.FromName(atr.Name);
}
else
if (meta._proxy.Group != null)
{
return MetaGroup.FromNameSpace(type);
return meta._proxy.Group;
}
return MetaGroup.FromNameSpace(meta.Type);
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetGroup)} method does not work.");
return MetaGroup.Empty;
@ -561,11 +626,22 @@ namespace DCFApixels.DragonECS
#endregion
#region GetDescription
public static MetaDescription GetDescription(Type type)
public static MetaDescription GetDescription(TypeMeta meta)
{
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
bool isCustom = type.TryGetAttribute(out MetaDescriptionAttribute atr);
return isCustom ? atr.Data : MetaDescription.Empty;
#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;
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetDescription)} method does not work.");
return MetaDescription.Empty;
@ -574,11 +650,22 @@ namespace DCFApixels.DragonECS
#endregion
#region GetTags
public static IReadOnlyList<string> GetTags(Type type)
public static IReadOnlyList<string> GetTags(TypeMeta meta)
{
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
var atr = type.GetCustomAttribute<MetaTagsAttribute>();
return atr != null ? atr.Tags : Array.Empty<string>();
#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>();
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetTags)} method does not work.");
return Array.Empty<string>();
@ -589,7 +676,7 @@ namespace DCFApixels.DragonECS
#region GetMetaID
public static string GetMetaID(Type type)
{
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
var atr = type.GetCustomAttribute<MetaIDAttribute>();
if (atr == null)

View File

@ -3,7 +3,6 @@
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.PoolsCore;
using System;
using System.Collections.Generic;
@ -418,52 +417,6 @@ 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,7 +5,6 @@ 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;
@ -34,6 +33,7 @@ namespace DCFApixels.DragonECS
{
public readonly int ID;
public readonly short WorldID;
public readonly EcsWorld World;
internal readonly EcsStaticMask _staticMask;
internal readonly EcsMaskChunck[] _incChunckMasks;
@ -51,11 +51,6 @@ 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
{
@ -74,6 +69,25 @@ 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)]
@ -116,6 +130,7 @@ namespace DCFApixels.DragonECS
_staticMask = staticMask;
ID = id;
WorldID = worldID;
World = EcsWorld.GetWorld(worldID);
_flags = staticMask.Flags;
EcsWorld world = EcsWorld.GetWorld(worldID);
@ -345,8 +360,6 @@ 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);
@ -367,24 +380,82 @@ 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
@ -402,15 +473,18 @@ namespace DCFApixels.DragonECS
public readonly int ID;
public readonly EcsWorld world;
private readonly short _worldID;
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 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 bool IsEmpty { get { return _source.IsEmpty; } }
public bool IsBroken { get { return _source.IsBroken; } }
@ -422,51 +496,26 @@ namespace DCFApixels.DragonECS
ID = mask.ID;
world = EcsWorld.GetWorld(mask.WorldID);
_worldID = mask.WorldID;
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();
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();
}
public override string ToString()
{
return CreateLogString(_worldID, included, excluded, any);
return CreateLogString(_worldID, incs, excs, anys);
}
}
#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]
@ -607,8 +656,8 @@ namespace DCFApixels.DragonECS
}
private void Cleanup(bool disposing)
{
_bufferHandler.Dispose();
_chunckBufferHandler.Dispose();
_bufferHandler.DisposeAndReset();
_chunckBufferHandler.DisposeAndReset();
}
#endregion
@ -636,8 +685,7 @@ namespace DCFApixels.DragonECS
if (_sortIncChunckBuffer.Length > 1)
{
var comparer = new IncCountComparer(counts);
UnsafeArraySortHalperX<int>.InsertionSort(sortIncBuffer.ptr, sortIncBuffer.Length, ref comparer);
SortHalper.Sort(sortIncBuffer.AsSpan(), new IncCountComparer(counts));
ConvertToChuncks(preSortingBuffer, sortIncBuffer, _sortIncChunckBuffer);
}
if (_sortIncChunckBuffer.Length > 0)
@ -651,8 +699,7 @@ namespace DCFApixels.DragonECS
if (_sortExcChunckBuffer.Length > 1)
{
ExcCountComparer comparer = new ExcCountComparer(counts);
UnsafeArraySortHalperX<int>.InsertionSort(sortExcBuffer.ptr, sortExcBuffer.Length, ref comparer);
SortHalper.Sort(sortExcBuffer.AsSpan(), new ExcCountComparer(counts));
ConvertToChuncks(preSortingBuffer, sortExcBuffer, _sortExcChunckBuffer);
}
// Выражение IncCount < (AllEntitesCount - ExcCount) мало вероятно будет истинным.
@ -662,8 +709,7 @@ namespace DCFApixels.DragonECS
if (_sortAnyChunckBuffer.Length > 1)
{
ExcCountComparer comparer = new ExcCountComparer(counts);
UnsafeArraySortHalperX<int>.InsertionSort(sortAnyBuffer.ptr, sortAnyBuffer.Length, ref comparer);
SortHalper.Sort(sortAnyBuffer.AsSpan(), new ExcCountComparer(counts));
ConvertToChuncks(preSortingBuffer, sortAnyBuffer, _sortAnyChunckBuffer);
}
// Any не влияет на maxEntites если есть Inc и сложно высчитывается если нет Inc
@ -691,9 +737,13 @@ namespace DCFApixels.DragonECS
#endregion
#region IterateTo
//TODO Перемеиноваться в CacheTo
public EcsMaskFlags MaskFlags
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _maskFlags; }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void IterateTo(EcsSpan source, EcsGroup group)
public void CacheTo(EcsSpan source, EcsGroup group)
{
switch (_maskFlags)
{
@ -701,7 +751,7 @@ namespace DCFApixels.DragonECS
group.CopyFrom(source);
break;
case EcsMaskFlags.Inc:
IterateOnlyInc(source).CopyTo(group);
IterateOnlyInc(source).CacheTo(group);
break;
case EcsMaskFlags.Exc:
case EcsMaskFlags.Any:
@ -709,7 +759,7 @@ namespace DCFApixels.DragonECS
case EcsMaskFlags.IncAny:
case EcsMaskFlags.ExcAny:
case EcsMaskFlags.IncExcAny:
Iterate(source).CopyTo(group);
Iterate(source).CacheTo(group);
break;
case EcsMaskFlags.Broken:
group.Clear();
@ -720,21 +770,21 @@ namespace DCFApixels.DragonECS
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IterateTo(EcsSpan source, ref int[] array)
public int CacheTo(EcsSpan source, ref int[] array)
{
switch (_maskFlags)
{
case EcsMaskFlags.Empty:
return source.ToArray(ref array);
case EcsMaskFlags.Inc:
return IterateOnlyInc(source).CopyTo(ref array);
return IterateOnlyInc(source).CacheTo(ref array);
case EcsMaskFlags.Exc:
case EcsMaskFlags.Any:
case EcsMaskFlags.IncExc:
case EcsMaskFlags.IncAny:
case EcsMaskFlags.ExcAny:
case EcsMaskFlags.IncExcAny:
return Iterate(source).CopyTo(ref array);
return Iterate(source).CacheTo(ref array);
case EcsMaskFlags.Broken:
return new EcsSpan(World.ID, Array.Empty<int>()).ToArray(ref array);
default:
@ -764,7 +814,7 @@ namespace DCFApixels.DragonECS
#region CopyTo
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(EcsGroup group)
public void CacheTo(EcsGroup group)
{
group.Clear();
var enumerator = GetEnumerator();
@ -774,7 +824,7 @@ namespace DCFApixels.DragonECS
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CopyTo(ref int[] array)
public int CacheTo(ref int[] array)
{
int count = 0;
var enumerator = GetEnumerator();
@ -920,7 +970,7 @@ namespace DCFApixels.DragonECS
#region CopyTo
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(EcsGroup group)
public void CacheTo(EcsGroup group)
{
group.Clear();
var enumerator = GetEnumerator();
@ -930,7 +980,7 @@ namespace DCFApixels.DragonECS
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CopyTo(ref int[] array)
public int CacheTo(ref int[] array)
{
int count = 0;
var enumerator = GetEnumerator();
@ -1077,7 +1127,7 @@ namespace DCFApixels.DragonECS.Core.Internal
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
internal readonly struct IncCountComparer : IStructComparer<int>
internal readonly struct IncCountComparer : IComparer<int>
{
public readonly EcsWorld.PoolSlot[] counts;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -1096,7 +1146,7 @@ namespace DCFApixels.DragonECS.Core.Internal
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
internal readonly struct ExcCountComparer : IStructComparer<int>
internal readonly struct ExcCountComparer : IComparer<int>
{
public readonly EcsWorld.PoolSlot[] counts;
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@ -3,7 +3,6 @@
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.RunnersCore;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
@ -122,7 +121,6 @@ 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}");
@ -600,20 +598,6 @@ 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 RunnersCore
namespace Core
{
//добавить инъекцию в раннеры
public abstract class EcsRunner
@ -63,6 +63,23 @@ 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)]
@ -80,9 +97,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
{
@ -148,7 +165,7 @@ namespace DCFApixels.DragonECS
#region Constructors
public RunHelper(EcsRunner<TProcess> runner) : this(runner,
#if DEBUG
typeof(TProcess).ToMeta().Name)
typeof(TProcess).GetMeta().Name)
#else
string.Empty)
#endif
@ -298,7 +315,7 @@ namespace DCFApixels.DragonECS
#region Constructors
public RunHelperWithFinally(EcsRunner<TProcess> runner) : this(runner,
#if DEBUG
typeof(TProcess).ToMeta().Name)
typeof(TProcess).GetMeta().Name)
#else
string.Empty)
#endif
@ -476,8 +493,6 @@ namespace DCFApixels.DragonECS
#endregion
}
#endregion
//----
}
}

View File

@ -65,7 +65,7 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _excs; }
}
/// <summary> Sorted set excluding constraints presented as global type codes. </summary>
/// <summary> Sorted set any constraints presented as global type codes. </summary>
public ReadOnlySpan<EcsTypeCode> AnyTypeCodes
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -109,9 +109,15 @@ 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)
@ -305,12 +311,24 @@ 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(); }
@ -329,6 +347,15 @@ 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(); }
@ -354,6 +381,7 @@ 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,7 +2,6 @@
#undef DEBUG
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.PoolsCore;
namespace DCFApixels.DragonECS
{

View File

@ -4,7 +4,6 @@
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;
@ -71,6 +70,7 @@ 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,6 +86,7 @@ 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
@ -394,8 +395,11 @@ namespace DCFApixels.DragonECS
{
slot.gen |= GEN_SLEEP_MASK;
}
_entityListeners.InvokeOnNewEntity(entityID);
MoveToEmptyEntities(entityID);
if (_hasAnyEntityListener)
{
_entityListeners.InvokeOnNewEntity(entityID);
}
MoveToEmptyEntities(entityID, false);
}
@ -436,25 +440,92 @@ namespace DCFApixels.DragonECS
_delEntBuffer[_delEntBufferCount++] = entityID;
_entities[entityID].isUsed = false;
_entitiesCount--;
_entityListeners.InvokeOnDelEntity(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void MoveToEmptyEntities(int entityID)
{
_emptyEntities[_emptyEntitiesCount++] = entityID;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool RemoveFromEmptyEntities(int entityID)
{
for (int i = _emptyEntitiesCount - 1; i >= 0; i--)
if (_hasAnyEntityListener)
{
if (_emptyEntities[i] == entityID)
_entityListeners.InvokeOnDelEntity(entityID);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void MoveToEmptyEntities(int entityID, bool readyToRemove)
{
if (readyToRemove)
{
entityID |= int.MinValue;
}
_emptyEntities[_emptyEntitiesLength++] = entityID;
_emptyEntitiesCount++;
if (_emptyEntitiesLength == _emptyEntities.Length)
{
ReleaseEmptyEntitiesBuffer_OnlyReadyToRemove();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RemoveFromEmptyEntities(int entityID)
{
const int THRESHOLD = 16;
_emptyEntitiesCount--;
if (_emptyEntitiesLength < THRESHOLD)
{
for (int i = _emptyEntitiesLength - 1; i >= 0; i--)
{
_emptyEntities[i] = _emptyEntities[--_emptyEntitiesCount];
return true;
if ((_emptyEntities[i] & int.MaxValue) == entityID)
{
_emptyEntities[i] = _emptyEntities[--_emptyEntitiesLength];
}
}
}
return false;
#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;
}
#endregion
@ -698,7 +769,7 @@ namespace DCFApixels.DragonECS
}
else
{
poolIdsPtr = UnmanagedArrayUtility.New<int>(count);
poolIdsPtr = MemoryAllocator.Alloc<int>(count).Ptr;
}
UnsafeArray<int> ua = UnsafeArray<int>.Manual(poolIdsPtr, count);
@ -711,7 +782,7 @@ namespace DCFApixels.DragonECS
if (count >= BUFFER_THRESHOLD)
{
UnmanagedArrayUtility.Free(poolIdsPtr);
MemoryAllocator.Free(poolIdsPtr);
}
@ -748,7 +819,7 @@ namespace DCFApixels.DragonECS
}
else
{
poolIdsPtr = UnmanagedArrayUtility.New<int>(count);
poolIdsPtr = MemoryAllocator.Alloc<int>(count).Ptr;
}
GetComponentTypeIDsFor_Internal(fromEntityID, poolIdsPtr, count);
@ -759,7 +830,7 @@ namespace DCFApixels.DragonECS
if (count >= BUFFER_THRESHOLD)
{
UnmanagedArrayUtility.Free(poolIdsPtr);
MemoryAllocator.Free(poolIdsPtr);
}
//foreach (var pool in _pools)
@ -890,16 +961,12 @@ namespace DCFApixels.DragonECS
{
ReleaseDelEntityBuffer(-1);
}
public unsafe void ReleaseDelEntityBuffer(int count)
public void ReleaseDelEntityBuffer(int count)
{
if (_emptyEntitiesCount <= 0 && _delEntBufferCount <= 0) { return; }
if (_emptyEntitiesLength <= 0 && _delEntBufferCount <= 0) { return; }
unchecked { _version++; }
for (int i = 0; i < _emptyEntitiesCount; i++)
{
TryDelEntity(_emptyEntities[i]);
}
_emptyEntitiesCount = 0;
ReleaseEmptyEntitiesBuffer();
if (count < 0)
{
@ -1023,10 +1090,12 @@ 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
@ -1082,7 +1151,7 @@ namespace DCFApixels.DragonECS
}
else
{
poolIdsPtr = UnmanagedArrayUtility.New<int>(count);
poolIdsPtr = MemoryAllocator.Alloc<int>(count).Ptr;
}
GetComponentTypeIDsFor_Internal(entityID, poolIdsPtr, count);
@ -1105,7 +1174,7 @@ namespace DCFApixels.DragonECS
if (count >= BUFFER_THRESHOLD)
{
UnmanagedArrayUtility.Free(poolIdsPtr);
MemoryAllocator.Free(poolIdsPtr);
}
}
public ReadOnlySpan<object> GetComponentsFor(int entityID)
@ -1352,6 +1421,7 @@ namespace DCFApixels.DragonECS
public interface IEcsEntityEventListener
{
void OnNewEntity(int entityID);
void OnMigrateEntity(int entityID);
void OnDelEntity(int entityID);
}
internal static class WorldEventListExtensions
@ -1389,6 +1459,14 @@ 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,7 +127,6 @@ namespace DCFApixels.DragonECS
{
return _cmpTypeCode_2_CmpTypeIDs.Contains((int)EcsTypeCodeManager.Get(componentType));
}
//TODO пересмотреть нейминг или функцию
public bool IsComponentTypeDeclared(int componentTypeID)
{
if (componentTypeID >= 0 && componentTypeID < _pools.Length)
@ -287,6 +286,10 @@ 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)
@ -297,12 +300,15 @@ namespace DCFApixels.DragonECS
slot.version++;
var count = --_entities[entityID].componentsCount;
_entityComponentMasks[(entityID << _entityComponentMaskLengthBitShift) + maskBit.chunkIndex] &= ~maskBit.mask;
if (count == 0 && IsUsed(entityID))
{
MoveToEmptyEntities(entityID);
MoveToEmptyEntities(entityID, true);
}
CheckUnregisterValid(count, entityID);
if (_hasAnyEntityListener)
{
_entityListeners.InvokeOnMigrateEntity(entityID);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool TryRegisterEntityComponent(int entityID, int componentTypeID, EcsMaskChunck maskBit)
@ -321,6 +327,10 @@ namespace DCFApixels.DragonECS
{
RemoveFromEmptyEntities(entityID);
}
if (_hasAnyEntityListener)
{
_entityListeners.InvokeOnMigrateEntity(entityID);
}
return true;
}
return false;
@ -341,9 +351,13 @@ namespace DCFApixels.DragonECS
if (count == 0 && IsUsed(entityID))
{
MoveToEmptyEntities(entityID);
MoveToEmptyEntities(entityID, true);
}
CheckUnregisterValid(count, entityID);
if (_hasAnyEntityListener)
{
_entityListeners.InvokeOnMigrateEntity(entityID);
}
return true;
}
return false;

View File

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

View File

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

View File

@ -84,6 +84,76 @@ 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)//TODO проверить
private object Extract_Internal(Type type)
{
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) // TODO проверить
public void Extract<T>(ref T obj)
{
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>(); // TODO Проверить IMonoWorldInject
var monoWorldProcess = pipeline.GetProcess<IMonoWorldInject>();
foreach (var monoWorldSystem in monoWorldProcess)
{
monoWorldSystem.World = _monoWorld;

View File

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

View File

@ -1,25 +1,97 @@
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
{
internal static class AllocatorUtility
#if ENABLE_IL2CPP
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
internal static unsafe class AllocatorUtility
{
public static unsafe void ClearAllocatedMemory(IntPtr ptr, int startByte, int lengthInBytes)
public static void ClearAllocatedMemory(IntPtr ptr, int startByte, int lengthInBytes)
{
ClearAllocatedMemory((byte*)ptr, startByte, lengthInBytes);
}
public static unsafe void ClearAllocatedMemory(byte* ptr, int startByte, int lengthInBytes)
public static 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();
#endif
}
[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;
}
}
}

View File

@ -2,6 +2,7 @@
#undef DEBUG
#endif
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS.Core.Internal
@ -9,6 +10,7 @@ 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
@ -24,32 +26,51 @@ 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 Handler AllocAndInit<T>(int count) where T : unmanaged
public static HMem<T> AllocAndInit<T>(int count) where T : unmanaged
{
return AllocAndInit_Internal(Marshal.SizeOf<T>() * count, typeof(T));
return new HMem<T>(AllocAndInit_Internal(Marshal.SizeOf<T>() * count, typeof(T)), count);
}
public static Handler AllocAndInit(int byteLength)
public static HMem<byte> AllocAndInit(int byteLength)
{
return AllocAndInit_Internal(byteLength, null);
return new HMem<byte>(AllocAndInit_Internal(byteLength, null), byteLength);
}
public static Handler AllocAndInit_Internal(int byteLength, Type type)
{
Handler handler = Alloc_Internal(byteLength, type);
AllocatorUtility.ClearAllocatedMemory(handler.Ptr, 0, byteLength);
AllocatorUtility.ClearAllocatedMemory(handler.RawPtr, 0, byteLength);
return handler;
}
#endregion
#region Alloc
public static Handler Alloc<T>(int count) where T : unmanaged
public static HMem<T> Alloc<T>(int count) where T : unmanaged
{
return Alloc_Internal(Marshal.SizeOf<T>() * count, typeof(T));
return new HMem<T>(Alloc_Internal(Marshal.SizeOf<T>() * count, typeof(T)), count);
}
public static Handler Alloc(int byteLength)
public static HMem<byte> Alloc(int byteLength)
{
return Alloc_Internal(byteLength, null);
return new HMem<byte>(Alloc_Internal(byteLength, null), byteLength); ;
}
public static Handler Alloc_Internal(int byteLength, Type type)
{
@ -75,6 +96,7 @@ 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
@ -84,69 +106,135 @@ namespace DCFApixels.DragonECS.Core.Internal
#endregion
#region ReallocAndInit
public static Handler ReallocAndInit<T>(void* target, int oldCount, int newCount) where T : unmanaged
public static HMem<T> ReallocAndInit<T>(T* target, int oldCount, int newCount) where T : unmanaged
{
return ReallocAndInit<T>(Handler.FromDataPtr(target), oldCount, newCount);
}
public static Handler ReallocAndInit(void* target, int oldByteLength, int newByteLength)
public static HMem<byte> ReallocAndInit(void* target, int oldByteLength, int newByteLength)
{
return ReallocAndInit(Handler.FromDataPtr(target), oldByteLength, newByteLength);
}
public static Handler ReallocAndInit<T>(Handler target, int oldCount, int newCount) where T : unmanaged
public static HMem<T> ReallocAndInit<T>(HMem<T> target, int newCount) where T : unmanaged
{
var size = Marshal.SizeOf<T>();
return ReallocAndInit_Internal(target, size * oldCount, size * newCount, typeof(T));
return new HMem<T>(ReallocAndInit_Internal(target, size * target.Length, size * newCount, typeof(T)), newCount);
}
public static Handler ReallocAndInit(Handler target, int oldByteLength, int newByteLength)
public static HMem<T> ReallocAndInit<T>(Handler target, int oldCount, int newCount) where T : unmanaged
{
return ReallocAndInit_Internal(target, oldByteLength, newByteLength, null);
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);
}
private static Handler ReallocAndInit_Internal(Handler target, int oldByteLength, int newByteLength, Type newType)
{
Handler handler = Realloc_Internal(target, newByteLength, newType);
AllocatorUtility.ClearAllocatedMemory(handler.Ptr, oldByteLength, newByteLength - oldByteLength);
AllocatorUtility.ClearAllocatedMemory(handler.RawPtr, oldByteLength, newByteLength - oldByteLength);
return handler;
}
#endregion
#region Realloc
public static Handler Realloc<T>(void* target, int newCount) where T : unmanaged
public static HMem<T> Realloc<T>(T* target, int newCount) where T : unmanaged
{
return Realloc<T>(Handler.FromDataPtr(target), Marshal.SizeOf<T>() * newCount);
}
public static Handler Realloc(void* target, int newByteLength)
public static HMem<byte> Realloc(void* target, int newByteLength)
{
return Realloc(Handler.FromDataPtr(target), newByteLength);
return new HMem<byte>(Realloc(Handler.FromDataPtr(target), newByteLength), newByteLength);
}
public static Handler Realloc<T>(Handler target, int newCount) where T : unmanaged
public static HMem<T> Realloc<T>(HMem<T> target, int newCount) where T : unmanaged
{
return Realloc_Internal(target, Marshal.SizeOf<T>() * newCount, typeof(T));
return new HMem<T>(Realloc_Internal(target, Marshal.SizeOf<T>() * newCount, typeof(T)), newCount);
}
public static Handler Realloc(Handler target, int newByteLength)
public static HMem<T> Realloc<T>(Handler target, int newCount) where T : unmanaged
{
return Realloc_Internal(target, newByteLength, null);
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);
}
private static Handler Realloc_Internal(Handler target, int newByteLength, Type newType)
{
newByteLength = newByteLength == 0 ? 1 : newByteLength;
Meta* newHandledPtr = (Meta*)Marshal.ReAllocHGlobal((IntPtr)target.GetHandledPtr(), (IntPtr)newByteLength + sizeof(Meta));
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));
Handler handler = Handler.FromHandledPtr(newHandledPtr);
#if DEBUG
newHandledPtr->ID = id;
newHandledPtr->ByteLength = newByteLength;
#if DRAGONECS_DEEP_DEBUG
_debugInfos[newHandledPtr->ID].stackTrace = new System.Diagnostics.StackTrace();
_debugInfos[id].stackTrace = new System.Diagnostics.StackTrace();
#endif
_debugInfos[newHandledPtr->ID].type = newType;
_debugInfos[newHandledPtr->ID].handler = handler;
_debugInfos[id].increment = ++_inrement;
_debugInfos[id].type = newType;
_debugInfos[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());
@ -159,6 +247,10 @@ 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);
@ -199,6 +291,7 @@ 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
@ -210,11 +303,97 @@ 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
public readonly struct Handler : IDisposable, IEquatable<Handler>
{
public static readonly Handler Empty = new Handler();
internal readonly Meta* Data; // Data[-1] is meta;
@ -239,16 +418,39 @@ namespace DCFApixels.DragonECS.Core.Internal
#endif
}
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 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; }
void IDisposable.Dispose() { Free((void*)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; }
#region Debugger
#if DEBUG
#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
internal unsafe string DebuggerDisplay()
internal string DebuggerDisplay()
{
if (Data == null)
{
@ -282,8 +484,9 @@ namespace DCFApixels.DragonECS.Core.Internal
}
return array;
}
[StructLayout(LayoutKind.Explicit)]
private unsafe struct Union
private struct Union
{
[FieldOffset(0)]
public Array array;
@ -303,13 +506,13 @@ namespace DCFApixels.DragonECS.Core.Internal
public HandlerDebugInfo[] OtherHandlersInfo;
public unsafe DebuggerProxy(Handler handler)
public DebuggerProxy(Handler handler)
{
IsAlive = handler.Ptr.ToPointer() != null;
IsAlive = handler.RawPtr.ToPointer() != null;
if (IsAlive == false) { return; }
Meta = handler.GetHandledPtr()[0];
_data = (byte*)handler.Ptr;
_data = (byte*)handler.RawPtr;
DebugInfo = _debugInfos[Meta.ID];
if (DebugInfo.type == null)
@ -333,13 +536,20 @@ namespace DCFApixels.DragonECS.Core.Internal
#endif
#endregion
}
#endregion
}
internal static class MemoryAllocatorHandlerExtensions
{
public static void Dispose(this ref MemoryAllocator.Handler self)
public static void DisposeAndReset(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).As<byte>();
_ptr = MemoryAllocator.Alloc<byte>(byteSize).Ptr;
_byteSize = byteSize;
}
return (T*)_ptr;

View File

@ -1,272 +0,0 @@
#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

@ -6,7 +6,6 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS.Core.Internal
{
@ -270,114 +269,6 @@ 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,6 +9,33 @@ 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
{

392
src/Internal/SortHalper.cs Normal file
View File

@ -0,0 +1,392 @@
#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

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

View File

@ -38,13 +38,13 @@ namespace DCFApixels.DragonECS.Core.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UnsafeArray(int length)
{
UnmanagedArrayUtility.New(out ptr, length);
ptr = MemoryAllocator.Alloc<T>(length).Ptr;
Length = length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UnsafeArray(int length, bool isInit)
{
UnmanagedArrayUtility.NewAndInit(out ptr, length);
ptr = MemoryAllocator.AllocAndInit<T>(length).Ptr;
Length = length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -90,18 +90,20 @@ namespace DCFApixels.DragonECS.Core.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UnsafeArray<T> Clone()
{
return new UnsafeArray<T>(UnmanagedArrayUtility.Clone(ptr, Length), Length);
return new UnsafeArray<T>(MemoryAllocator.From(ptr, Length).Ptr, Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
UnmanagedArrayUtility.Free(ref ptr, ref Length);
MemoryAllocator.Free(ptr);
ptr = default;
Length = default;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadonlyDispose()
{
UnmanagedArrayUtility.Free(ptr);
MemoryAllocator.Free(ptr);
}
public override string ToString()
{
@ -114,6 +116,9 @@ 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,7 +3,6 @@
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.PoolsCore;
using System;
using System.Collections;
using System.Collections.Generic;
@ -26,6 +25,7 @@ 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,7 +35,8 @@ namespace DCFApixels.DragonECS
public sealed class EcsPool<T> : IEcsPoolImplementation<T>, IEcsStructPool<T>, IEnumerable<T> //IEnumerable<T> - IntelliSense hack
where T : struct, IEcsComponent
{
private EcsWorld _source;
private short _worldID;
private EcsWorld _world;
private int _componentTypeID;
private EcsMaskChunck _maskBit;
@ -45,14 +46,14 @@ namespace DCFApixels.DragonECS
private int[] _recycledItems;
private int _recycledItemsCount = 0;
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;
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;
#if !DRAGONECS_DISABLE_POOLS_EVENTS
private StructList<IEcsPoolEventListener> _listeners = new StructList<IEcsPoolEventListener>(2);
private int _listenersCachedCount = 0;
private bool _hasAnyListener = false;
#endif
private bool _isLocked;
@ -77,7 +78,7 @@ namespace DCFApixels.DragonECS
}
public EcsWorld World
{
get { return _source; }
get { return _world; }
}
public bool IsReadOnly
{
@ -104,7 +105,8 @@ namespace DCFApixels.DragonECS
}
void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID)
{
_source = world;
_world = world;
_worldID = world.ID;
_mediator = mediator;
_componentTypeID = componentTypeID;
_maskBit = EcsMaskChunck.FromID(componentTypeID);
@ -128,13 +130,13 @@ namespace DCFApixels.DragonECS
{
ref int itemIndex = ref _mapping[entityID];
#if DEBUG
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_source, entityID); }
if (_source.IsUsed(entityID) == false) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_source, entityID); }
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
if (_world.IsUsed(entityID) == false) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_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 | _source.IsUsed(entityID) == false) { return ref _items[0]; }
if (_isLocked | _world.IsUsed(entityID) == false) { return ref _items[0]; }
#endif
if (_recycledItemsCount > 0)
{
@ -151,9 +153,9 @@ namespace DCFApixels.DragonECS
}
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
ref T result = ref _items[itemIndex];
EnableComponent(ref result);
InvokeOnAdd(entityID, ref _items[itemIndex]);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
_listeners.InvokeOnAddAndGet(entityID, _listenersCachedCount);
if (_hasAnyListener) { _listeners.InvokeOnAddAndGet(entityID); }
#endif
return ref result;
}
@ -164,7 +166,7 @@ namespace DCFApixels.DragonECS
if (!Has(entityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
#endif
#if !DRAGONECS_DISABLE_POOLS_EVENTS
_listeners.InvokeOnGet(entityID, _listenersCachedCount);
if (_hasAnyListener) { _listeners.InvokeOnGet(entityID); }
#endif
return ref _items[_mapping[entityID]];
}
@ -179,7 +181,7 @@ namespace DCFApixels.DragonECS
public ref T TryAddOrGet(int entityID)
{
#if DEBUG
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_source, entityID); }
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
#endif
ref int itemIndex = ref _mapping[entityID];
if (itemIndex <= 0)
@ -203,13 +205,13 @@ namespace DCFApixels.DragonECS
}
}
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
EnableComponent(ref _items[itemIndex]);
InvokeOnAdd(entityID, ref _items[itemIndex]);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
_listeners.InvokeOnAdd(entityID, _listenersCachedCount);
if (_hasAnyListener) { _listeners.InvokeOnAdd(entityID); }
#endif
} //Add block end
#if !DRAGONECS_DISABLE_POOLS_EVENTS
_listeners.InvokeOnGet(entityID, _listenersCachedCount);
if (_hasAnyListener) { _listeners.InvokeOnGet(entityID); }
#endif
return ref _items[itemIndex];
}
@ -222,14 +224,14 @@ namespace DCFApixels.DragonECS
{
ref int itemIndex = ref _mapping[entityID];
#if DEBUG
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_source, entityID); }
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_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
DisableComponent(ref _items[itemIndex]);
InvokeOnDel(entityID, ref _items[itemIndex]);
if (_recycledItemsCount >= _recycledItems.Length)
{
Array.Resize(ref _recycledItems, ArrayUtility.NextPow2Safe(_recycledItemsCount));
@ -239,7 +241,7 @@ namespace DCFApixels.DragonECS
_itemsCount--;
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
_listeners.InvokeOnDel(entityID, _listenersCachedCount);
if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); }
#endif
}
public void TryDel(int entityID)
@ -256,7 +258,7 @@ namespace DCFApixels.DragonECS
#elif DRAGONECS_STABILITY_MODE
if (!Has(fromEntityID)) { return; }
#endif
CopyComponent(ref Get(fromEntityID), ref TryAddOrGet(toEntityID));
EcsComponentCopy<T>.Copy(_isCustomCopy, _customCopy, ref Get(fromEntityID), ref TryAddOrGet(toEntityID));
}
public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID)
{
@ -265,7 +267,7 @@ namespace DCFApixels.DragonECS
#elif DRAGONECS_STABILITY_MODE
if (!Has(fromEntityID)) { return; }
#endif
CopyComponent(ref Get(fromEntityID), ref toWorld.GetPool<T>().TryAddOrGet(toEntityID));
EcsComponentCopy<T>.Copy(_isCustomCopy, _customCopy, ref Get(fromEntityID), ref toWorld.GetPool<T>().TryAddOrGet(toEntityID));
}
public void ClearAll()
@ -277,15 +279,15 @@ namespace DCFApixels.DragonECS
#endif
_recycledItemsCount = 0; // ñïåðåäè ÷òîáû îáíóëÿëîñü, òàê êàê Del íå îáíóëÿåò
if (_itemsCount <= 0) { return; }
var span = _source.Where(out SingleAspect<T> _);
var span = _world.Where(out SingleAspect<T> _);
foreach (var entityID in span)
{
ref int itemIndex = ref _mapping[entityID];
DisableComponent(ref _items[itemIndex]);
InvokeOnDel(entityID, ref _items[itemIndex]);
itemIndex = 0;
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
_listeners.InvokeOnDel(entityID, _listenersCachedCount);
if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); }
#endif
}
_itemsCount = 0;
@ -314,6 +316,26 @@ 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)
{
@ -332,58 +354,19 @@ namespace DCFApixels.DragonECS
{
if (listener == null) { EcsPoolThrowHelper.ThrowNullListener(); }
_listeners.Add(listener);
_listenersCachedCount++;
_hasAnyListener = _listeners.Count > 0;
}
public void RemoveListener(IEcsPoolEventListener listener)
{
if (listener == null) { EcsPoolThrowHelper.ThrowNullListener(); }
if (_listeners.RemoveWithOrder(listener))
{
_listenersCachedCount--;
_hasAnyListener = _listeners.Count > 0;
}
}
#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(); }
@ -406,12 +389,20 @@ 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
@ -484,7 +475,6 @@ namespace DCFApixels.DragonECS
#endregion
}
public static class EcsPoolExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -518,29 +508,5 @@ 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,14 +1,19 @@
#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.PoolsCore
namespace DCFApixels.DragonECS.Core
{
/// <summary> Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool<T>. </summary>
public interface IEcsPoolImplementation : IEcsPool
@ -62,7 +67,12 @@ namespace DCFApixels.DragonECS.PoolsCore
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowNullListener()
{
throw new ArgumentNullException("listener is null");
throw new ArgumentNullException("Listener is null");
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowNullComponent()
{
throw new ArgumentNullException("Component is null");
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowPoolLocked()
@ -95,7 +105,7 @@ namespace DCFApixels.DragonECS.Core.Internal
#if DEBUG
throw new NullInstanceException();
#else
return EcsWorld.GetWorld(0);
return EcsWorld.GetWorld(0);
#endif
}
}
@ -286,72 +296,17 @@ 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)
{
self.InvokeOnAdd(entityID, self.Count);
}
[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)
{
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);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnGet(this List<IEcsPoolEventListener> self, int entityID)
{
self.InvokeOnGet(entityID, self.Count);
}
[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)
{
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); }
}
//
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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 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)
public static void InvokeOnAddAndGet(this List<IEcsPoolEventListener> self, int entityID)
{
for (int i = 0; i < self.Count; i++)
{
@ -359,36 +314,50 @@ namespace DCFApixels.DragonECS
self[i].OnGet(entityID);
}
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InvokeOnAddAndGet(ref this StructList<IEcsPoolEventListener> self, int entityID, int cachedCount)
public static void InvokeOnGet(this List<IEcsPoolEventListener> self, int entityID)
{
for (int i = 0; i < cachedCount; i++)
for (int i = 1; i < self.Count; i++) { self[i].OnGet(entityID); }
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[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); }
}
//
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InvokeOnAdd(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)
{
for (int i = 0; i < self.Count; i++)
{
self[i].OnAdd(entityID);
self[i].OnGet(entityID);
}
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InvokeOnGet(ref this StructList<IEcsPoolEventListener> self, int entityID)
internal static void InvokeOnGet(this StructList<IEcsPoolEventListener> self, int entityID)
{
for (int i = 1; i < self.Count; i++) { self[i].OnGet(entityID); }
for (int i = 0; i < self.Count; i++) { self[i].OnGet(entityID); }
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[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)
internal static void InvokeOnDel(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,7 +3,6 @@
#endif
using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.PoolsCore;
using System;
using System.Collections;
using System.Collections.Generic;
@ -26,6 +25,7 @@ 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 _source;
private EcsWorld _world;
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 int _listenersCachedCount = 0;
private bool _hasAnyListener = false;
#endif
private bool _isLocked;
@ -78,7 +78,7 @@ namespace DCFApixels.DragonECS
}
public EcsWorld World
{
get { return _source; }
get { return _world; }
}
public bool IsReadOnly
{
@ -102,7 +102,7 @@ namespace DCFApixels.DragonECS
}
void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID)
{
_source = world;
_world = world;
_mediator = mediator;
_componentTypeID = componentTypeID;
_maskBit = EcsMaskChunck.FromID(componentTypeID);
@ -116,17 +116,18 @@ namespace DCFApixels.DragonECS
public void Add(int entityID)
{
#if DEBUG
if (_source.IsUsed(entityID) == false) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_source, entityID); }
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
if (_world.IsUsed(entityID) == false) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
if (Has(entityID)) { EcsPoolThrowHelper.ThrowAlreadyHasComponent<T>(entityID); }
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
#elif DRAGONECS_STABILITY_MODE
if (Has(entityID) | _source.IsUsed(entityID) == false | _isLocked) { return; }
if (Has(entityID) | _world.IsUsed(entityID) == false | _isLocked) { return; }
#endif
_count++;
_mapping[entityID] = true;
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
_listeners.InvokeOnAdd(entityID, _listenersCachedCount);
if (_hasAnyListener) { _listeners.InvokeOnAdd(entityID); }
#endif
}
public void TryAdd(int entityID)
@ -144,6 +145,7 @@ 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
@ -153,7 +155,7 @@ namespace DCFApixels.DragonECS
_count--;
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
_listeners.InvokeOnDel(entityID, _listenersCachedCount);
if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); }
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -216,14 +218,14 @@ namespace DCFApixels.DragonECS
if (_isLocked) { return; }
#endif
if (_count <= 0) { return; }
var span = _source.Where(out SingleTagAspect<T> _);
var span = _world.Where(out SingleTagAspect<T> _);
_count = 0;
foreach (var entityID in span)
{
_mapping[entityID] = false;
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
_listeners.InvokeOnDel(entityID, _listenersCachedCount);
if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); }
#endif
}
}
@ -293,14 +295,14 @@ namespace DCFApixels.DragonECS
{
if (listener == null) { EcsPoolThrowHelper.ThrowNullListener(); }
_listeners.Add(listener);
_listenersCachedCount++;
_hasAnyListener = _listeners.Count > 0;
}
public void RemoveListener(IEcsPoolEventListener listener)
{
if (listener == null) { EcsPoolThrowHelper.ThrowNullListener(); }
if (_listeners.RemoveWithOrder(listener))
{
_listenersCachedCount--;
_hasAnyListener = _listeners.Count > 0;
}
}
#endif
@ -328,12 +330,22 @@ 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
@ -407,7 +419,6 @@ 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)]
@ -441,68 +452,5 @@ 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
}
}

560
src/Pools/EcsValuePool.cs Normal file
View File

@ -0,0 +1,560 @@
#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

@ -0,0 +1,11 @@
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.ToMeta().TypeName} is not allowed in the {worldType.ToMeta().TypeName} world.");
throw new InvalidOperationException($"Using component {componentType.GetMeta().TypeName} is not allowed in the {worldType.GetMeta().TypeName} world.");
}
}
}

View File

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

View File

@ -252,74 +252,5 @@ 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

@ -1,202 +0,0 @@
#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

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

View File

@ -5,73 +5,6 @@ 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
@ -129,6 +62,14 @@ 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,6 +116,48 @@ 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()
{
@ -128,12 +170,6 @@ 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
@ -155,6 +191,13 @@ 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)
@ -192,17 +235,24 @@ 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) { Throw.ArgumentDifferentWorldsException(); }
if (world.ID != _world) { id = EcsConsts.NULL_ENTITY_ID; return false; }
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) { Throw.ArgumentDifferentWorldsException(); }
if (world.ID != _world) { gen = 0; id = EcsConsts.NULL_ENTITY_ID; return false; }
gen = _gen;
id = _id;
return world.IsAlive(_id, _gen);
@ -210,14 +260,14 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryUnpack(EcsMask mask, out int id)
{
if (mask.WorldID != _world) { Throw.ArgumentDifferentWorldsException(); }
if (mask.WorldID != _world) { id = EcsConsts.NULL_ENTITY_ID; return false; }
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) { Throw.ArgumentDifferentWorldsException(); }
if (mask.WorldID != _world) { gen = 0; id = EcsConsts.NULL_ENTITY_ID; return false; }
gen = _gen;
id = _id;
return mask.World.IsAlive(_id, _gen) && mask.World.IsMatchesMask(mask, _id);
@ -225,14 +275,14 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryUnpack(EcsAspect aspect, out int id)
{
if (aspect.World.ID != _world) { Throw.ArgumentDifferentWorldsException(); }
if (aspect.World.ID != _world) { id = EcsConsts.NULL_ENTITY_ID; return false; }
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) { Throw.ArgumentDifferentWorldsException(); }
if (aspect.World.ID != _world) { gen = 0; id = EcsConsts.NULL_ENTITY_ID; return false; }
gen = _gen;
id = _id;
return aspect.World.IsAlive(_id, _gen) && aspect.IsMatches(_id);
@ -323,9 +373,39 @@ namespace DCFApixels.DragonECS
id = _id;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Deconstruct(out int id, out short gen, out short worldID) { Unpack(out id, out gen, out worldID); }
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;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Deconstruct(out int id, out EcsWorld world) { Unpack(out id, out world); }
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;
}
#endregion
#region Unpacking Unchecked
@ -384,16 +464,10 @@ 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)]
private static entlong Combine_Internal(int entityID, EcsWorld world)
{
return world == null ? new entlong(entityID, 0, 0) : world.GetEntityLong(entityID);
}
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); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator long(entlong a) { return a._full; }
@ -405,6 +479,21 @@ 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
@ -412,6 +501,7 @@ 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)]