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