├── .gitignore ├── JimboA.EcsLite.ECB.asmdef ├── JimboA.EcsLite.ECB.asmdef.meta ├── LICENSE.md ├── LICENSE.md.meta ├── README.md ├── README.md.meta ├── package.json ├── package.json.meta ├── src.meta └── src ├── Components.cs ├── Components.cs.meta ├── ECBSystem.cs ├── ECBSystem.cs.meta ├── EcbPool.cs ├── EcbPool.cs.meta ├── EntityCommandBuffer.cs ├── EntityCommandBuffer.cs.meta ├── FastList.cs ├── FastList.cs.meta ├── IEcbPool.cs └── IEcbPool.cs.meta /.gitignore: -------------------------------------------------------------------------------- 1 | *.sln 2 | .vscode 3 | .idea 4 | .DS_Store 5 | bin 6 | obj 7 | Library 8 | Temp 9 | -------------------------------------------------------------------------------- /JimboA.EcsLite.ECB.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "JimboA.EcsLite.ECB", 3 | "rootNamespace": "JimboA.EcsLite.ECB", 4 | "references": [ 5 | "Leopotam.EcsLite" 6 | ], 7 | "includePlatforms": [], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": false, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [], 15 | "noEngineReferences": false 16 | } -------------------------------------------------------------------------------- /JimboA.EcsLite.ECB.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 24b5ed10dcdbe38418461ec0dcb1c75c 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 JimboA 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0921546b2b51eef42abf35229ada54fc 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EcsLiteEntityCommandBuffer 2 | Entity command buffer for [LeoECS Lite](https://github.com/Leopotam/ecslite). 3 | It will allow you to create delayed commands on entities (like Add/Set/Del components etc.) and execute them in special systems. 4 | 5 | Why? I personally use it for turn based games, but you can do it your way. 6 | 7 | > Tested on unity 2020.3 (not dependent on it) and contains assembly definition for compiling to separate assembly file for performance reason. 8 | 9 | > **Important!** Don't forget to use `DEBUG` builds for development and `RELEASE` builds in production: all internal error checks / exception throwing works only in `DEBUG` builds and eleminated for performance reasons in `RELEASE`. 10 | 11 | # Installation 12 | 13 | ## As unity module 14 | This repository can be installed as unity module directly from git url. In this way new line should be added to `Packages/manifest.json`: 15 | ``` 16 | "com.jimboa.ecsliteentitycommandbuffer": "https://github.com/JimboA/EcsLiteEntityCommandBuffer.git", 17 | ``` 18 | 19 | ## As source 20 | If you can't / don't want to use unity modules, code can be downloaded as sources archive from `Releases` page. 21 | 22 | # How to use 23 | To start using the command buffer you must create a special ECB system. Just inherit your system from the EcbSystem class: 24 | ```csharp 25 | using Leopotam.EcsLite; 26 | using JimboA.EcsLite.ECB; 27 | 28 | class UserEcbSystem : EcbSystem, IEcsRunSystem 29 | { 30 | public void Run (IEcsSystems systems) 31 | { 32 | // Will be called on each EcsSystems.Run() call. 33 | } 34 | } 35 | ``` 36 | And add it to systems in startup: 37 | ```csharp 38 | ... 39 | IEcsSystems systems = new IEcsSystems (world); 40 | systems 41 | .Add (new UserEcbSystem(world)) 42 | .Init (); 43 | ... 44 | ``` 45 | The ECB system is responsible for creating and executing the buffer. Think of it as a synchronization point. For each such system, its own buffer is created and the world is attached to it. Each command buffer can only be associated with one world and one ECB system. 46 | 47 | The command buffer can be accessed from anywhere in your code via the ecs world: 48 | ```csharp 49 | using Leopotam.EcsLite; 50 | using JimboA.EcsLite.ECB; 51 | 52 | class UserRunSystem : IEcsRunSystem 53 | { 54 | public void Run (IEcsSystems systems) 55 | { 56 | ... 57 | var world = systems.GetWorld(); // getting the default world 58 | var ecb = world.GetCommandBufferFrom(); // getting buffer from your EcbSystem. You can cache it 59 | ref var laterComponent = ref ecb.Add(entity, out var cmdEntity) // when buffer will be executed a UserComponent will be added to entity 60 | // out parameter here return a packed ecb command. You can store it and execute that particular command later. Or just ignore it with out _ 61 | // for performance reasons you can pass cached EcsPool<> to commands methods: 62 | ref UserComponent laterComponent = ref ecb.Add(entity, _cachedUserComponentPool, out _); 63 | ... 64 | } 65 | } 66 | ``` 67 | In addition to the usual commands, you can also save specific sequences and execute them later: 68 | ```csharp 69 | class UserRunSystem : IEcsRunSystem 70 | { 71 | public void Run (IEcsSystems systems) 72 | { 73 | ... 74 | var world = systems.GetWorld(); // getting the default world 75 | var ecb = world.GetCommandBufferFrom(); // getting buffer from your EcbSystem 76 | ref var seq = ref ecb.Sequence(out var seqEntity); // getting special sequence 77 | seq.Add(entity1) = new UserComponent{ Value = blabla }; 78 | seq.Add(entity1); 79 | seq.Del(entity2); 80 | //you can store seqEntity and execute that sequence later 81 | ... 82 | } 83 | } 84 | ``` 85 | Now let's talk about execution. 86 | Commands are executed in ecb systems. 87 | 88 | > **Important!** EcbSystems only provides the API for execution. The order and rules of execution are determined by the user. 89 | 90 | For instance: 91 | ```csharp 92 | using Leopotam.EcsLite; 93 | using JimboA.EcsLite.ECB; 94 | 95 | class UserEcbSystem : EcbSystem, IEcsRunSystem 96 | { 97 | private EcsFilter _userFilter; 98 | ... 99 | public void Run (IEcsSystems systems) 100 | { 101 | foreach(var entity in _userFilter) 102 | { 103 | ExecuteCommandsOnEntity(entity); // execute all commands that belong to the entity 104 | ExecutePackedCommand(packedCmdEntity) // execute particular command or sequence 105 | // etc. 106 | } 107 | } 108 | ... 109 | } 110 | ``` 111 | All execution methods has summary, so just read it and use :) 112 | 113 | > **Important!** The command buffer is deterministic. In what order the commands were assigned in which order they will be executed if the user has not specified otherwise. 114 | 115 | # License 116 | The software is released under the terms of the [MIT license](./LICENSE.md). 117 | 118 | No personal support or any guarantees. 119 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c9c4759e0612ad04c924e07c55f9f2b8 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.jimboa.ecsliteentitycommandbuffer", 3 | "author": "JimboA", 4 | "displayName": "LeoECS Lite Entity Command Buffer", 5 | "description": "Entity command bufer for Leopotam EcsLite framework", 6 | "unity": "2020.3", 7 | "version": "2022.5.12", 8 | "keywords": [ 9 | "leoecslite", 10 | "leoecs", 11 | "ecs", 12 | "entitycommandbuffer" 13 | ], 14 | "dependencies": {}, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/JimboA/EcsLiteEntityCommandBuffer.git" 18 | } 19 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5bc6fe6e6b8c2d74b8e21f38f21c6ecf 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /src.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9ae502e9df5143f40b0632e363abb745 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /src/Components.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using Leopotam.EcsLite; 4 | 5 | namespace JimboA.EcsLite.ECB 6 | { 7 | // Why enums, custom lists etc? Why not just use components as commands and process them through filters? 8 | // Well, because of order. The correct and deterministic execution order of commands is important to us. 9 | // Since filters can't guarantee this, component-commands do not make sense. 10 | internal enum CommandType 11 | { 12 | Add, 13 | Del, 14 | Set, 15 | SetOrAdd 16 | } 17 | 18 | public struct CommandsSequenceComponent : IEcsAutoReset 19 | { 20 | internal FastList Commands; 21 | internal EntityCommandBuffer Buffer; 22 | 23 | public void AutoReset(ref CommandsSequenceComponent c) 24 | { 25 | if (c.Commands == null) 26 | c.Commands = new FastList(); 27 | else 28 | c.Commands.Clear(); 29 | } 30 | } 31 | 32 | internal struct CommandComponent 33 | { 34 | public EcsPackedEntity PackedEntity; 35 | public CommandType Type; 36 | public int EcbPoolId; 37 | public bool IsNew; 38 | } 39 | 40 | public static class EcbComponentsExtensions 41 | { 42 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 43 | internal static bool CheckSourceEntityIsAlive(in this CommandComponent command, EntityCommandBuffer buffer, out int sourceEntity) 44 | { 45 | var sourceWorld = buffer.GetWorld; 46 | if (command.IsNew) 47 | { 48 | sourceEntity = sourceWorld.NewEntity(); 49 | return true; 50 | } 51 | 52 | return command.PackedEntity.Unpack(sourceWorld, out sourceEntity); 53 | } 54 | 55 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 56 | internal static bool ExecuteAdd(ref this CommandComponent cmd, int commandEntity, EntityCommandBuffer buffer, out int sourceEntity) 57 | { 58 | if (!cmd.CheckSourceEntityIsAlive(buffer, out sourceEntity)) 59 | return false; 60 | 61 | var pool = buffer.Pools[cmd.EcbPoolId]; 62 | pool.AddToSourceEntity(commandEntity, sourceEntity); 63 | return true; 64 | } 65 | 66 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 67 | internal static bool ExecuteSet(ref this CommandComponent cmd, int commandEntity, EntityCommandBuffer buffer, out int sourceEntity) 68 | { 69 | if (!cmd.CheckSourceEntityIsAlive(buffer, out sourceEntity)) 70 | return false; 71 | 72 | var pool = buffer.Pools[cmd.EcbPoolId]; 73 | pool.SetToSourceEntity(commandEntity, sourceEntity); 74 | return true; 75 | } 76 | 77 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 78 | internal static bool ExecuteSetOrAdd(ref this CommandComponent cmd, int commandEntity, EntityCommandBuffer buffer, out int sourceEntity) 79 | { 80 | if (!cmd.CheckSourceEntityIsAlive(buffer, out sourceEntity)) 81 | return false; 82 | 83 | var pool = buffer.Pools[cmd.EcbPoolId]; 84 | pool.SetOrAddToSourceEntity(commandEntity, sourceEntity); 85 | return true; 86 | } 87 | 88 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 89 | internal static bool ExecuteDel(ref this CommandComponent cmd, EntityCommandBuffer buffer, out int sourceEntity) 90 | { 91 | if (!cmd.CheckSourceEntityIsAlive(buffer, out sourceEntity)) 92 | return false; 93 | 94 | var pool = buffer.Pools[cmd.EcbPoolId]; 95 | pool.Del(sourceEntity); 96 | return true; 97 | } 98 | 99 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 100 | internal static bool ExecuteCommand(this ref CommandComponent command, int cmdEntity, EntityCommandBuffer buffer, out int sourceEntity) 101 | { 102 | switch (command.Type) 103 | { 104 | case CommandType.Add: 105 | return command.ExecuteAdd(cmdEntity, buffer, out sourceEntity); 106 | case CommandType.Set: 107 | return command.ExecuteSet(cmdEntity, buffer, out sourceEntity); 108 | case CommandType.SetOrAdd: 109 | return command.ExecuteSetOrAdd(cmdEntity, buffer, out sourceEntity); 110 | case CommandType.Del: 111 | return command.ExecuteDel(buffer, out sourceEntity); 112 | default: 113 | throw new ArgumentOutOfRangeException(); 114 | } 115 | } 116 | 117 | // #region EcbSequence 118 | // 119 | // public static ref CommandsSequenceComponent Sequence(this EntityCommandBuffer buffer) 120 | // { 121 | // var seqEntity = buffer.GetBufferWorld.NewEntity(); 122 | // ref var sequence = ref buffer.CommandsSequencePool.Add(seqEntity); 123 | // sequence.Buffer = buffer; 124 | // sequence.SequenceEntity = seqEntity; 125 | // return ref sequence; 126 | // } 127 | // 128 | // public static ref CommandsSequenceComponent Add(ref this CommandsSequenceComponent sequence, int entity, TComponent value) where TComponent : struct 129 | // { 130 | // var cmdEntity = sequence.Buffer.CreateCommand(entity, CommandType.Add, out var ecbPool); 131 | // sequence.Commands.Add(cmdEntity); 132 | // ecbPool.BufferPool.Add(cmdEntity) = value; 133 | // return ref sequence; 134 | // } 135 | // 136 | // public static ref CommandsSequenceComponent Add(ref this CommandsSequenceComponent sequence, int entity, EcsPool sourcePool, TComponent value) where TComponent : struct 137 | // { 138 | // var cmdEntity = sequence.Buffer.CreateCommand(entity, CommandType.Add, out var ecbPool); 139 | // sequence.Commands.Add(cmdEntity); 140 | // ecbPool.BufferPool.Add(cmdEntity) = value; 141 | // return ref sequence; 142 | // } 143 | // 144 | // 145 | // public static ref CommandsSequenceComponent Set(ref this CommandsSequenceComponent sequence, int entity, TComponent value) where TComponent : struct 146 | // { 147 | // var cmdEntity = sequence.Buffer.CreateCommand(entity, CommandType.Set, out var ecbPool); 148 | // sequence.Commands.Add(cmdEntity); 149 | // ecbPool.BufferPool.Add(cmdEntity) = value; 150 | // return ref sequence; 151 | // } 152 | // 153 | // public static ref CommandsSequenceComponent Set(ref this CommandsSequenceComponent sequence, int entity, EcsPool sourcePool, TComponent value) where TComponent : struct 154 | // { 155 | // var cmdEntity = sequence.Buffer.CreateCommand(entity, CommandType.Set, out var ecbPool); 156 | // sequence.Commands.Add(cmdEntity); 157 | // ecbPool.BufferPool.Add(cmdEntity) = value; 158 | // return ref sequence; 159 | // } 160 | // 161 | // public static ref CommandsSequenceComponent SetOrAdd(ref this CommandsSequenceComponent sequence, int entity, TComponent value) where TComponent : struct 162 | // { 163 | // var cmdEntity = sequence.Buffer.CreateCommand(entity, CommandType.SetOrAdd, out var ecbPool); 164 | // sequence.Commands.Add(cmdEntity); 165 | // ecbPool.BufferPool.Add(cmdEntity) = value; 166 | // return ref sequence; 167 | // } 168 | // 169 | // public static ref CommandsSequenceComponent SetOrAdd(ref this CommandsSequenceComponent sequence, int entity, EcsPool sourcePool, TComponent value) where TComponent : struct 170 | // { 171 | // var cmdEntity = sequence.Buffer.CreateCommand(entity, CommandType.SetOrAdd, out var ecbPool); 172 | // sequence.Commands.Add(cmdEntity); 173 | // ecbPool.BufferPool.Add(cmdEntity) = value; 174 | // return ref sequence; 175 | // } 176 | // 177 | // public static ref CommandsSequenceComponent Del(ref this CommandsSequenceComponent sequence, int entity) where TComponent : struct 178 | // { 179 | // var cmdEntity = sequence.Buffer.CreateCommand(entity, CommandType.Del, out var ecbPool); 180 | // sequence.Commands.Add(cmdEntity); 181 | // return ref sequence; 182 | // } 183 | // 184 | // public static EcsPackedEntity End(this ref CommandsSequenceComponent sequence) 185 | // { 186 | // return sequence.Buffer.GetWorld.PackEntity(sequence.SequenceEntity); 187 | // } 188 | // 189 | // #endregion 190 | 191 | #region ecbSequence 192 | 193 | public static ref CommandsSequenceComponent Sequence(this EntityCommandBuffer buffer, out EcsPackedEntity packedSeqEntity) 194 | { 195 | var world = buffer.GetBufferWorld; 196 | var seqEntity = world.NewEntity(); 197 | ref var sequence = ref buffer.CommandsSequencePool.GetOrAdd(seqEntity); 198 | sequence.Buffer = buffer; 199 | packedSeqEntity = world.PackEntity(seqEntity); 200 | buffer.Sequences.Add(seqEntity); 201 | return ref sequence; 202 | } 203 | 204 | public static ref TComponent Add(ref this CommandsSequenceComponent sequence, int entity) where TComponent : struct 205 | { 206 | var cmdEntity = sequence.Buffer.CreateCommand(entity, CommandType.Add, out var ecbPool); 207 | sequence.Commands.Add(cmdEntity); 208 | return ref ecbPool.BufferPool.Add(cmdEntity); 209 | } 210 | 211 | public static ref TComponent Add(ref this CommandsSequenceComponent sequence, int entity, EcsPool sourcePool) where TComponent : struct 212 | { 213 | var cmdEntity = sequence.Buffer.CreateCommand(entity, CommandType.Add, out var ecbPool); 214 | sequence.Commands.Add(cmdEntity); 215 | return ref ecbPool.BufferPool.Add(cmdEntity); 216 | } 217 | 218 | public static ref TComponent Set(ref this CommandsSequenceComponent sequence, int entity) where TComponent : struct 219 | { 220 | var cmdEntity = sequence.Buffer.CreateCommand(entity, CommandType.Set, out var ecbPool); 221 | sequence.Commands.Add(cmdEntity); 222 | return ref ecbPool.BufferPool.Add(cmdEntity); 223 | } 224 | 225 | public static ref TComponent Set(ref this CommandsSequenceComponent sequence, int entity, EcsPool sourcePool) where TComponent : struct 226 | { 227 | var cmdEntity = sequence.Buffer.CreateCommand(entity, CommandType.Set, out var ecbPool); 228 | sequence.Commands.Add(cmdEntity); 229 | return ref ecbPool.BufferPool.Add(cmdEntity); 230 | } 231 | 232 | public static ref TComponent SetOrAdd(ref this CommandsSequenceComponent sequence, int entity) where TComponent : struct 233 | { 234 | var cmdEntity = sequence.Buffer.CreateCommand(entity, CommandType.SetOrAdd, out var ecbPool); 235 | sequence.Commands.Add(cmdEntity); 236 | return ref ecbPool.BufferPool.Add(cmdEntity); 237 | } 238 | 239 | public static ref TComponent SetOrAdd(ref this CommandsSequenceComponent sequence, int entity, EcsPool sourcePool) where TComponent : struct 240 | { 241 | var cmdEntity = sequence.Buffer.CreateCommand(entity, CommandType.SetOrAdd, out var ecbPool); 242 | sequence.Commands.Add(cmdEntity); 243 | return ref ecbPool.BufferPool.Add(cmdEntity); 244 | } 245 | 246 | public static void Del(ref this CommandsSequenceComponent sequence, int entity) where TComponent : struct 247 | { 248 | var cmdEntity = sequence.Buffer.CreateCommand(entity, CommandType.Add, out _); 249 | sequence.Commands.Add(cmdEntity); 250 | } 251 | 252 | #endregion 253 | } 254 | } -------------------------------------------------------------------------------- /src/Components.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fdc8478a1be0cca408920dabd7b7b92a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /src/ECBSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using Leopotam.EcsLite; 4 | 5 | namespace JimboA.EcsLite.ECB 6 | { 7 | public abstract class EcbSystem : IEcsPostDestroySystem 8 | { 9 | protected EntityCommandBuffer CommandBuffer; 10 | private Type _cachedType; 11 | 12 | protected EcbSystem(EcsWorld sourceWorld, EcsWorld.Config config = default) 13 | { 14 | CommandBuffer = new EntityCommandBuffer(sourceWorld, config); 15 | _cachedType = GetType(); 16 | if (EntityCommandBuffer.Map.TryGetValue(_cachedType, out var list)) 17 | list.Add(CommandBuffer); 18 | else 19 | EntityCommandBuffer.Map.Add(_cachedType, new FastList {CommandBuffer}); 20 | } 21 | 22 | /// 23 | /// Execute all commands that belong to the entity. 24 | /// 25 | /// 26 | /// Set false if you want to clear Command Buffer manually. True by default 27 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 28 | protected void ExecuteCommandsOnEntity(int entity, bool autoClear = true) => CommandBuffer.ExecuteCommandsOnEntity(entity, autoClear); 29 | 30 | /// 31 | /// Execute concrete packed command. 32 | /// 33 | /// 34 | /// Set false if you want to clear Command Buffer manually. True by default 35 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 36 | protected void ExecutePackedCommand(ref EcsPackedEntity sequence, bool autoClear = true) => CommandBuffer.ExecutePackedCommandEntity(ref sequence, autoClear); 37 | 38 | /// 39 | /// Execute one Command Buffer step at index. You can use it in for loop with your custom logic. 40 | /// 41 | /// 42 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 43 | protected void ExecuteStep(int index) => CommandBuffer.ExecuteStep(index); 44 | 45 | /// 46 | /// Execute one custom sequences step in Command Buffer at index. You can use it in for loop with your custom logic 47 | /// 48 | /// 49 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 50 | protected void ExecuteSequenceStep(int index) => CommandBuffer.ExecuteSequenceStep(index); 51 | 52 | /// 53 | /// Execute all commands 54 | /// 55 | /// 56 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 57 | protected void ExecuteAll(bool autoClear = true) => CommandBuffer.ExecuteAll(autoClear); 58 | 59 | /// 60 | /// Manually clear Command Buffer 61 | /// 62 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 63 | protected void ClearCommandBuffer() => CommandBuffer.ClearAll(); 64 | 65 | public void PostDestroy(IEcsSystems systems) 66 | { 67 | CommandBuffer.Destroy(_cachedType); 68 | CommandBuffer = null; 69 | _cachedType = null; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/ECBSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c3104d268859e6a41b83f19ae08bdf2b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /src/EcbPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Leopotam.EcsLite; 3 | 4 | namespace JimboA.EcsLite.ECB 5 | { 6 | internal class EcbPool : IEcbPool where TComponent : struct 7 | { 8 | public EcsPool SourcePool; 9 | public EcsPool BufferPool; 10 | 11 | public EcbPool(EcsWorld sourceWorld, EcsWorld bufferWorld) 12 | { 13 | SourcePool = sourceWorld.GetPool(); 14 | BufferPool = bufferWorld.GetPool(); 15 | } 16 | 17 | public EcbPool(EcsWorld bufferWorld, EcsPool sourcePool) 18 | { 19 | SourcePool = sourcePool; 20 | BufferPool = bufferWorld.GetPool(); 21 | } 22 | 23 | public void AddToSourceEntity(int bufferEntity, int sourceEntity) 24 | { 25 | ref var sourceComponent = ref SourcePool.Add(sourceEntity); 26 | ref var bufferComponent = ref BufferPool.Get(bufferEntity); 27 | sourceComponent = bufferComponent; 28 | } 29 | 30 | public void SetToSourceEntity(int bufferEntity, int sourceEntity) 31 | { 32 | ref var sourceComponent = ref SourcePool.Get(sourceEntity); 33 | ref var bufferComponent = ref BufferPool.Get(bufferEntity); 34 | sourceComponent = bufferComponent; 35 | } 36 | 37 | public void SetOrAddToSourceEntity(int bufferEntity, int sourceEntity) 38 | { 39 | ref var sourceComponent = ref SourcePool.GetOrAdd(sourceEntity); 40 | ref var bufferComponent = ref BufferPool.Get(bufferEntity); 41 | sourceComponent = bufferComponent; 42 | } 43 | 44 | public void Del(int sourceEntity) 45 | { 46 | SourcePool.Del(sourceEntity); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/EcbPool.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6916d0da710e4650875d14a8b5b68b12 3 | timeCreated: 1650412677 -------------------------------------------------------------------------------- /src/EntityCommandBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using Leopotam.EcsLite; 5 | 6 | namespace JimboA.EcsLite.ECB 7 | { 8 | public class EntityCommandBuffer 9 | { 10 | internal static Dictionary> Map = new Dictionary>(); 11 | 12 | internal EcsPool CommandsSequencePool; 13 | internal EcsPool CommandPool; 14 | internal IEcbPool[] Pools; 15 | internal FastList Sequences; 16 | internal FastList Commands; 17 | 18 | internal EcsWorld GetBufferWorld => _bufferWorld; 19 | public EcsWorld GetWorld => _sourceWorld; 20 | public int Length => Commands.Length; 21 | 22 | private EcsWorld _sourceWorld; 23 | private EcsWorld _bufferWorld; 24 | private int[] _sparseEntities; 25 | 26 | internal EntityCommandBuffer(EcsWorld sourceWorld, EcsWorld.Config config = default) 27 | { 28 | _sourceWorld = sourceWorld; 29 | _bufferWorld = new EcsWorld(config); 30 | var poolsCap = config.Pools > 0 ? config.Pools : 512; 31 | var entitiesCap = config.Entities > 0 ? config.Entities : 512; 32 | Pools = new IEcbPool[poolsCap]; 33 | CommandsSequencePool = _bufferWorld.GetPool(); 34 | CommandPool = _bufferWorld.GetPool(); 35 | Sequences = new FastList(entitiesCap); 36 | Commands = new FastList(entitiesCap); 37 | _sparseEntities = new int[entitiesCap]; 38 | } 39 | 40 | [MethodImpl (MethodImplOptions.AggressiveInlining)] 41 | internal void ExecuteCommandsOnEntity(int entity, bool autoClear = true) 42 | { 43 | if (entity >= _sparseEntities.Length || entity < 0) 44 | return; 45 | 46 | var commandsEntity = _sparseEntities[entity]; 47 | if(commandsEntity == 0) 48 | return; 49 | 50 | ref var commands = ref CommandsSequencePool.Get(commandsEntity + 1); 51 | if (autoClear) 52 | { 53 | foreach (var cmdEntity in commands.Commands) 54 | { 55 | ref var command = ref CommandPool.Get(cmdEntity); 56 | command.ExecuteCommand(cmdEntity, this, out _); 57 | _bufferWorld.DelEntity(cmdEntity); 58 | } 59 | _sparseEntities[entity] = 0; 60 | } 61 | else 62 | { 63 | foreach (var cmdEntity in commands.Commands) 64 | { 65 | ref var command = ref CommandPool.Get(cmdEntity); 66 | command.ExecuteCommand(cmdEntity, this, out _); 67 | } 68 | } 69 | } 70 | 71 | [MethodImpl (MethodImplOptions.AggressiveInlining)] 72 | internal void ExecutePackedCommandEntity(ref EcsPackedEntity packedEntity, bool autoClear = true) 73 | { 74 | if(!packedEntity.Unpack(_bufferWorld, out var entity)) 75 | return; 76 | 77 | if (CommandsSequencePool.TryGet(entity, out var sequence)) 78 | { 79 | if (autoClear) 80 | { 81 | foreach (var cmdEntity in sequence.Commands) 82 | { 83 | ref var cmd = ref CommandPool.Get(cmdEntity); 84 | cmd.ExecuteCommand(cmdEntity, this, out var sourceEntity); 85 | _sparseEntities[sourceEntity] = 0; 86 | _bufferWorld.DelEntity(cmdEntity); 87 | } 88 | _bufferWorld.DelEntity(entity); 89 | return; 90 | } 91 | foreach (var cmdEntity in sequence.Commands) 92 | { 93 | ref var cmd = ref CommandPool.Get(cmdEntity); 94 | cmd.ExecuteCommand(cmdEntity, this, out _); 95 | } 96 | return; 97 | } 98 | 99 | if (CommandPool.TryGet(entity, out var command)) 100 | { 101 | command.ExecuteCommand(entity, this, out var sourceEntity); 102 | if (autoClear) 103 | { 104 | _bufferWorld.DelEntity(entity); 105 | _sparseEntities[sourceEntity] = 0; 106 | } 107 | } 108 | } 109 | 110 | 111 | [MethodImpl (MethodImplOptions.AggressiveInlining)] 112 | internal void ExecuteStep(int index) 113 | { 114 | if(index < 0 || index >= Commands.Length) 115 | return; 116 | 117 | var cmdEntity = Commands[index]; 118 | ref var command = ref CommandPool.Get(cmdEntity); 119 | command.ExecuteCommand(cmdEntity, this, out _); 120 | } 121 | 122 | [MethodImpl (MethodImplOptions.AggressiveInlining)] 123 | internal void ExecuteSequenceStep(int index) 124 | { 125 | if(index < 0 || index >= Sequences.Length) 126 | return; 127 | 128 | var seqEntity = Sequences[index]; 129 | ref var sequence = ref CommandsSequencePool.Get(seqEntity); 130 | foreach (var cmdEntity in sequence.Commands) 131 | { 132 | ref var command = ref CommandPool.Get(cmdEntity); 133 | command.ExecuteCommand(cmdEntity, this, out _); 134 | } 135 | } 136 | 137 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 138 | internal void ExecuteAll(bool autoClear = true) 139 | { 140 | if (autoClear) 141 | { 142 | foreach (var cmdEntity in Commands) 143 | { 144 | ref var command = ref CommandPool.Get(cmdEntity); 145 | command.ExecuteCommand(cmdEntity, this, out var sourceEntity); 146 | _bufferWorld.DelEntity(cmdEntity); 147 | _sparseEntities[sourceEntity] = 0; 148 | } 149 | Commands.Clear(); 150 | Sequences.Clear(); 151 | } 152 | else 153 | { 154 | foreach (var cmdEntity in Commands) 155 | { 156 | ref var command = ref CommandPool.Get(cmdEntity); 157 | command.ExecuteCommand(cmdEntity, this, out _); 158 | } 159 | } 160 | } 161 | 162 | internal void ClearAll() 163 | { 164 | foreach (var seqEntity in Sequences) 165 | { 166 | _bufferWorld.DelEntity(seqEntity); 167 | } 168 | 169 | foreach (var cmdEntity in Commands) 170 | { 171 | _bufferWorld.DelEntity(cmdEntity); 172 | } 173 | 174 | Array.Clear(_sparseEntities, 0, _sparseEntities.Length); 175 | Commands.Clear(); 176 | Sequences.Clear(); 177 | } 178 | 179 | internal void Destroy(Type key) 180 | { 181 | ClearAll(); 182 | _sourceWorld = null; 183 | _bufferWorld = null; 184 | Commands = null; 185 | Pools = null; 186 | _sparseEntities = null; 187 | CommandsSequencePool = null; 188 | CommandPool = null; 189 | if (Map.TryGetValue(key, out var list)) 190 | list.Remove(this); 191 | } 192 | 193 | internal int CreateCommand(int entity, CommandType type, out EcbPool ecbPool) where TComponent : struct 194 | { 195 | var sourcePool = _sourceWorld.GetPool(); 196 | return CreateCommand(entity, type, sourcePool, out ecbPool); 197 | } 198 | 199 | [MethodImpl (MethodImplOptions.AggressiveInlining)] 200 | internal int CreateCommand(int entity, CommandType type, EcsPool sourcePool, out EcbPool ecbPool) where TComponent : struct 201 | { 202 | var typeIndex = sourcePool.GetId(); 203 | ecbPool = GetEcbPool(typeIndex, sourcePool); 204 | var commandsEntity = GetCommandsEntity(entity); 205 | ref var commands = ref CommandsSequencePool.GetOrAdd(commandsEntity); 206 | var cmdEntity = CreateCommandEntity(type, entity, typeIndex); 207 | commands.Commands.Add(cmdEntity); 208 | Commands.Add(cmdEntity); 209 | return cmdEntity; 210 | } 211 | 212 | [MethodImpl (MethodImplOptions.AggressiveInlining)] 213 | private EcbPool GetEcbPool(int poolId, EcsPool sourcePool) where TComponent : struct 214 | { 215 | if(poolId >= Pools.Length) 216 | Array.Resize(ref Pools, poolId << 1); 217 | 218 | var ecbPool = (EcbPool)Pools[poolId]; 219 | if (ecbPool == null) 220 | { 221 | ecbPool = new EcbPool(_bufferWorld, sourcePool); 222 | Pools[poolId] = ecbPool; 223 | } 224 | 225 | return ecbPool; 226 | } 227 | 228 | [MethodImpl (MethodImplOptions.AggressiveInlining)] 229 | private int GetCommandsEntity(int entity) 230 | { 231 | if(entity >= _sparseEntities.Length) 232 | Array.Resize(ref _sparseEntities, entity << 1); 233 | 234 | var commandsEntity = _sparseEntities[entity]; 235 | if (commandsEntity == 0) 236 | { 237 | commandsEntity = _bufferWorld.NewEntity(); 238 | _sparseEntities[entity] = commandsEntity + 1; 239 | } 240 | else 241 | { 242 | commandsEntity--; 243 | } 244 | 245 | return commandsEntity; 246 | } 247 | 248 | [MethodImpl (MethodImplOptions.AggressiveInlining)] 249 | private int CreateCommandEntity(CommandType type, int entity, int ecbPoolId) 250 | { 251 | var commandEntity = _bufferWorld.NewEntity(); 252 | CommandPool.Add(commandEntity) = new CommandComponent 253 | { 254 | PackedEntity = _sourceWorld.PackEntity(entity), 255 | Type = type, 256 | EcbPoolId = ecbPoolId, 257 | IsNew = entity == -1 258 | }; 259 | return commandEntity; 260 | } 261 | } 262 | 263 | public static class EcbExtensions 264 | { 265 | public static EntityCommandBuffer GetCommandBufferFrom(this EcsWorld world) where TEcbSystem : EcbSystem 266 | { 267 | var key = typeof(TEcbSystem); 268 | if (EntityCommandBuffer.Map.TryGetValue(key, out var buffers)) 269 | { 270 | foreach (var buffer in buffers) 271 | { 272 | if (buffer.GetWorld == world) 273 | return buffer; 274 | } 275 | } 276 | 277 | #if DEBUG && !LEOECSLITE_NO_SANITIZE_CHECKS 278 | throw new NullReferenceException($"EntityCommandBuffer from {nameof(TEcbSystem)} doesn't exist."); 279 | #endif 280 | return null; 281 | } 282 | 283 | #region PublicAPI 284 | 285 | public static ref TComponent Add(this EntityCommandBuffer buffer, int entity, out EcsPackedEntity packedCommand) where TComponent : struct 286 | { 287 | var cmdEntity = buffer.CreateCommand(entity, CommandType.Add, out var ecbPool); 288 | packedCommand = buffer.GetBufferWorld.PackEntity(cmdEntity); 289 | return ref ecbPool.BufferPool.Add(cmdEntity); 290 | } 291 | 292 | public static ref TComponent Add(this EntityCommandBuffer buffer, int entity, EcsPool sourcePool, out EcsPackedEntity packedCommand) where TComponent : struct 293 | { 294 | var cmdEntity = buffer.CreateCommand(entity, CommandType.Add, sourcePool, out var ecbPool); 295 | packedCommand = buffer.GetBufferWorld.PackEntity(cmdEntity); 296 | return ref ecbPool.BufferPool.Add(cmdEntity); 297 | } 298 | 299 | public static ref TComponent Set(this EntityCommandBuffer buffer, int entity, out EcsPackedEntity packedCommand) where TComponent : struct 300 | { 301 | var cmdEntity = buffer.CreateCommand(entity, CommandType.Set, out var ecbPool); 302 | packedCommand = buffer.GetBufferWorld.PackEntity(cmdEntity); 303 | return ref ecbPool.BufferPool.Add(cmdEntity); 304 | } 305 | 306 | public static ref TComponent Set(this EntityCommandBuffer buffer, int entity, EcsPool sourcePool, out EcsPackedEntity packedCommand) where TComponent : struct 307 | { 308 | var cmdEntity = buffer.CreateCommand(entity, CommandType.Set, sourcePool, out var ecbPool); 309 | packedCommand = buffer.GetBufferWorld.PackEntity(cmdEntity); 310 | return ref ecbPool.BufferPool.Add(cmdEntity); 311 | } 312 | 313 | public static ref TComponent SetOrAdd(this EntityCommandBuffer buffer, int entity, out EcsPackedEntity packedCommand) where TComponent : struct 314 | { 315 | var cmdEntity = buffer.CreateCommand(entity, CommandType.SetOrAdd, out var ecbPool); 316 | packedCommand = buffer.GetBufferWorld.PackEntity(cmdEntity); 317 | return ref ecbPool.BufferPool.Add(cmdEntity); 318 | } 319 | 320 | public static ref TComponent SetOrAdd(this EntityCommandBuffer buffer, int entity, EcsPool sourcePool, out EcsPackedEntity packedCommand) where TComponent : struct 321 | { 322 | var cmdEntity = buffer.CreateCommand(entity, CommandType.SetOrAdd, sourcePool, out var ecbPool); 323 | packedCommand = buffer.GetBufferWorld.PackEntity(cmdEntity); 324 | return ref ecbPool.BufferPool.Add(cmdEntity); 325 | } 326 | 327 | public static void Del(this EntityCommandBuffer buffer, int entity, out EcsPackedEntity packedCommand) where TComponent : struct 328 | { 329 | var cmdEntity = buffer.CreateCommand(entity, CommandType.Del, out _); 330 | packedCommand = buffer.GetBufferWorld.PackEntity(cmdEntity); 331 | } 332 | 333 | public static void Del(this EntityCommandBuffer buffer, int entity, EcsPool sourcePool, out EcsPackedEntity packedCommand) where TComponent : struct 334 | { 335 | var cmdEntity = buffer.CreateCommand(entity, CommandType.Del, sourcePool, out _); 336 | packedCommand = buffer.GetBufferWorld.PackEntity(cmdEntity); 337 | } 338 | 339 | #endregion 340 | 341 | [MethodImpl (MethodImplOptions.AggressiveInlining)] 342 | internal static ref T GetOrAdd(this EcsPool pool, int entity) where T : struct 343 | { 344 | if (pool.Has(entity)) 345 | return ref pool.Get(entity); 346 | 347 | return ref pool.Add(entity); 348 | } 349 | 350 | [MethodImpl (MethodImplOptions.AggressiveInlining)] 351 | internal static bool TryGet(this EcsPool pool, int entity, out T component) where T : struct 352 | { 353 | if (pool.Has(entity)) 354 | { 355 | component = pool.Get(entity); 356 | return true; 357 | } 358 | component = default; 359 | return false; 360 | } 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /src/EntityCommandBuffer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 60c89619b539c4844b6d27c2f86f2ebc 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /src/FastList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace JimboA.EcsLite.ECB 7 | { 8 | [System.Serializable] 9 | public class FastList : IEnumerable 10 | { 11 | public T[] Items; 12 | public ref T this[int index] => ref Items[index]; 13 | public int Length => _length; 14 | 15 | private int _length; 16 | private IEqualityComparer _comparer; 17 | 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public FastList() 20 | { 21 | Items = new T[6]; 22 | _length = 0; 23 | _comparer = EqualityComparer.Default; 24 | } 25 | 26 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 27 | public FastList(int cap = 0) 28 | { 29 | Items = new T[cap > 0 ? cap : 6]; 30 | _length = 0; 31 | _comparer = EqualityComparer.Default; 32 | } 33 | 34 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 35 | public FastList(IEqualityComparer comparer, int cap = 0) 36 | { 37 | Items = new T[cap > 0 ? cap : 6]; 38 | _length = 0; 39 | _comparer = comparer; 40 | } 41 | 42 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 43 | public void Add(T item) 44 | { 45 | if (_length >= Items.Length) 46 | { 47 | var arr = new T[_length << 1]; 48 | Array.Copy(Items, arr, _length); 49 | Items = arr; 50 | } 51 | 52 | Items[_length++] = item; 53 | } 54 | 55 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 56 | public void Add(T item, out int index) 57 | { 58 | if (_length >= Items.Length) 59 | { 60 | var arr = new T[_length << 1]; 61 | Array.Copy(Items, arr, _length); 62 | Items = arr; 63 | } 64 | 65 | Items[_length++] = item; 66 | index = _length - 1; 67 | } 68 | 69 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 70 | public void AddRange(params T[] items) 71 | { 72 | for (int i = 0; i < items.Length; i++) 73 | { 74 | if (_length >= Items.Length) 75 | { 76 | var arr = new T[_length << 1]; 77 | Array.Copy(Items, arr, _length); 78 | Items = arr; 79 | } 80 | 81 | Items[_length++] = items[i]; 82 | } 83 | } 84 | 85 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 86 | public void Insert(T item, int index) 87 | { 88 | if(index >= Items.Length || index < 0) 89 | return; 90 | 91 | if (_length >= Items.Length) 92 | { 93 | var arr = new T[_length << 1]; 94 | Array.Copy(Items, arr, _length); 95 | Items = arr; 96 | } 97 | 98 | Array.Copy(Items, index, Items, index + 1, _length++); 99 | Items[index] = item; 100 | } 101 | 102 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 103 | public bool Remove(T item) 104 | { 105 | int index = IndexOf(ref item); 106 | var removed = index > -1; 107 | if (removed && index < --_length) 108 | Array.Copy(Items, index + 1, Items, index, _length - index); 109 | return removed; 110 | } 111 | 112 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 113 | public bool Remove(ref T item) 114 | { 115 | int index = IndexOf(ref item); 116 | var removed = index > -1; 117 | if (removed && index < --_length) 118 | Array.Copy(Items, index + 1, Items, index, _length - index); 119 | return removed; 120 | } 121 | 122 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 123 | public bool Remove(T item, out int index) 124 | { 125 | index = IndexOf(ref item); 126 | var removed = index > -1; 127 | if (removed && index < --_length) 128 | Array.Copy(Items, index + 1, Items, index, _length - index); 129 | return removed; 130 | } 131 | 132 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 133 | public bool Remove(ref T item, out int index) 134 | { 135 | index = IndexOf(ref item); 136 | var removed = index > -1; 137 | if (removed && index < --_length) 138 | Array.Copy(Items, index + 1, Items, index, _length - index); 139 | return removed; 140 | } 141 | 142 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 143 | public void RemoveAt(int index) 144 | { 145 | Array.Copy(Items, index + 1, Items, index, --_length - index); 146 | } 147 | 148 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 149 | public void ClearAt(int index) 150 | { 151 | if (index <= 0) 152 | { 153 | Clear(); 154 | return; 155 | } 156 | _length = index; 157 | } 158 | 159 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 160 | public int IndexOf(ref T item) 161 | { 162 | for (int i = _length - 1; i >= 0; i--) 163 | { 164 | if (_comparer.Equals(Items[i], item)) 165 | return i; 166 | } 167 | 168 | return -1; 169 | } 170 | 171 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 172 | public void Clear() 173 | { 174 | _length = 0; 175 | } 176 | 177 | public T[] ToArray() 178 | { 179 | var arr = new T[_length]; 180 | Array.Copy(Items, arr, _length); 181 | return arr; 182 | } 183 | 184 | public void Sort(int index, int length) 185 | { 186 | Array.Sort(Items, index, length); 187 | } 188 | 189 | public void Sort(TKeys[] keys, int index, int length) 190 | { 191 | Array.Sort(keys, Items, index, length); 192 | } 193 | 194 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 195 | 196 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 197 | 198 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 199 | public Enumerator GetEnumerator() 200 | { 201 | return new Enumerator(this); 202 | } 203 | 204 | [System.Serializable] 205 | public struct Enumerator : IEnumerator 206 | { 207 | public FastList List; 208 | public int Index; 209 | 210 | public T Current => List.Items[Index]; 211 | 212 | object IEnumerator.Current => Current; 213 | 214 | internal Enumerator(FastList list) 215 | { 216 | List = list; 217 | Index = list._length; 218 | } 219 | 220 | [MethodImpl (MethodImplOptions.AggressiveInlining)] 221 | public bool MoveNext() 222 | { 223 | return --Index >= 0; 224 | } 225 | 226 | [MethodImpl (MethodImplOptions.AggressiveInlining)] 227 | public void Reset() 228 | { 229 | Index = List._length; 230 | } 231 | 232 | [MethodImpl (MethodImplOptions.AggressiveInlining)] 233 | public void Dispose() 234 | { 235 | List = null; 236 | } 237 | } 238 | } 239 | } -------------------------------------------------------------------------------- /src/FastList.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3934ecf6075f2c9469048e6c6d34ab74 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /src/IEcbPool.cs: -------------------------------------------------------------------------------- 1 | namespace JimboA.EcsLite.ECB 2 | { 3 | internal interface IEcbPool 4 | { 5 | void AddToSourceEntity(int bufferEntity, int sourceEntity); 6 | void SetToSourceEntity(int bufferEntity, int sourceEntity); 7 | void SetOrAddToSourceEntity(int bufferEntity, int sourceEntity); 8 | void Del(int sourceEntity); 9 | } 10 | } -------------------------------------------------------------------------------- /src/IEcbPool.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 161096c2aed84fc3b554aeb33f26d2f2 3 | timeCreated: 1650412693 --------------------------------------------------------------------------------