1 /**
2 Entity management module.
3 
4 Copyright: © 2015-2016 Claude Merle
5 Authors: Claude Merle
6 License: This file is part of EntitySysD.
7 
8 EntitySysD is free software: you can redistribute it and/or modify it
9 under the terms of the Lesser GNU General Public License as published
10 by the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12 
13 EntitySysD is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 Lesser GNU General Public License for more details.
17 
18 You should have received a copy of the Lesser GNU General Public License
19 along with EntitySysD. If not, see $(LINK http://www.gnu.org/licenses/).
20 */
21 
22 module entitysysd.entity;
23 
24 import std.bitmanip;
25 import std.container;
26 import std.string;
27 
28 import entitysysd.component;
29 import entitysysd.event;
30 import entitysysd.exception;
31 import entitysysd.pool;
32 
33 /// Attribute to use uppon component struct's and union's.
34 public import entitysysd.component : component;
35 
36 /**
37  * Entity structure.
38  *
39  * This is the combination of two 32-bits id: a unique-id and a version-id.
40  */
41 struct Entity
42 {
43 public:
44     static struct Id
45     {
46     public:
47         this(uint uId, uint vId)
48         {
49             mId = cast(ulong)uId | cast(ulong)vId << 32;
50         }
51 
52         ulong id() const @property
53         {
54             return mId;
55         }
56         uint uniqueId() @property
57         {
58             return mId & 0xffffffffUL;
59         }
60         uint versionId() @property
61         {
62             return mId >> 32;
63         }
64 
65         bool opEquals()(auto const ref Id lId) const
66         {
67             return id == lId.id;
68         }
69 
70         string toString()
71         {
72             return format("#%d:%d", uniqueId, versionId);
73         }
74 
75     private:
76         ulong   mId;
77     }
78 
79     enum Id invalid = Id(0, 0);
80 
81     this(EntityManager manager, Id id)
82     {
83         mManager = manager;
84         mId = id;
85     }
86 
87     /**
88      * Destroy the entity (unregister all attached components).
89      *
90      * Throws: EntityException if the entity is invalid.
91      */
92     void destroy()
93     {
94         enforce!EntityException(valid);
95         mManager.destroy(mId);
96         invalidate();
97     }
98 
99     /**
100      * Tells whether the entity is valid.
101      *
102      * Returns: true if the entity is valid, false otherwise.
103      */
104     bool valid() @property
105     {
106         return mManager !is null && mManager.valid(mId);
107     }
108 
109     /**
110      * Invalidate the entity instance (but does not destroy it).
111      */
112     void invalidate()
113     {
114         mId = invalid;
115         mManager = null;
116     }
117 
118     /**
119      * Returns the id of the entity.
120      */
121     Id id() const @property
122     {
123         return mId;
124     }
125 
126     /**
127      * Register a component C to an entity.
128      *
129      * Returns: A pointer on the component for this entity.
130      *
131      * Throws: EntityException if the entity is invalid.
132      *         ComponentException if there is no room for that component or if
133      *                            if the component is already registered.
134      */
135     C* register(C, Args...)(Args args)
136         if (isComponent!C)
137     {
138         enforce!EntityException(valid);
139         auto component = mManager.register!C(mId);
140         static if (Args.length != 0)
141             *component = C(args);
142         return component;
143     }
144 
145     /**
146      * Unregister a component C from an entity.
147      *
148      * Throws: EntityException if the entity is invalid.
149      *         ComponentException if the component is not registered.
150      */
151     void unregister(C)()
152         if (isComponent!C)
153     {
154         enforce!EntityException(valid);
155         mManager.unregister!C(mId);
156     }
157 
158     /**
159      * Get a component pointer of the entity.
160      *
161      * Returns: A pointer on the component for this entity.
162      *
163      * Throws: EntityException if the entity is invalid.
164      *         ComponentException if the component is not registered.
165      */
166     C* component(C)() @property
167         if (isComponent!C)
168     {
169         enforce!EntityException(valid);
170         return mManager.getComponent!(C)(mId);
171     }
172 
173     /**
174      * Set the value of a component of the entity.
175      *
176      * Throws: EntityException if the entity is invalid.
177      *         ComponentException if the component is not registered.
178      */
179     void component(C)(auto ref C c) @property
180         if (isComponent!C)
181     {
182         enforce!EntityException(valid);
183         *mManager.getComponent!(C)(mId) = c;
184     }
185 
186     /**
187      * Set the value of a component of the entity.
188      *
189      * Returns: true if the component is registered to the entity,
190      *          false otherwise.
191      *
192      * Throws: EntityException if the entity is invalid.
193      */
194     bool isRegistered(C)()
195         if (isComponent!C)
196     {
197         enforce!EntityException(valid);
198         return mManager.isRegistered!C(mId);
199     }
200 
201     /**
202      * Compare two entities and tells whether they are the same (same id).
203      */
204     bool opEquals()(auto const ref Entity lEntity) const
205     {
206         return id == lEntity.id;
207     }
208 
209     /**
210      * Returns a string representation of an entity.
211      *
212      * It has the form: #uid:vid where uid is the unique-id and
213      * vid is the version-id
214      */
215     string toString()
216     {
217         return mId.toString();
218     }
219 
220 private:
221     EntityManager mManager;
222     Id            mId = invalid;
223 }
224 
225 ///
226 unittest
227 {
228     @component struct Position
229     {
230         float x, y;
231     }
232 
233     auto em = new EntityManager(new EventManager);
234     auto entity = em.create();
235     auto posCompPtr = entity.register!Position(2.0, 3.0);
236 
237     assert(posCompPtr == entity.component!Position);
238     assert(posCompPtr.x == 2.0);
239     assert(entity.component!Position.y == 3.0);
240 }
241 
242 /**
243  * Manages entities creation and component memory management.
244  */
245 class EntityManager
246 {
247 public:
248     /**
249      * Constructor of the entity-manager. eventManager may be used to notify
250      * about entity creation and component registration. maxComponent sets
251      * the maximum number of components supported by the whole manager. poolSize
252      * is the chunk size in bytes for each components.
253      */
254     this(EventManager eventManager,
255          size_t maxComponent = 64,
256          size_t poolSize     = 8192)
257     {
258         mEventManager = eventManager;
259         mMaxComponent = maxComponent;
260         mPoolSize     = poolSize;
261     }
262 
263     /**
264      * Current number of managed entities.
265      */
266     size_t size() @property
267     {
268         return mEntityComponentMask.length - mNbFreeIds;
269     }
270 
271     /**
272      * Current capacity entity.
273      */
274     size_t capacity() @property
275     {
276         return mEntityComponentMask.length;
277     }
278 
279     /**
280      * Return true if the given entity ID is still valid.
281      */
282     bool valid(Entity.Id id)
283     {
284         return id != Entity.invalid &&
285                id.uniqueId-1 < mEntityVersions.length &&
286                mEntityVersions[id.uniqueId-1] == id.versionId;
287     }
288 
289     /**
290      * Create an entity.
291      *
292      * Returns: a new valid entity.
293      */
294     Entity create()
295     {
296         uint uniqueId, versionId;
297 
298         if (mFreeIds.empty)
299         {
300             mIndexCounter++;
301             uniqueId = mIndexCounter;
302             accomodateEntity();
303             versionId = mEntityVersions[uniqueId-1];
304         }
305         else
306         {
307             uniqueId = mFreeIds.front;
308             mFreeIds.removeFront();
309             mNbFreeIds--;
310             versionId = mEntityVersions[uniqueId-1];
311         }
312 
313         Entity entity = Entity(this, Entity.Id(uniqueId, versionId));
314 
315         return entity;
316     }
317 
318     /**
319      * Returns an entity from an an entity-id
320      *
321      * Returns: the entity from the id.
322      *
323      * Throws: EntityException if the id is invalid.
324      */
325     Entity getEntity(Entity.Id id)
326     {
327         enforce!EntityException(valid(id));
328         return Entity(this, id);
329     }
330 
331     //*** Browsing features ***
332 
333     /**
334      * Allows to browse through all the valid entities of the manager with
335      * a foreach loop.
336      */
337     int opApply(int delegate(Entity entity) dg)
338     {
339         int result = 0;
340 
341         // copy version-ids
342         auto versionIds = mEntityVersions.dup;
343         // and remove those that are free
344         foreach (freeId; mFreeIds)
345             versionIds[freeId-1] = uint.max;
346 
347         foreach (uniqueId, versionId; versionIds)
348         {
349             if (versionId == uint.max)
350                 continue;
351             result = dg(Entity(this,
352                                Entity.Id(cast(uint)uniqueId+1, versionId)));
353             if (result)
354                 break;
355         }
356 
357         return result;
358     }
359 
360 
361     /**
362      * Return a range of all the valid instances of a component.
363      */
364     auto components(C)() @property
365         if (isComponent!C)
366     {
367         import std.range : iota;
368         import std.algorithm : map, filter;
369 
370         immutable compId = componentId!C();
371 
372         // if no such component has ever been registered, no pool will exist.
373         auto pool = cast(Pool!C)mComponentPools[compId];
374         assert(pool !is null, "A component pool should never be null");
375 
376         return iota(0, pool.nbElements)
377             .filter!(i => mEntityComponentMask[i][compId])
378             .map!(i => &pool[i]);
379     }
380 
381     /**
382      * Allows to browse through the entities that have a required set of
383      * components.
384      */
385     auto entitiesWith(CList...)() @property
386         if (areComponents!CList)
387     {
388         struct EntitiesWithView(CList...)
389             if (areComponents!CList)
390         {
391             this(EntityManager em)
392             {
393                 entityManager = em;
394             }
395 
396             int opApply(int delegate(Entity entity) dg)
397             {
398                 int result = 0;
399 
400                 entityLoop: foreach (i, ref componentMask;
401                                      entityManager.mEntityComponentMask)
402                 {
403                     foreach (C; CList)
404                     {
405                         auto compId = entityManager.componentId!C();
406                         if (!componentMask[compId])
407                             continue entityLoop;
408                     }
409 
410                     auto versionId = entityManager.mEntityVersions[i];
411                     result = dg(Entity(entityManager,
412                                        Entity.Id(cast(uint)i+1, versionId)));
413                     if (result)
414                         break;
415                 }
416 
417                 return result;
418             }
419 
420             int opApply(int delegate(Entity entity, Pointers!CList components) dg)
421             {
422                 auto withComponents(Entity ent)
423                 {
424                     auto get(T)()
425                     {
426                         return ent.component!T;
427                     }
428                     import std.meta : staticMap;
429                     return dg(ent, staticMap!(get, CList));
430                 }
431 
432                 return opApply(&withComponents);
433             }
434 
435             EntityManager entityManager;
436         }
437         return EntitiesWithView!(CList)(this);
438     }
439 
440 private:
441     void destroy(Entity.Id id)
442     {
443         uint uniqueId = id.uniqueId;
444 
445         // reset all components for that entity
446         foreach (ref bit; mEntityComponentMask[uniqueId-1])
447             bit = 0;
448         // invalidate its version, incrementing it
449         mEntityVersions[uniqueId-1]++;
450         mFreeIds.insertFront(uniqueId);
451         mNbFreeIds++;
452     }
453 
454     C* register(C)(Entity.Id id)
455         if (isComponent!C)
456     {
457         const auto compId = componentId!(C)();
458         enforce!ComponentException(compId < mMaxComponent);
459         const auto uniqueId = id.uniqueId;
460         enforce!ComponentException(!mEntityComponentMask[uniqueId-1][compId]);
461 
462         // place new component into the pools
463         auto pool = cast(Pool!C)mComponentPools[compId];
464         assert(pool !is null, "A component pool should never be null");
465 
466         // Set the bit for this component.
467         mEntityComponentMask[uniqueId-1][compId] = true;
468 
469         pool.initN(uniqueId-1);
470 
471         return &pool[uniqueId-1];
472     }
473 
474     void unregister(C)(Entity.Id id)
475         if (isComponent!C)
476     {
477         const auto compId = componentId!(C)();
478         enforce!ComponentException(compId < mMaxComponent);
479         const auto uniqueId = id.uniqueId;
480         enforce!ComponentException(mEntityComponentMask[uniqueId-1][compId]);
481 
482         // Remove component bit.
483         mEntityComponentMask[uniqueId-1][compId] = false;
484     }
485 
486     bool isRegistered(C)(Entity.Id id)
487         if (isComponent!C)
488     {
489         const auto compId = componentId!(C)();
490         const auto uniqueId = id.uniqueId;
491 
492         if (compId >= mMaxComponent)
493             return false;
494 
495         return mEntityComponentMask[uniqueId-1][compId];
496     }
497 
498     C* getComponent(C)(Entity.Id id)
499         if (isComponent!C)
500     {
501         const auto compId = componentId!(C)();
502         enforce!ComponentException(compId < mMaxComponent);
503         const auto uniqueId = id.uniqueId;
504         enforce!ComponentException(mEntityComponentMask[uniqueId-1][compId]);
505 
506         // Placement new into the component pool.
507         Pool!C pool = cast(Pool!C)mComponentPools[compId];
508         return &pool[uniqueId-1];
509     }
510 
511     size_t componentId(C)()
512     {
513         immutable compId = ComponentCounter!(C).getId();
514 
515         // ensure we have a pool to hold components of this type
516         if (mComponentPools.length <= compId)
517         {
518             mComponentPools.length = compId + 1;
519             mComponentPools[compId] = new Pool!C(mIndexCounter);
520         }
521 
522         return compId;
523     }
524 
525     void accomodateEntity()
526     {
527         if (mEntityComponentMask.length < mIndexCounter)
528         {
529             mEntityComponentMask.length = mIndexCounter;
530             foreach (ref mask; mEntityComponentMask)
531                 mask.length = mMaxComponent;
532             mEntityVersions.length = mIndexCounter;
533             foreach (ref pool; mComponentPools)
534                 pool.accomodate(mIndexCounter);
535         }
536     }
537 
538     // Current number of Entities
539     uint            mIndexCounter = 0;
540     size_t          mMaxComponent;
541     size_t          mPoolSize;
542     // Event Manager
543     EventManager    mEventManager;
544     // Array of pools for each component types
545     BasePool[]      mComponentPools;
546     // Bitmask of components for each entities.
547     // Index into the vector is the Entity.Id.
548     BitArray[]      mEntityComponentMask;
549     // Vector of entity version id's
550     // Incremented each time an entity is destroyed
551     uint[]          mEntityVersions;
552     // List of available entity id's.
553     SList!uint      mFreeIds;
554     uint            mNbFreeIds;
555 }
556 
557 // Translate a list of types to a list of pointers to those types.
558 private template Pointers(T...)
559 {
560     import std.meta : staticMap;
561     private alias PtrTo(U) = U*;
562     alias Pointers = staticMap!(PtrTo, T);
563 }
564 
565 
566 //******************************************************************************
567 //***** UNIT-TESTS
568 //******************************************************************************
569 
570 import std.stdio;
571 
572 unittest
573 {
574     auto em = new EntityManager(new EventManager());
575 
576     auto ent0 = em.create();
577     assert(em.capacity == 1);
578     assert(em.size == 1);
579     assert(ent0.valid);
580     assert(ent0.id.uniqueId == 1);
581     assert(ent0.id.versionId == 0);
582 
583     ent0.destroy();
584     assert(em.capacity == 1);
585     assert(em.size == 0);
586     assert(!ent0.valid);
587     assert(ent0.id.uniqueId == 0);
588     assert(ent0.id.versionId == 0);
589 
590     ent0 = em.create();
591     auto ent1 = em.create();
592     auto ent2 = em.create();
593     assert(em.capacity == 3);
594     assert(em.size == 3);
595     assert(ent0.id.uniqueId == 1);
596     assert(ent0.id.versionId == 1);
597     assert(ent1.id.uniqueId == 2);
598     assert(ent1.id.versionId == 0);
599     assert(ent2.id.uniqueId == 3);
600     assert(ent2.id.versionId == 0);
601 
602     @component struct NameComponent
603     {
604         string name;
605     }
606 
607     @component struct PosComponent
608     {
609         int x, y;
610     }
611     ent0.register!NameComponent();
612     ent1.register!NameComponent();
613     ent2.register!NameComponent();
614 
615     ent0.register!PosComponent();
616     ent2.register!PosComponent();
617 
618     ent0.component!NameComponent.name = "Hello";
619     ent1.component!NameComponent.name = "World";
620     ent2.component!NameComponent.name = "Again";
621     assert(ent0.component!NameComponent.name == "Hello");
622     assert(ent1.component!NameComponent.name == "World");
623     assert(ent2.component!NameComponent.name == "Again");
624 
625     ent0.component!PosComponent = PosComponent(5, 6);
626     ent2.component!PosComponent = PosComponent(2, 3);
627     assert(ent0.component!PosComponent.x == 5);
628     assert(ent0.component!PosComponent.y == 6);
629     assert(ent2.component!PosComponent.x == 2);
630     assert(ent2.component!PosComponent.y == 3);
631 
632     //ent1.destroy();
633 
634     // List all current valid entities
635     foreach (ent; em)
636     {
637         assert(ent.valid);
638         //writeln(ent.component!NameComponent.name);
639     }
640 
641     // List all name components
642     foreach (comp; em.components!NameComponent)
643     {
644         //writeln(comp.name);
645     }
646 
647     // List all name components
648     foreach (ent; em.entitiesWith!(NameComponent, PosComponent))
649     {
650         assert(ent.valid);
651         //writeln(ent.component!NameComponent.name);
652     }
653 
654     // Check const fields are properly handled
655     @component struct ConstComp
656     {
657         int         a, b;
658         const float cFloat = 5.0;
659     }
660 
661     ent0.register!ConstComp();
662     assert(ent0.component!ConstComp.cFloat == 5.0);
663 
664     // Check immutable fields are not accepted
665     @component struct ImmutableComp
666     {
667         int             a, b;
668         immutable float iFloat = 5.0;
669     }
670 
671     // Check it will NOT compile if a field is immutable
672     assert(!__traits(compiles, ent0.register!ImmutableComp()));
673 }
674 
675 unittest
676 {
677     @component struct Velocity { int x, y; }
678     @component struct Position { int x, y; }
679 
680     auto em = new EntityManager(new EventManager());
681 
682     auto ent0 = em.create();
683     auto ent1 = em.create();
684 
685     ent0.register!Position(0, 0);
686     ent1.register!Position(4, 5);
687 
688     ent0.register!Velocity(1, 2);
689     ent1.register!Velocity(3, 4);
690 
691     // test getting components from the opApply loop
692     foreach(ent, pos, vel; em.entitiesWith!(Position, Velocity))
693     {
694         pos.x += vel.x;
695         pos.y += vel.y;
696     }
697 
698     assert(ent0.component!Position.x == 1 && ent0.component!Position.y == 2);
699     assert(ent1.component!Position.x == 7 && ent1.component!Position.y == 9);
700 }
701 
702 // Ensure that em.components!T does not throw if no `T` has ever been registered
703 unittest
704 {
705     @component struct Dummy { }
706 
707     auto em = new EntityManager(new EventManager());
708 
709     foreach(pos ; em.components!Dummy)
710         assert(0, "Loop should never be entered");
711 }
712 
713 // Validate fix for a bug where you could end up with uninitialized pools.
714 // ent.isRegistered would create room for a pool without allocating it,
715 // potentially creating null pools in the middle of the collection.
716 // register was only checking the collection length, but did not ensure that the
717 // pool it retrieved to store the component was non-null.
718 unittest
719 {
720     @component struct Dummy1 { }
721     @component struct Dummy2 { }
722 
723     auto em = new EntityManager(new EventManager());
724     auto ent = em.create();
725 
726     assert(!ent.isRegistered!Dummy1);
727     assert(!ent.isRegistered!Dummy2);
728     assert(ent.register!Dummy2);
729     assert(ent.register!Dummy1);
730 }
731 
732 // Test range interface for components!T
733 unittest
734 {
735     @component struct A
736     {
737         int a;
738     }
739 
740     @component struct B
741     {
742         string b;
743     }
744 
745     auto em = new EntityManager(new EventManager());
746 
747     auto e1 = em.create();
748     auto e2 = em.create();
749     auto e3 = em.create();
750 
751     e1.register!A(1);
752     e2.register!B("2");
753     e3.register!A(3);
754     e3.register!B("3");
755 
756     import std.algorithm : map, equal;
757     assert(em.components!A.map!(x => x.a).equal([1, 3]));
758     assert(em.components!B.map!(x => x.b).equal(["2", "3"]));
759 }