1 ///
2 /// Defines an architecture to manage entities, and several entity-related
3 /// events. Components must be defined as classes (for internal storage).
4 ///
5 /// Copyright: Copyright (c) 2014 James Zhu.
6 ///
7 /// License: MIT License (Expat). See accompanying file LICENSE.
8 ///
9 /// Authors: James Zhu <github.com/jzhu98>
10 ///
11 
12 module star.entity;
13 
14 import std.container;
15 import std.algorithm : filter;
16 
17 import star.event;
18 
19 /// An id encapsulates an index (unique ulong in an entity manager)
20 /// and a tag (to check if the entity is in sync (valid) with the manager).
21 struct ID
22 {
23 public:
24     /// An invalid id, used for invalidating entities.
25     static immutable ID INVALID = ID(0, 0);
26 
27     /// Construct an id from a 64-bit integer:
28     /// A concatenated 32-bit index and 32-bit tag.
29     this(ulong id) pure nothrow @safe
30     {
31         _id = id;
32     }
33 
34     /// Construct an id from a 32-bit index and a 32-bit tag.
35     this(uint index, uint tag) pure nothrow @safe
36     {
37         this(cast(ulong) index << 32UL | (cast(ulong) tag));
38     }
39 
40     /// Return the index of the ID.
41     inout(uint) index() inout pure nothrow @property @safe
42     {
43         return cast(uint)(_id >> 32UL);
44     }
45 
46     unittest
47     {
48         auto id1 = ID(0x0000000100000002UL);
49         auto id2 = ID(3U, 4U);
50         assert(id1.index == 1U);
51         assert(id2.index == 3U);
52     }
53 
54     /// Return the tag of the ID.
55     inout(uint) tag() inout pure nothrow @property @safe
56     {
57         return cast(uint) (_id);
58     }
59 
60     unittest
61     {
62         auto id1 = ID((12UL << 32) + 13UL);
63         auto id2 = ID(210U, 5U);
64         assert(id1.tag == 13U);
65         assert(id2.tag == 5U);
66     }
67 
68     string toString() const pure @safe
69     {
70         return "ID(" ~ std.conv.to!string(this.index) ~ ", " ~ std.conv.to!string(this.tag) ~ ")";
71     }
72 
73     /// Equals operator (check for equality).
74     bool opEquals()(auto ref const ID other) const pure nothrow @safe
75     {
76         return _id == other._id;
77     }
78 
79     unittest
80     {
81         auto id1 = ID(12U, 32U);
82         auto id2 = ID(12U, 32U);
83         auto id3 = ID(13U, 32U);
84         assert(id1 == id2);
85         assert(id1 != id3);
86         assert(id2 != id3);
87     }
88 
89     /// Comparison operators (check for greater / less than).
90     int opCmp(ref const ID other) const pure nothrow @safe
91     {
92         if (_id > other._id)
93         {
94             return 1;
95         }
96         else if (_id == other._id)
97         {
98             return 0;
99         }
100         else
101         {
102             return -1;
103         }
104     }
105 
106     unittest
107     {
108         auto id1 = ID(1U, 10U);
109         auto id2 = ID(1U, 11U);
110         auto id3 = ID(2U, 1U);
111         assert(id1 < id2);
112         assert(id1 <= id3);
113         assert(id3 >= id2);
114         assert(id3 > id1);
115     }
116 
117 private:
118     ulong _id;
119 }
120 
121 /// An entity an aggregate of components (pure data), accessible with an id.
122 class Entity
123 {
124 public:
125     /// Construct an entity with a manager reference and an ID.
126     this(EntityManager manager, ID id) pure nothrow @safe
127     {
128         _manager = manager;
129         _id = id;
130     }
131 
132     /// Return the entity id.
133     inout(ID) id() inout pure nothrow @property @safe
134     {
135         return _id;
136     }
137 
138     unittest
139     {
140         auto entity = new Entity(null, ID(1, 2));
141         assert(entity.id.index == 1);
142         assert(entity.id.tag == 2);
143     }
144 
145     /// Return the component added to this entity.
146     inout(C) component(C)() inout pure nothrow @safe
147     {
148         return _manager.component!C(_id);
149     }
150 
151     /// Check if the entity has a specific component.
152     bool hasComponent(C)() pure nothrow @safe
153     {
154         return _manager.hasComponent!C(_id);
155     }
156 
157     /// Add a component to the entity.
158     void add(C)(C component) pure nothrow @safe
159     {
160         _manager.addComponent!C(_id, component);
161     }
162 
163     /// Remove the component if the entity has it.
164     void remove(C)() pure nothrow @safe
165     {
166         _manager.remove!C(_id);
167     }
168 
169     /// Destroy this entity and invalidate all handles to this entity.
170     void destroy() pure nothrow @safe
171     {
172         _manager.destroy(_id);
173         invalidate();
174     }
175 
176     /// Check if this handle is valid (points to the entity with the same tag).
177     bool valid() pure nothrow @safe
178     {
179         if (_manager is null)
180         {
181             return false;
182         }
183         else
184         {
185             return _manager.valid(_id);
186         }
187     }
188 
189     /// Invalidate this entity handle (but not other handles).
190     void invalidate() pure nothrow @safe
191     {
192         _manager = null;
193         _id = ID.INVALID;
194     }
195 
196     unittest
197     {
198         auto entity = new Entity(null, ID.INVALID);
199         assert(!entity.valid());
200     }
201 
202     /// Equals operator (check for equality).
203     override bool opEquals(Object o) const
204     {
205         auto other = cast(Entity) o;
206         return _id == other._id && _manager == other._manager;
207     }
208 
209     unittest
210     {
211         auto entity1 = new Entity(null, ID(1, 1));
212         auto entity2 = new Entity(null, ID(1, 1));
213         auto entity3 = new Entity(null, ID(1, 2));
214         auto entity4 = new Entity(null, ID(2, 1));
215 
216         assert(entity1 == entity1);
217         assert(entity1 == entity2);
218         assert(entity1 != entity3);
219         assert(entity1 != entity4);
220 
221         assert(entity2 == entity1);
222         assert(entity2 == entity2);
223         assert(entity2 != entity3);
224         assert(entity2 != entity4);
225 
226         assert(entity3 != entity1);
227         assert(entity3 != entity2);
228         assert(entity3 == entity3);
229         assert(entity3 != entity4);
230 
231         assert(entity4 != entity1);
232         assert(entity4 != entity2);
233         assert(entity4 != entity3);
234         assert(entity4 == entity4);
235     }
236 
237     /// Comparison operator.
238     override int opCmp(Object o) const @safe
239     {
240         auto other = cast(Entity) o;
241         return _id.opCmp(other._id);
242     }
243 
244     unittest
245     {
246         auto entity1 = new Entity(null, ID(0, 1));
247         auto entity2 = new Entity(null, ID(10, 230));
248         auto entity3 = new Entity(null, ID(11, 200));
249 
250         assert(entity1 < entity2);
251         assert(entity1 <= entity3);
252         assert(entity3 > entity2);
253         assert(entity2 >= entity1);
254     }
255 
256 private:
257     EntityManager _manager;
258     ID _id;
259 }
260 
261 mixin template EntityEvent()
262 {
263     this(Entity entity)
264     {
265         this.entity = entity;
266     }
267     Entity entity;
268 }
269 
270 mixin template ComponentEvent(C)
271 {
272     this(Entity entity, C component)
273     {
274         this.entity = entity;
275         this.component = component;
276     }
277     Entity entity;
278     C component;
279 }
280 
281 struct EntityCreatedEvent
282 {
283     mixin EntityEvent;
284 }
285 
286 struct EntityDestroyedEvent
287 {
288     mixin EntityEvent;
289 }
290 
291 struct ComponentAddedEvent(C)
292 {
293     mixin ComponentEvent!C;
294 }
295 
296 struct ComponentRemovedEvent(C)
297 {
298     mixin ComponentEvent!C;
299 }
300 
301 /// Manages entities and their associated components.
302 class EntityManager
303 {
304 public:
305     /// Construct an empty entity manager.
306     this(EventManager events)
307     {
308         _indexCounter = 0U;
309         _numEntities = 0U;
310         _events = events;
311     }
312 
313     /// A range over all entities in the manager.
314     struct Range
315     {
316         /// Construct a range over this manager.
317         private this(EntityManager manager, uint index = 0) pure nothrow @safe
318         {
319             _manager = manager;
320             _index = index;
321         }
322 
323         /// Return the number of entities to iterate over (including empty ones).
324         size_t length() const pure nothrow @property @safe
325         {
326             return _manager.capacity;
327         }
328 
329         /// Return if an only if the range cannot access any more entities.
330         bool empty() const pure nothrow @property @safe
331         {
332             return _index >= _manager.capacity;
333         }
334 
335         /// Return the current entity.
336         Entity front() pure nothrow @property @safe
337         {
338             return _manager.entity(_index);
339         }
340 
341         /// Access the next entity.
342         void popFront() pure nothrow @safe
343         {
344             _index++;
345         }
346 
347         /// Return a copy of this range.
348         Range save() pure nothrow @safe
349         {
350             return Range(_manager, _index);
351         }
352 
353         private EntityManager _manager;
354         private uint _index;
355     }
356 
357     /// Return a range over the entities.
358     Range opSlice() pure nothrow @safe
359     {
360         return Range(this);
361     }
362 
363     unittest
364     {
365         class Test
366         {
367             this(int x)
368             {
369                 y = x;
370             }
371             int y;
372         }
373 
374         auto manager = new EntityManager(new EventManager);
375         auto entity1 = manager.create();
376         auto entity2 = manager.create();
377 
378         entity1.add(new Test(3061));
379         entity2.add(new Test(2015));
380 
381         foreach(entity; manager[])
382         {
383             if (entity == entity1)
384             {
385                 assert(entity1.component!Test().y == 3061);
386             }
387             else if (entity == entity2)
388             {
389                 assert(entity2.component!Test().y == 2015);
390             }
391             else
392             {
393                 assert(0);
394             }
395         }
396     }
397 
398     /// Create a range with only the entities with the specified components.
399     auto entities(Components...)() pure nothrow @safe
400     {
401         auto mask = componentMask!Components();
402         bool hasComponents(Entity entity)
403         {
404             typeof(mask) combinedMask;
405             combinedMask[] = componentMask(entity.id)[] & mask[];
406             return combinedMask[] == mask[];
407         }
408 
409         return this[].filter!(hasComponents)();
410     }
411 
412     unittest
413     {
414         class Position
415         {
416             this(int x, int y)
417             {
418                 this.x = x;
419                 this.y = y;
420             }
421             int x, y;
422         }
423 
424         class Velocity
425         {
426             this(int x, int y)
427             {
428                 this.x = x;
429                 this.y = y;
430             }
431             int x, y;
432         }
433 
434         class Gravity
435         {
436             this(double acc)
437             {
438                 accel = acc;
439             }
440             double accel;
441         }
442 
443         auto manager = new EntityManager(new EventManager);
444 
445         auto entity1 = manager.create();
446         entity1.add(new Position(2, 1));
447         entity1.add(new Velocity(15, 4));
448 
449         auto entity2 = manager.create();
450         entity2.add(new Velocity(-1, -3));
451         entity2.add(new Gravity(10));
452 
453         auto entity3 = manager.create();
454         entity3.add(new Gravity(-9.8));
455         entity3.add(new Position(14, -9));
456 
457         auto positionEntities = manager.entities!Position();
458         auto velocityEntities = manager.entities!Velocity();
459         auto gravityEntities = manager.entities!Gravity();
460         auto physicsEntities = manager.entities!(Position, Velocity, Gravity)();
461 
462         foreach (pos; positionEntities)
463         {
464             assert(pos == entity1 || pos == entity3);
465             assert(pos != entity2);
466         }
467 
468         foreach(vel; velocityEntities)
469         {
470             assert(vel == entity1 || vel == entity2);
471             assert(vel != entity3);
472         }
473 
474         foreach(grav; gravityEntities)
475         {
476             assert(grav == entity2 || grav == entity3);
477             assert(grav != entity1);
478         }
479 
480         assert(physicsEntities.empty());
481     }
482 
483     /// Return the number of entities.
484     size_t count() const pure nothrow @property @safe
485     {
486         return _numEntities;
487     }
488 
489     /// Return the number of free entity indices.
490     size_t free() const pure nothrow @property @safe
491     {
492         return capacity - count;
493     }
494 
495     /// Return the maximum capacity of entities before needing reallocation.
496     size_t capacity() const pure nothrow @property @safe
497     {
498         return _indexCounter;
499     }
500 
501     /// Return if and only if there are no entities.
502     bool empty() const pure nothrow @property @safe
503     {
504         return _numEntities == 0;
505     }
506 
507     /// Return the entity with the specified index.
508     Entity entity(uint index) pure nothrow @safe
509     {
510         return entity(id(index));
511     }
512 
513     unittest
514     {
515         auto manager = new EntityManager(new EventManager);
516         auto entity = manager.create();
517         assert(entity == manager.entity(entity.id.index));
518     }
519 
520     /// Return the entity with the specified (and valid) id.
521     /// Returns null if the id is invalid.
522     Entity entity(ID id) pure nothrow @safe
523     out (result)
524     {
525         if (result !is null)
526         {
527             assert(valid(result.id));
528         }
529     }
530     body
531     {
532         if (valid(id))
533         {
534             return new Entity(this, id);
535         }
536         else
537         {
538             return null;
539         }
540     }
541 
542     unittest
543     {
544         auto manager = new EntityManager(new EventManager);
545         auto entity = manager.create();
546         assert(entity == manager.entity(entity.id));
547     }
548 
549     /// Return the id with the specified index.
550     ID id(uint index) pure nothrow @safe
551     {
552         if (index < _indexCounter)
553         {
554             return ID(index, _entityTags[index]);
555         }
556         else
557         {
558             return ID(index, 0);
559         }
560     }
561 
562     unittest
563     {
564         auto manager = new EntityManager(new EventManager);
565         auto entity = manager.create();
566         assert(entity.id == manager.id(entity.id.index));
567     }
568 
569     /// Check if this entity handle is valid - is not invalidated or outdated
570     bool valid(ID id) const pure nothrow @safe
571     {
572         return (id.index < _indexCounter && _entityTags[id.index] == id.tag);
573     }
574 
575     unittest
576     {
577         auto manager = new EntityManager(new EventManager);
578         assert(!manager.valid(ID.INVALID));
579         assert(manager.valid(manager.create().id));
580     }
581 
582     /// Create an entity in a free slot.
583     Entity create() pure nothrow @safe
584     out (result)
585     {
586         assert(valid(result.id));
587     }
588     body
589     {
590         uint index;
591 
592         // Expand containers to accomodate new index
593         if (_freeIndices.empty())
594         {
595             index = _indexCounter;
596             accomodateEntity(_indexCounter);
597 
598             // Uninitialized value is 0, so any entities with tag 0 are invalid
599             _entityTags[index] = 1;
600         }
601         // Fill unused index, no resizing necessary
602         else
603         {
604             // Remove index from free indices list
605             index = _freeIndices.front();
606             _freeIndices.removeFront();
607         }
608 
609         _numEntities++;
610         _events.emit(EntityCreatedEvent());
611         return new Entity(this, ID(index, _entityTags[index]));
612     }
613 
614     unittest
615     {
616         auto manager = new EntityManager(new EventManager);
617         auto entity1 = manager.create();
618         auto entity2 = manager.create();
619 
620         assert(entity1.valid());
621         assert(entity2.valid());
622         assert(entity1 != entity2);
623 
624         entity1.invalidate();
625         assert(!entity1.valid());
626     }
627 
628     /// Destroy the specified entity and invalidate all handles to it.
629     void destroy(ID id) pure nothrow @safe
630     out
631     {
632         assert(!valid(id));
633     }
634     body
635     {
636         if (valid(id))
637         {
638             auto index = id.index;
639             // Invalidate all handles by incrementing tag
640             _entityTags[index]++;
641 
642             // Add index to free list
643             _freeIndices.insert(index);
644 
645             // Remove all components
646             foreach(component; _components)
647             {
648                 component[index] = null;
649             }
650 
651             // Clear the component bitmask
652             _componentMasks[index].clear();
653 
654             _numEntities--;
655             _events.emit(EntityDestroyedEvent());
656         }
657     }
658 
659     unittest
660     {
661         auto manager = new EntityManager(new EventManager);
662         auto entity1 = manager.create();
663         auto entity2 = manager.create();
664         auto entity3 = manager.create();
665 
666         assert(entity1.id == ID(0, 1));
667         assert(entity2.id == ID(1, 1));
668         assert(entity3.id == ID(2, 1));
669 
670         // Two methods of destroying entities
671         manager.destroy(entity1.id);
672         entity2.destroy();
673 
674         assert(!entity1.valid());
675         assert(!entity2.valid());
676         assert(entity3.valid());
677 
678         auto entity4 = manager.create();
679         auto entity5 = manager.create();
680 
681         assert(entity3.valid());
682         assert(entity4.valid());
683         assert(entity5.valid());
684         assert(entity4.id == ID(1, 2));
685         assert(entity5.id == ID(0, 2));
686     }
687 
688     /// Add a component to the specified entity.
689     void addComponent(C)(ID id, C component) pure nothrow @safe
690     in
691     {
692         assert(valid(id));
693     }
694     out
695     {
696         assert(hasComponent!C(id));
697     }
698     body
699     {
700         accomodateComponent!C();
701         setComponent!C(id, component);
702         setMask!C(id, true);
703         _events.emit(ComponentAddedEvent!C());
704     }
705 
706     /// Remove a component from the entity (no effects if it is not present).
707     void removeComponent(C)(ID id) pure nothrow @safe
708     out
709     {
710         assert(!hasComponent!C(id));
711     }
712     body
713     {
714         if (hasComponent!C(id))
715         {
716             setComponent(id, null);
717             setMask!C(id, false);
718             _events.emit(ComponentRemovedEvent!C());
719         }
720     }
721 
722     /// Check if the entity has the specified component.
723     bool hasComponent(C)(const ID id) const pure nothrow @safe
724     in
725     {
726         assert(valid(id));
727     }
728     body
729     {
730         return (hasType!C() && component!C(id) !is null);
731     }
732 
733     /// Return the component associated with this entity.
734     inout(C) component(C)(ID id) inout pure nothrow @safe
735     in
736     {
737         assert(valid(id));
738     }
739     body
740     {
741         return cast(inout(C)) _components[type!C()][id.index];
742     }
743 
744     unittest
745     {
746         class Position
747         {
748             this(int x, int y)
749             {
750                 this.x = x;
751                 this.y = y;
752             }
753             int x, y;
754         }
755 
756         class Jump
757         {
758             bool onGround = true;
759         }
760 
761         auto manager = new EntityManager(new EventManager);
762         auto entity = manager.create();
763         auto position = new Position(1001, -19);
764         auto jump = new Jump();
765 
766         entity.add(position);
767         manager.addComponent(entity.id, jump);
768         assert(entity.hasComponent!Position());
769         assert(entity.hasComponent!Jump());
770         assert(position == entity.component!Position());
771         assert(jump == manager.component!Jump(entity.id));
772     }
773 
774     /// Delete all entities and components.
775     void clear() pure nothrow @safe
776     {
777         _indexCounter = 0U;
778         _numEntities = 0U;
779         _freeIndices.clear();
780         _entityTags = null;
781         _components = null;
782         _componentTypes.clear();
783         _componentMasks = null;
784     }
785 
786     unittest
787     {
788         class Position
789         {
790             this(int x, int y)
791             {
792                 this.x = x;
793                 this.y = y;
794             }
795             int x, y;
796         }
797 
798         class Velocity
799         {
800             this(int x, int y)
801             {
802                 this.x = x;
803                 this.y = y;
804             }
805             int x, y;
806         }
807 
808         class Gravity
809         {
810             this(double acc)
811             {
812                 accel = acc;
813             }
814             double accel;
815         }
816 
817         auto manager = new EntityManager(new EventManager);
818 
819         auto entity1 = manager.create();
820         entity1.add(new Position(2, 1));
821 
822         auto entity2 = manager.create();
823         entity2.add(new Velocity(-1, -3));
824 
825         auto entity3 = manager.create();
826         entity3.add(new Gravity(-9.8));
827 
828         auto position = entity1.component!Position();
829         auto velocity = entity2.component!Velocity();
830         auto gravity = entity3.component!Gravity();
831 
832         assert(position.x == 2 && position.y == 1);
833         assert(velocity.x == -1 && velocity.y == -3);
834         assert(std.math.abs(-9.8 - gravity.accel) < 1e-9);
835 
836         manager.clear();
837         assert(!entity1.valid());
838         assert(!entity2.valid());
839         assert(!entity3.valid());
840         assert(manager._indexCounter == 0);
841         assert(manager._freeIndices.empty);
842         assert(manager._entityTags.length == 0);
843         assert(manager._components.length == 0);
844         assert(manager._componentMasks.length == 0);
845 
846         auto entity4 = manager.create();
847         assert(entity4.valid());
848         assert(entity4.id.index == 0);
849     }
850 
851 private:
852     // Convenience function to set components.
853     void setComponent(C)(ID id, C component) pure nothrow @safe
854     {
855         _components[type!C()][id.index] = component;
856     }
857 
858     // Convenience function to set mask bits.
859     void setMask(C)(ID id, bool value) pure nothrow @safe
860     {
861         _componentMasks[id.index][type!C()] = value;
862     }
863 
864     // Reallocate space for a new component.
865     void accomodateComponent(C)() pure nothrow @safe
866     {
867         if (!hasType!C())
868         {
869             addType!C();
870             auto type = type!C();
871 
872             // Expand component array (new component - first dimension widens).
873             if (_components.length < type + 1)
874             {
875                 _components ~= new Object[_indexCounter];
876             }
877 
878             // Expand all component masks to include new component.
879             if (_componentMasks.length > 0 && _componentMasks[0].length < type + 1)
880             {
881                 foreach (ref componentMask; _componentMasks)
882                 {
883                     componentMask.length = type + 1;
884                 }
885             }
886         }
887     }
888 
889     // Reallocate space for a new entity.
890     void accomodateEntity(uint index) pure nothrow @safe
891     {
892         if (index >= _indexCounter)
893         {
894             // Expand entity tags.
895             if (_entityTags.length < index + 1)
896             {
897                 _entityTags.length = index + 1;
898             }
899 
900             // Expand component mask array.
901             if (_componentMasks.length < index + 1)
902             {
903                 _componentMasks ~= new bool[_components.length];
904             }
905 
906             // Expand all component arrays (new entity - second dimension widens).
907             if (_components.length > 0 && _components[0].length < index + 1)
908             {
909                 foreach (ref component; _components)
910                 {
911                     component.length = index + 1;
912                 }
913             }
914         }
915         _indexCounter = index + 1;
916     }
917 
918     // Return a unique integer for every component type.
919     ulong type(C)() inout pure nothrow @safe
920     in
921     {
922         assert(hasType!C());
923     }
924     body
925     {
926         return _componentTypes[C.classinfo];
927     }
928 
929     // Create a unique id for a new component type.
930     void addType(C)() pure nothrow @trusted
931     {
932         if (!hasType!C())
933         {
934             _componentTypes[C.classinfo] = _componentTypes.length;
935             _componentTypes.rehash();
936         }
937     }
938 
939     // Return if this component type has already been assigned a unique id.
940     bool hasType(C)() const pure nothrow @safe
941     {
942         return (C.classinfo in _componentTypes) !is null;
943     }
944 
945     // Return the component mask (bool array) of this entity.
946     bool[] componentMask(ID id) pure nothrow @safe
947     in
948     {
949         assert(valid(id));
950     }
951     body
952     {
953         return _componentMasks[id.index];
954     }
955 
956     // Return the component mask with the specified components marked as true.
957     bool[] componentMask(Components...)() pure nothrow @safe
958     in
959     {
960         foreach(C; Components)
961         {
962             assert(type!C() < _components.length);
963         }
964     }
965     body
966     {
967         bool[] mask = new bool[_components.length];
968         foreach (C; Components)
969         {
970             mask[type!C()] = true;
971         }
972         return mask;
973     }
974 
975     unittest
976     {
977         class Position { }
978         class Velocity { }
979         class Gravity { }
980 
981         auto manager = new EntityManager(new EventManager);
982         auto entity = manager.create();
983         entity.add(new Position());
984         entity.add(new Velocity());
985         entity.add(new Gravity());
986 
987         assert(manager.componentMask!(Position)() == [true, false, false]);
988         assert(manager.componentMask!(Position, Velocity, Gravity)() == [true, true, true]);
989     }
990 
991     // Debugging checks to ensure valid space for new entities and components.
992     invariant()
993     {
994         assert(_numEntities <= _indexCounter);
995         assert(_entityTags.length == _indexCounter);
996         assert(_componentMasks.length == _indexCounter);
997         assert(_components.length == _componentTypes.length);
998 
999         foreach(componentMask; _componentMasks)
1000         {
1001             assert(componentMask.length == _components.length);
1002         }
1003 
1004         foreach(ref componentArray; _components)
1005         {
1006             assert(componentArray.length == _indexCounter);
1007         }
1008     }
1009 
1010     // Tracks the actual number of entities.
1011     uint _numEntities;
1012 
1013     // Tracks the next unused entity index (i.e. capacity)..
1014     uint _indexCounter;
1015 
1016     // Tracks entity indices recently freed.
1017     SList!uint _freeIndices;
1018 
1019     // Tracks entity versions (incremented when entity is destroyed) for validity checking.
1020     uint[] _entityTags;
1021 
1022     // A nested array of entity components, ordered by component and then entity index.
1023     Object[][] _components;
1024 
1025     // A map associating each component class with a unique unsigned integer.
1026     ulong[ClassInfo] _componentTypes;
1027 
1028     // Bitmasks of each entity's components, ordered by entity and then by component bit.
1029     bool[][] _componentMasks;
1030 
1031     // Event manager.
1032     EventManager _events;
1033 }