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 }