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 * Params: 130 * C = Component to register. 131 * args = List of arguments to initialize the component, will be passed to 132 * its constructor. Optional. 133 * 134 * Returns: A pointer on the component for this entity. 135 * 136 * Throws: $(D EntityException) if the entity is invalid. 137 * $(D ComponentException) if there is no room for that component or 138 * if the component is already registered. 139 */ 140 C* register(C, Args...)(Args args) 141 if (isComponent!C) 142 { 143 enforce!EntityException(valid); 144 auto component = mManager.register!C(mId); 145 static if (Args.length != 0) 146 *component = C(args); 147 return component; 148 } 149 150 /** 151 * Unregister a component C from an entity. 152 * 153 * Params: 154 * C = Component to unregister. 155 * 156 * Throws: $(D EntityException) if the entity is invalid. 157 * $(D ComponentException) if the component is not registered. 158 */ 159 void unregister(C)() 160 if (isComponent!C) 161 { 162 enforce!EntityException(valid); 163 mManager.unregister!C(mId); 164 } 165 166 /** 167 * Get a component pointer of the entity. 168 * 169 * Params: 170 * C = Component for the entity. 171 * 172 * Returns: A pointer on the component for this entity. 173 * 174 * Throws: $(D EntityException) if the entity is invalid. 175 * $(D ComponentException) if the component is not registered. 176 */ 177 C* component(C)() @property 178 if (isComponent!C) 179 { 180 enforce!EntityException(valid); 181 return mManager.getComponent!(C)(mId); 182 } 183 184 /** 185 * Set the value of a component of the entity. 186 * 187 * Params: 188 * C = Component to set. 189 * 190 * Throws: $(D EntityException) if the entity is invalid. 191 * $(D ComponentException) if the component is not registered. 192 */ 193 void component(C)(auto ref C c) @property 194 if (isComponent!C) 195 { 196 enforce!EntityException(valid); 197 *mManager.getComponent!(C)(mId) = c; 198 } 199 200 /** 201 * Tell whether a component is registered to the entity. 202 * 203 * Params: 204 * C = Component to test. 205 * 206 * Returns: $(D true) if the component is registered to the entity, 207 * $(D false) otherwise. 208 * 209 * Throws: EntityException if the entity is invalid. 210 */ 211 bool isRegistered(C)() 212 if (isComponent!C) 213 { 214 enforce!EntityException(valid); 215 return mManager.isRegistered!C(mId); 216 } 217 218 /** 219 * Iterate over the components registered to the entity. It calls the 220 * accessor delegate that has been set to each component. 221 * 222 * Throws: $(D EntityException) if the entity is invalid. 223 */ 224 void iterate() 225 { 226 enforce!EntityException(valid); 227 mManager.iterate(this); 228 } 229 230 /** 231 * Compare two entities and tells whether they are the same (same id). 232 */ 233 bool opEquals()(auto const ref Entity e) const 234 { 235 return id == e.id; 236 } 237 238 /** 239 * Returns a string representation of an entity. 240 * 241 * It has the form: #uid:vid where uid is the unique-id and 242 * vid is the version-id 243 */ 244 string toString() 245 { 246 return mId.toString(); 247 } 248 249 private: 250 EntityManager mManager; 251 Id mId = invalid; 252 } 253 254 /// 255 unittest 256 { 257 @component struct Position 258 { 259 float x, y; 260 } 261 262 auto em = new EntityManager(new EventManager); 263 auto entity = em.create(); 264 auto posCompPtr = entity.register!Position(2.0, 3.0); 265 266 assert(posCompPtr == entity.component!Position); 267 assert(posCompPtr.x == 2.0); 268 assert(entity.component!Position.y == 3.0); 269 } 270 271 /** 272 * Manages entities creation and component memory management. 273 */ 274 class EntityManager 275 { 276 public: 277 /** 278 * Constructor of the entity-manager. 279 * Params: 280 * eventManager = May be used to notify about entity creation and 281 * component registration. 282 * maxComponent = Maximum number of components supported by the whole 283 * manager. 284 * poolSize = Chunk size in bytes for each components. 285 */ 286 this(EventManager eventManager, 287 size_t maxComponent = 64, 288 size_t poolSize = 8192) 289 { 290 mEventManager = eventManager; 291 mMaxComponent = maxComponent; 292 mPoolSize = poolSize; 293 } 294 295 /** 296 * Current number of managed entities. 297 */ 298 size_t size() @property 299 { 300 return mEntityComponentMask.length - mNbFreeIds; 301 } 302 303 /** 304 * Current capacity entity. 305 */ 306 size_t capacity() @property 307 { 308 return mEntityComponentMask.length; 309 } 310 311 /** 312 * Return true if the given entity ID is still valid. 313 */ 314 bool valid(Entity.Id id) 315 { 316 return id != Entity.invalid && 317 id.uniqueId-1 < mEntityVersions.length && 318 mEntityVersions[id.uniqueId-1] == id.versionId; 319 } 320 321 /** 322 * Create an entity. 323 * 324 * Returns: a new valid entity. 325 */ 326 Entity create() 327 { 328 uint uniqueId, versionId; 329 330 if (mFreeIds.empty) 331 { 332 mIndexCounter++; 333 uniqueId = mIndexCounter; 334 accomodateEntity(); 335 versionId = mEntityVersions[uniqueId-1]; 336 } 337 else 338 { 339 uniqueId = mFreeIds.front; 340 mFreeIds.removeFront(); 341 mNbFreeIds--; 342 versionId = mEntityVersions[uniqueId-1]; 343 } 344 345 Entity entity = Entity(this, Entity.Id(uniqueId, versionId)); 346 347 return entity; 348 } 349 350 /** 351 * Returns an entity from an an entity-id 352 * 353 * Returns: the entity from the id. 354 * 355 * Throws: EntityException if the id is invalid. 356 */ 357 Entity getEntity(Entity.Id id) 358 { 359 enforce!EntityException(valid(id)); 360 return Entity(this, id); 361 } 362 363 //*** Browsing features *** 364 365 /** 366 * Allows to browse through all the valid entities of the manager with 367 * a foreach loop. 368 * 369 * Examples: 370 * -------------------- 371 * foreach (entity; entityManager) 372 * { ... } 373 * -------------------- 374 */ 375 int opApply(int delegate(Entity entity) dg) 376 { 377 int result = 0; 378 379 // copy version-ids 380 auto versionIds = mEntityVersions.dup; 381 // and remove those that are free 382 foreach (freeId; mFreeIds) 383 versionIds[freeId-1] = uint.max; 384 385 foreach (uniqueId, versionId; versionIds) 386 { 387 if (versionId == uint.max) 388 continue; 389 result = dg(Entity(this, 390 Entity.Id(cast(uint)uniqueId+1, versionId))); 391 if (result) 392 break; 393 } 394 395 return result; 396 } 397 398 399 /** 400 * Return a range of all the valid instances of a component. 401 */ 402 auto components(C)() @property 403 if (isComponent!C) 404 { 405 import std.range : iota; 406 import std.algorithm : map, filter; 407 408 immutable compId = componentId!C(); 409 410 // if no such component has ever been registered, no pool will exist. 411 auto pool = cast(Pool!C)mComponentPools[compId]; 412 assert(pool !is null, "A component pool should never be null"); 413 414 return iota(0, pool.nbElements) 415 .filter!(i => mEntityComponentMask[i][compId]) 416 .map!(i => &pool[i]); 417 } 418 419 /** 420 * Allows to browse through the entities that have a required set of 421 * components. 422 * 423 * Examples: 424 * -------------------- 425 * foreach (entity; entityManager.entitiesWith!(CompA, CompB)) 426 * { ... } 427 * -------------------- 428 */ 429 auto entitiesWith(CList...)() @property 430 if (areComponents!CList) 431 { 432 struct EntitiesWithView(CList...) 433 if (areComponents!CList) 434 { 435 this(EntityManager em) 436 { 437 entityManager = em; 438 } 439 440 int opApply(int delegate(Entity entity) dg) 441 { 442 int result = 0; 443 444 entityLoop: foreach (i, ref componentMask; 445 entityManager.mEntityComponentMask) 446 { 447 foreach (C; CList) 448 { 449 auto compId = entityManager.componentId!C(); 450 if (!componentMask[compId]) 451 continue entityLoop; 452 } 453 454 auto versionId = entityManager.mEntityVersions[i]; 455 result = dg(Entity(entityManager, 456 Entity.Id(cast(uint)i+1, versionId))); 457 if (result) 458 break; 459 } 460 461 return result; 462 } 463 464 int opApply(int delegate(Entity entity, Pointers!CList components) dg) 465 { 466 auto withComponents(Entity ent) 467 { 468 auto get(T)() 469 { 470 return ent.component!T; 471 } 472 import std.meta : staticMap; 473 return dg(ent, staticMap!(get, CList)); 474 } 475 476 return opApply(&withComponents); 477 } 478 479 EntityManager entityManager; 480 } 481 return EntitiesWithView!(CList)(this); 482 } 483 484 alias CompAccessor = void delegate(Entity e, void* pc); 485 486 /** 487 * Set an accessor delegate for a component. 488 * 489 * Params: 490 * C = Component to which the accessor delegate will be set. 491 * dg = Delegate that will be called when using $(D Entity.iterate). 492 * Use $(D null) to clear the accessor. 493 */ 494 void accessor(C)(void delegate(Entity e, C* pc) dg) @property 495 { 496 immutable compId = ComponentCounter!(C).getId(); 497 // Make sure the delegate array is large enough 498 if (mComponentAccessors.length <= compId) 499 { 500 if (dg is null) 501 return; 502 else 503 mComponentAccessors.length = compId + 1; 504 } 505 mComponentAccessors[compId] = cast(CompAccessor)dg; 506 } 507 508 /** 509 * Get the accessor delegate assigned to a component. 510 * 511 * Params: 512 * C = Component from which the accessor delegate will be retreived. 513 * 514 * Returns: 515 * The accessor delegate; null if it has never been set, if it was cleared 516 * or if the component is missing. 517 */ 518 void delegate(Entity e, C* pc) accessor(C)() @property 519 { 520 immutable compId = ComponentCounter!(C).getId(); 521 if (mComponentAccessors.length <= compId) 522 return null; 523 return cast(void delegate(Entity e, C* pc))mComponentAccessors[compId]; 524 } 525 526 private: 527 void destroy(Entity.Id id) 528 { 529 uint uniqueId = id.uniqueId; 530 531 // reset all components for that entity 532 foreach (ref bit; mEntityComponentMask[uniqueId-1]) 533 bit = 0; 534 // invalidate its version, incrementing it 535 mEntityVersions[uniqueId-1]++; 536 mFreeIds.insertFront(uniqueId); 537 mNbFreeIds++; 538 } 539 540 C* register(C)(Entity.Id id) 541 if (isComponent!C) 542 { 543 const auto compId = componentId!(C)(); 544 enforce!ComponentException(compId < mMaxComponent); 545 const auto uniqueId = id.uniqueId; 546 enforce!ComponentException(!mEntityComponentMask[uniqueId-1][compId]); 547 548 // place new component into the pools 549 auto pool = cast(Pool!C)mComponentPools[compId]; 550 assert(pool !is null, "A component pool should never be null"); 551 552 // Set the bit for this component. 553 mEntityComponentMask[uniqueId-1][compId] = true; 554 555 pool.initN(uniqueId-1); 556 557 return &pool[uniqueId-1]; 558 } 559 560 void unregister(C)(Entity.Id id) 561 if (isComponent!C) 562 { 563 const auto compId = componentId!(C)(); 564 enforce!ComponentException(compId < mMaxComponent); 565 const auto uniqueId = id.uniqueId; 566 enforce!ComponentException(mEntityComponentMask[uniqueId-1][compId]); 567 568 // Remove component bit. 569 mEntityComponentMask[uniqueId-1][compId] = false; 570 } 571 572 bool isRegistered(C)(Entity.Id id) 573 if (isComponent!C) 574 { 575 const auto compId = componentId!(C)(); 576 const auto uniqueId = id.uniqueId; 577 578 if (compId >= mMaxComponent) 579 return false; 580 581 return mEntityComponentMask[uniqueId-1][compId]; 582 } 583 584 C* getComponent(C)(Entity.Id id) 585 if (isComponent!C) 586 { 587 const auto compId = componentId!(C)(); 588 enforce!ComponentException(compId < mMaxComponent); 589 const auto uniqueId = id.uniqueId; 590 enforce!ComponentException(mEntityComponentMask[uniqueId-1][compId]); 591 592 // Placement new into the component pool. 593 Pool!C pool = cast(Pool!C)mComponentPools[compId]; 594 return &pool[uniqueId-1]; 595 } 596 597 size_t componentId(C)() 598 { 599 immutable compId = ComponentCounter!(C).getId(); 600 601 // ensure we have a pool to hold components of this type 602 if (mComponentPools.length <= compId) 603 { 604 mComponentPools.length = compId + 1; 605 mComponentPools[compId] = new Pool!C(mIndexCounter); 606 } 607 608 return compId; 609 } 610 611 void accomodateEntity() 612 { 613 if (mEntityComponentMask.length < mIndexCounter) 614 { 615 mEntityComponentMask.length = mIndexCounter; 616 foreach (ref mask; mEntityComponentMask) 617 mask.length = mMaxComponent; 618 mEntityVersions.length = mIndexCounter; 619 foreach (ref pool; mComponentPools) 620 pool.accomodate(mIndexCounter); 621 } 622 } 623 624 void iterate(Entity entity) 625 { 626 const auto uniqueId = entity.id.uniqueId; 627 628 // Iterate over all components registered to that entity 629 foreach (compId; 0..mComponentAccessors.length) 630 { 631 // If the component is registered and has a delegate 632 if (mEntityComponentMask[uniqueId-1][compId]) 633 if (mComponentAccessors[compId] !is null) 634 { 635 auto compPtr = mComponentPools[compId].getPtr(uniqueId-1); 636 mComponentAccessors[compId](entity, compPtr); 637 } 638 } 639 } 640 641 // Current number of Entities 642 uint mIndexCounter = 0; 643 size_t mMaxComponent; 644 size_t mPoolSize; 645 // Event Manager 646 EventManager mEventManager; 647 // Array of pools for each component types 648 BasePool[] mComponentPools; 649 // Bitmask of components for each entities 650 // Index into the vector is the Entity.Id 651 BitArray[] mEntityComponentMask; 652 // Array of delegates for each component 653 CompAccessor[] mComponentAccessors; 654 // Vector of entity version id's 655 // Incremented each time an entity is destroyed 656 uint[] mEntityVersions; 657 // List of available entity id's. 658 SList!uint mFreeIds; 659 uint mNbFreeIds; 660 } 661 662 // Translate a list of types to a list of pointers to those types. 663 private template Pointers(T...) 664 { 665 import std.meta : staticMap; 666 private alias PtrTo(U) = U*; 667 alias Pointers = staticMap!(PtrTo, T); 668 } 669 670 671 //****************************************************************************** 672 //***** UNIT-TESTS 673 //****************************************************************************** 674 675 import std.stdio; 676 677 unittest 678 { 679 auto em = new EntityManager(new EventManager()); 680 681 auto ent0 = em.create(); 682 assert(em.capacity == 1); 683 assert(em.size == 1); 684 assert(ent0.valid); 685 assert(ent0.id.uniqueId == 1); 686 assert(ent0.id.versionId == 0); 687 688 ent0.destroy(); 689 assert(em.capacity == 1); 690 assert(em.size == 0); 691 assert(!ent0.valid); 692 assert(ent0.id.uniqueId == 0); 693 assert(ent0.id.versionId == 0); 694 695 ent0 = em.create(); 696 auto ent1 = em.create(); 697 auto ent2 = em.create(); 698 assert(em.capacity == 3); 699 assert(em.size == 3); 700 assert(ent0.id.uniqueId == 1); 701 assert(ent0.id.versionId == 1); 702 assert(ent1.id.uniqueId == 2); 703 assert(ent1.id.versionId == 0); 704 assert(ent2.id.uniqueId == 3); 705 assert(ent2.id.versionId == 0); 706 707 @component struct NameComponent 708 { 709 string name; 710 } 711 712 @component struct PosComponent 713 { 714 int x, y; 715 } 716 ent0.register!NameComponent(); 717 ent1.register!NameComponent(); 718 ent2.register!NameComponent(); 719 720 ent0.register!PosComponent(); 721 ent2.register!PosComponent(); 722 723 ent0.component!NameComponent.name = "Hello"; 724 ent1.component!NameComponent.name = "World"; 725 ent2.component!NameComponent.name = "Again"; 726 assert(ent0.component!NameComponent.name == "Hello"); 727 assert(ent1.component!NameComponent.name == "World"); 728 assert(ent2.component!NameComponent.name == "Again"); 729 730 ent0.component!PosComponent = PosComponent(5, 6); 731 ent2.component!PosComponent = PosComponent(2, 3); 732 assert(ent0.component!PosComponent.x == 5); 733 assert(ent0.component!PosComponent.y == 6); 734 assert(ent2.component!PosComponent.x == 2); 735 assert(ent2.component!PosComponent.y == 3); 736 737 //ent1.destroy(); 738 739 // List all current valid entities 740 foreach (ent; em) 741 { 742 assert(ent.valid); 743 //writeln(ent.component!NameComponent.name); 744 } 745 746 // List all name components 747 foreach (comp; em.components!NameComponent) 748 { 749 //writeln(comp.name); 750 } 751 752 // List all name components 753 foreach (ent; em.entitiesWith!(NameComponent, PosComponent)) 754 { 755 assert(ent.valid); 756 //writeln(ent.component!NameComponent.name); 757 } 758 759 // Check const fields are properly handled 760 @component struct ConstComp 761 { 762 int a, b; 763 const float cFloat = 5.0; 764 immutable int iInt = 5; 765 } 766 767 ent0.register!ConstComp(); 768 assert(ent0.component!ConstComp.cFloat == 5.0); 769 770 // Check immutable fields are not accepted 771 @component struct ImmutableComp 772 { 773 int a, b; 774 shared float sFloat = 5.0; 775 __gshared float gsFloat = 5.0; 776 } 777 778 // Check it will NOT compile if a field is shared 779 assert(!__traits(compiles, ent0.register!ImmutableComp())); 780 } 781 782 unittest 783 { 784 @component struct Velocity { int x, y; } 785 @component struct Position { int x, y; } 786 787 auto em = new EntityManager(new EventManager()); 788 789 auto ent0 = em.create(); 790 auto ent1 = em.create(); 791 792 ent0.register!Position(0, 0); 793 ent1.register!Position(4, 5); 794 795 ent0.register!Velocity(1, 2); 796 ent1.register!Velocity(3, 4); 797 798 // test getting components from the opApply loop 799 foreach(ent, pos, vel; em.entitiesWith!(Position, Velocity)) 800 { 801 pos.x += vel.x; 802 pos.y += vel.y; 803 } 804 805 assert(ent0.component!Position.x == 1 && ent0.component!Position.y == 2); 806 assert(ent1.component!Position.x == 7 && ent1.component!Position.y == 9); 807 } 808 809 // Ensure that em.components!T does not throw if no `T` has ever been registered 810 unittest 811 { 812 @component struct Dummy { } 813 814 auto em = new EntityManager(new EventManager()); 815 816 foreach(pos ; em.components!Dummy) 817 assert(0, "Loop should never be entered"); 818 } 819 820 // Validate fix for a bug where you could end up with uninitialized pools. 821 // ent.isRegistered would create room for a pool without allocating it, 822 // potentially creating null pools in the middle of the collection. 823 // register was only checking the collection length, but did not ensure that the 824 // pool it retrieved to store the component was non-null. 825 unittest 826 { 827 @component struct Dummy1 { } 828 @component struct Dummy2 { } 829 830 auto em = new EntityManager(new EventManager()); 831 auto ent = em.create(); 832 833 assert(!ent.isRegistered!Dummy1); 834 assert(!ent.isRegistered!Dummy2); 835 assert(ent.register!Dummy2); 836 assert(ent.register!Dummy1); 837 } 838 839 // Test range interface for components!T 840 unittest 841 { 842 @component struct A 843 { 844 int a; 845 } 846 847 @component struct B 848 { 849 string b; 850 } 851 852 auto em = new EntityManager(new EventManager()); 853 854 auto e1 = em.create(); 855 auto e2 = em.create(); 856 auto e3 = em.create(); 857 858 e1.register!A(1); 859 e2.register!B("2"); 860 e3.register!A(3); 861 e3.register!B("3"); 862 863 import std.algorithm : map, equal; 864 assert(em.components!A.map!(x => x.a).equal([1, 3])); 865 assert(em.components!B.map!(x => x.b).equal(["2", "3"])); 866 } 867 868 869 // Test component accessors 870 unittest 871 { 872 import std.conv; 873 string output; 874 875 @component struct A 876 { 877 int i; 878 } 879 880 @component struct B 881 { 882 string str; 883 } 884 885 auto em = new EntityManager(new EventManager()); 886 887 auto e1 = em.create(); 888 auto e2 = em.create(); 889 auto e3 = em.create(); 890 891 e1.register!A(1); 892 e2.register!B("hello"); 893 e3.register!A(3); 894 e3.register!B("world"); 895 896 void accessorForA(Entity e, A* a) 897 { 898 assert(e == e1 || e == e3); 899 output ~= a.i.to!string; 900 } 901 902 em.accessor!A = &accessorForA; 903 assert(em.accessor!A == &accessorForA); 904 em.accessor!B = (e, b) { output ~= b.str; }; // use lambda 905 906 e1.iterate(); 907 assert(output == "1"); 908 909 output = ""; 910 e2.iterate(); 911 assert(output == "hello"); 912 913 output = ""; 914 e3.iterate(); 915 assert(output == "3world"); 916 }