1 /************************************************************************************************************************ 2 Most important module. Almost every function is called from EntityManager. 3 4 Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz 5 License: BSD 3-clause, see LICENSE file in project root folder. 6 */ 7 module bubel.ecs.manager; 8 9 import std.algorithm : max; 10 import std.conv : to; 11 import std.traits; 12 13 import bubel.ecs.system; //not ordered as forward reference bug workaround 14 import bubel.ecs.block_allocator; 15 import bubel.ecs.entity; 16 import bubel.ecs.events; 17 import bubel.ecs.hash_map; 18 import bubel.ecs.id_manager; 19 import bubel.ecs.simple_vector; 20 import bubel.ecs.std; 21 import bubel.ecs.traits; 22 import bubel.ecs.vector; 23 import bubel.ecs.atomic; 24 25 alias SerializeVector = bubel.ecs.vector.Vector!ubyte; 26 27 ///Global EntityManager used for everything. 28 export __gshared EntityManager* gEntityManager = null; 29 30 /************************************************************************************************************************ 31 Entity manager is responsible for everything. 32 33 Entity manager can be in three states: 34 - registration: time between beginRegister() and endRegister() calls. 35 - update: time between being() and end() calls. 36 - default: when it's not in registration or update time 37 38 Manager can be only in one state simultaneously. 39 40 Manager must be initialized before use. There is global instance of EntityManager: gEntityManager or gEntityManager as alias. 41 42 Registration process consist of registration of passes, systems, entities and events. 43 44 Pass is group of system which should be used inside one update() call. Passes are added as name (string) and can be referenced by name or id. 45 System is structure responsible for update of specified group of entities. System consist of EntitiesData structure which contain components used 46 by system and some callback. Main callback is onUpdate() which is called by update() entity manager function. Other callbacks are used as listeners for 47 adding entites, tracking system lifetime and events handling. 48 49 Component is basicly small fraction of data which is considered to be used as whole. In best scenario every byte of component is used when it's refered to. 50 In practice sometimes it's better to join data into one component even if it's can be accessed separetly. 51 Events are structures with data used to handle events. Event can contain for exmaple one floating point number used as damage dealt to entity. 52 Entity is group of components. In memory entity is only ID which makes it's possible to take it's components. Components are grouped into chunks, and 53 grouped by component type so entity can be fracted in big memory chunk. 54 55 There is two types of update: 56 - update(): function used to call update pass. 57 - updateMT(): function used to call update pass multithreaded. This call only generates jobs which must be called by user. 58 59 WARNING! No fileds from EntityManager should be used directly, they are not private for special, advanced things which are not implemented in library. 60 So if you don't need anything special, simply use only EntityManager methods. 61 */ 62 export struct EntityManager 63 { 64 /************************************************************************************************************************ 65 Initialize ECS. 66 */ 67 export static void initialize(uint threads_count = 1, uint page_size = 32768, 68 uint block_pages_count = 128) 69 { 70 if (gEntityManager is null) 71 { 72 gEntityManager = Mallocator.make!EntityManager(threads_count, page_size, block_pages_count); 73 74 with (gEntityManager) 75 { 76 UpdatePass* pass = Mallocator.make!UpdatePass; 77 pass.name = Mallocator.makeArray(cast(char[]) "update"); 78 passes.add(pass); 79 passes_map.add(cast(const(char)[]) pass.name, cast(ushort)(passes.length - 1)); 80 } 81 } 82 } 83 84 /************************************************************************************************************************ 85 Deinitialize and destroy ECS. This function release whole memory. 86 */ 87 export static void destroy() nothrow @nogc 88 { 89 if (gEntityManager is null) 90 return; 91 92 with (gEntityManager) 93 { 94 foreach (ref system; systems) 95 { 96 system.destroy(); 97 } 98 99 foreach (EntityInfo* info; &entities_infos.byValue) 100 { 101 //if(info.components)Mallocator.dispose(info.components); 102 103 Mallocator.dispose(info); 104 } 105 106 foreach (UpdatePass* pass; passes) 107 { 108 Mallocator.dispose(pass); 109 } 110 passes.clear(); 111 112 foreach (ComponentInfo info; components) 113 { 114 if (info.init_data) 115 Mallocator.dispose(info.init_data); 116 } 117 118 foreach (EventInfo info; events) 119 { 120 if (info.callers) 121 Mallocator.dispose(info.callers); 122 } 123 124 foreach (name; &components_map.byKey) 125 { 126 if (name) 127 Mallocator.dispose(name); 128 } 129 } 130 131 Mallocator.dispose(gEntityManager); 132 gEntityManager = null; 133 } 134 135 /************************************************************************************************************************ 136 Begin registering process. Every register function should be called between beginRegister() and endRegister(). 137 */ 138 export void beginRegister() nothrow @nogc 139 { 140 assert(!register_state, "beginRegister() can't be called twice before endRegister();"); 141 register_state = true; 142 143 foreach (pass; passes) 144 { 145 foreach (caller; pass.system_callers) 146 { 147 Mallocator.dispose(caller); 148 } 149 pass.system_callers.clear(); 150 } 151 } 152 153 /************************************************************************************************************************ 154 End registering process. Every register function should be called between beginRegister() and endRegister(). 155 */ 156 export void endRegister() 157 { 158 assert(register_state, "beginRegister() should be called before endRegister();"); 159 register_state = false; 160 161 foreach (ref info; &entities_infos.byValue) 162 { 163 if (info.systems) 164 Mallocator.dispose(info.systems); 165 info.systems = Mallocator.makeArray!bool(systems.length); 166 } 167 168 foreach (ref system; systems) 169 { 170 if (system.isAlive() == false) 171 continue; 172 if (system.m_empty) 173 { 174 if (system.m_update) 175 addSystemCaller(system.id); 176 continue; 177 } 178 if (system.m_update is null) 179 { 180 if (system.m_add_entity || system.m_remove_entity 181 || system.m_change_entity || system.m_event_callers.length) 182 { 183 foreach (info; &entities_infos.byValue) 184 { 185 connectListenerToEntityInfo(*info, system.id); 186 } 187 } 188 continue; 189 } 190 191 /*bool added = false; 192 foreach (i, caller; passes[system.m_pass].system_callers) 193 { 194 if (systems[caller.system_id].priority > system.priority) 195 { 196 SystemCaller* sys_caller = Mallocator.make!SystemCaller; 197 sys_caller.system_id = system.id; 198 sys_caller.job_group.caller = sys_caller; 199 system.m_any_system_caller = sys_caller; 200 passes[system.m_pass].system_callers.add(sys_caller, i); 201 added = true; 202 break; 203 } 204 } 205 if (!added) 206 { 207 SystemCaller* sys_caller = Mallocator.make!SystemCaller; 208 sys_caller.system_id = system.id; 209 sys_caller.job_group.caller = sys_caller; 210 system.m_any_system_caller = sys_caller; 211 passes[system.m_pass].system_callers.add(sys_caller); 212 }*/ 213 addSystemCaller(system.id); 214 215 foreach (info; &entities_infos.byValue) 216 { 217 addSystemCaller(*info, system.id); 218 //info.systems[system.id] = true; 219 } 220 } 221 222 event_manager.allocateData(cast(uint) threads.length); 223 224 foreach (ref info; events) 225 { 226 Mallocator.dispose(info.callers); 227 } 228 229 ushort[] event_callers = (cast(ushort*) alloca(ushort.sizeof * events.length))[0 230 .. events.length]; 231 foreach (ref caller; event_callers) 232 caller = 0; 233 234 foreach (ref system; systems) 235 { 236 if (system.isAlive() == false) 237 continue; 238 foreach (caller; system.m_event_callers) 239 { 240 event_callers[caller.id]++; 241 } 242 } 243 244 foreach (i, ref info; events) 245 { 246 info.callers = Mallocator.makeArray!(EventCaller)(event_callers[i]); 247 } 248 249 foreach (ref caller; event_callers) 250 caller = 0; 251 252 foreach (ref system; systems) 253 { 254 if (system.isAlive() == false) 255 continue; 256 foreach (caller; system.m_event_callers) 257 { 258 events[caller.id].callers[event_callers[caller.id]].callback = caller.callback; 259 events[caller.id].callers[event_callers[caller.id]].system = &system; 260 event_callers[caller.id]++; 261 } 262 } 263 264 extern (C) static int comapreEventCaller(const void* a, const void* b) nothrow @nogc 265 { 266 EventCaller _a = *cast(EventCaller*) a; 267 EventCaller _b = *cast(EventCaller*) b; 268 if (_a.system.priority < _b.system.priority) 269 return -1; 270 else if (_a.system.priority == _b.system.priority) 271 return 0; 272 else 273 return 1; 274 } 275 276 foreach (ref event; events) 277 { 278 qsort(event.callers.ptr, event.callers.length, 279 EventCaller.sizeof, &comapreEventCaller); 280 } 281 //qsort(event_callers.ptr, event_callers.length, EventInfo.sizeof, &compareUShorts); 282 283 foreach (EntityInfo* info; &entities_infos.byValue) 284 { 285 generateListeners(info); 286 } 287 288 generateDependencies(); 289 } 290 291 /************************************************************************************************************************ 292 *Default constructor. 293 */ 294 export this(uint threads_count, uint page_size, uint block_pages_count) nothrow @nogc 295 { 296 if (threads_count == 0) 297 threads_count = 1; 298 threads = Mallocator.makeArray!ThreadData(threads_count); 299 300 m_page_size = page_size; 301 m_pages_in_block = block_pages_count; 302 303 id_manager.initialize(); 304 event_manager.initialize(&this); 305 306 allocator = BlockAllocator(m_page_size, m_pages_in_block); 307 308 //add_mutex = Mallocator.make!Mutex; 309 entity_block_alloc_mutex = Mallocator.make!Mutex; 310 entity_block_alloc_mutex.initialize(); 311 //event_manager = EventManager(this); 312 //event_manager.manager = this; 313 } 314 315 export ~this() nothrow @nogc 316 { 317 id_manager.deinitialize(); 318 event_manager.destroy(); 319 320 if (threads) 321 Mallocator.dispose(threads); 322 if (entity_block_alloc_mutex) 323 { 324 entity_block_alloc_mutex.destroy(); 325 Mallocator.dispose(entity_block_alloc_mutex); 326 entity_block_alloc_mutex = null; 327 } 328 329 allocator.freeMemory(); 330 } 331 332 /************************************************************************************************************************ 333 Unregister given system form EntityManager. 334 */ 335 void unregisterSystem(Sys)() 336 { 337 ushort system_id = becsID!Sys; 338 System* system = getSystem(system_id); 339 340 unregisterSystem(system); 341 } 342 343 /************************************************************************************************************************ 344 Unregister given system form EntityManager. 345 */ 346 export void unregisterSystem(System* system) nothrow @nogc 347 { 348 assert(register_state, "unregisterSystem must be called between beginRegister() and endRegister()."); 349 assert(system !is null, "System was not registered"); 350 assert(system.isAlive, "System already unregistered"); 351 352 //disable, destroy and dispose user created system but keep name and other data 353 system.destroySystemData(); 354 } 355 356 /************************************************************************************************************************ 357 Same as "void registerSystem(Sys)(int priority, int pass = 0)" but use pass name instead of id. 358 */ 359 void registerSystem(Sys)(int priority, const(char)[] pass_name) 360 { 361 ushort pass = passes_map.get(pass_name, ushort.max); 362 version (D_BetterC) 363 assert(pass != ushort.max, "Update pass doesn't exist."); 364 else 365 assert(pass != ushort.max, "Update pass (Name " ~ pass_name ~ ") doesn't exist."); 366 registerSystem!(Sys)(priority, pass); 367 } 368 369 /************************************************************************************************************************ 370 Register new System into EntityManager. This funcion generate glue between EntityManager and System. 371 Systems can be registered from external dynamic library, and can be registered after adding entities too. 372 System mustn't be registered before components which system want to use, in this case functions call assertion. 373 374 Params: 375 priority = system priority. Priority determines order of execution of systems updates 376 pass = index of UpdatePass which sholud call system update 377 */ 378 void registerSystem(Sys)(int priority, ushort pass = 0) 379 { 380 //alias STC = ParameterStorageClass; 381 382 assert(register_state, 383 "registerSystem must be called between beginRegister() and endRegister()."); 384 version (D_BetterC) 385 assert(pass < passes.length, "Update pass doesn't exist."); 386 else 387 assert(pass < passes.length, "Update pass (ID " ~ pass.to!string ~ ") doesn't exist."); 388 389 // enum SystemName = fullyQualifiedName!Sys; 390 enum SystemName = fullName!Sys; 391 //enum SystemName = Sys.stringof; 392 393 System system; 394 system.m_pass = pass; 395 396 // static if (!(hasMember!(Sys, "system_id")) || !is(typeof(Sys.system_id) == ushort)) 397 // { 398 // static assert(0, "Add \"mixin ECS.System;\" in top of system structure;"); 399 // } 400 401 static if (!(hasMember!(Sys, "EntitiesData"))) 402 { 403 static assert(0, "System should gave \"EntitiesData\" struct for input components"); 404 } 405 406 static if (hasMember!(Sys, "handleEvent")) 407 { 408 static void callEventHandler(Type)(ref EventCallData data) 409 { 410 Sys* data_system = cast(Sys*) data.system_pointer; 411 412 Type* event = cast(Type*) data.event; 413 data_system.handleEvent(data.entity, *event); 414 } 415 416 void setEventCallers(Sys)(ref System system) 417 { 418 enum event_handlers_num = __traits(getOverloads, Sys, "handleEvent").length; 419 System.EventCaller[] callers = (cast(System.EventCaller*) alloca( 420 event_handlers_num * System.EventCaller.sizeof))[0 .. event_handlers_num]; 421 int i = 0; 422 423 foreach (j, func; __traits(getOverloads, Sys, "handleEvent")) 424 { 425 alias Params = Parameters!(__traits(getOverloads, Sys, "handleEvent")[j]); 426 static if (Params.length == 2 && is(Params[0] == Entity*)) 427 { 428 alias EventParamType = Params[1]; 429 enum EventName = fullName!(Unqual!(EventParamType)); 430 // enum EventName = fullyQualifiedName!(Unqual!(EventParamType));//.stringof; 431 ushort evt = events_map.get(cast(char[]) EventName, ushort.max); 432 assert(evt != ushort.max, 433 "Can't register system \"" ~ SystemName 434 ~ "\" due to non existing event \"" ~ EventName ~ "\"."); 435 436 callers[i].callback = cast(void*)&callEventHandler!(EventParamType); 437 callers[i].id = becsID!EventParamType; 438 i++; 439 } 440 } 441 442 system.m_event_callers = Mallocator.makeArray(callers[0 .. i]); 443 } 444 445 static if (__traits(hasMember, Sys, "handleEvent")) 446 { 447 setEventCallers!(Sys)(system); 448 } 449 } 450 451 static struct CompInfo 452 { 453 string name; 454 string type; 455 } 456 457 static struct ComponentsCounts 458 { 459 //one more than should be to prevent null arrays (zero length arrays) 460 uint readonly = 1; 461 uint mutable = 1; 462 uint excluded = 1; 463 uint optional = 1; 464 uint req = 1; 465 uint readonly_dep = 1; 466 uint writable_dep = 1; 467 } 468 469 static ComponentsCounts getComponentsCounts() 470 { 471 ComponentsCounts components_counts; 472 473 bool checkExcludedComponentsSomething(Sys)() 474 { 475 return __traits(compiles, allSameType!(string, typeof(Sys.ExcludedComponents))) && allSameType!(string, 476 typeof(Sys.ExcludedComponents)) && isExpressions!(Sys.ExcludedComponents); 477 } 478 479 foreach (member; __traits(allMembers, Sys.EntitiesData)) 480 { 481 alias MemberType = typeof(__traits(getMember, Sys.EntitiesData, member)); 482 if (member == "length" || member == "thread_id" || member == "job_id" 483 || is(MemberType == Entity[]) || is(MemberType == const(Entity)[])) 484 { 485 //continue; 486 } 487 else 488 { 489 string name; 490 static if (isArray!MemberType) 491 { // Workaround. This code is never called with: not an array type, but compiler prints an error 492 // name = fullyQualifiedName!(Unqual!(ForeachType!MemberType));//.stringof; 493 name = fullName!(Unqual!(typeof(MemberType.init[0]))); 494 } 495 496 bool is_optional; 497 bool is_read_only; 498 499 if (is(CopyConstness!(ForeachType!(MemberType), int) == const(int))) 500 { 501 is_read_only = true; 502 } 503 504 foreach (att; __traits(getAttributes, __traits(getMember, 505 Sys.EntitiesData, member))) 506 { 507 if (att == "optional") 508 { 509 is_optional = true; 510 } 511 if (att == "readonly") 512 { 513 is_read_only = true; 514 } 515 } 516 if (is_read_only) 517 { 518 components_counts.readonly++; 519 } 520 else 521 { 522 components_counts.mutable++; 523 } 524 if (is_optional) 525 { 526 components_counts.optional++; 527 } 528 else 529 { 530 components_counts.req++; 531 } 532 } 533 } 534 535 static if (__traits(hasMember, Sys, "ExcludedComponents")) 536 { 537 static if (is(Sys.ExcludedComponents == enum)) 538 { 539 foreach (str; __traits(allMembers, Sys.ExcludedComponents)) 540 { 541 components_counts.excluded++; 542 } 543 } 544 else //static if (checkExcludedComponentsSomething!Sys) 545 { 546 foreach (str; Sys.ExcludedComponents) 547 { 548 components_counts.excluded++; 549 } 550 } 551 } 552 553 static if (__traits(hasMember, Sys, "ReadOnlyDependencies")) 554 { 555 foreach (str; Sys.ReadOnlyDependencies) 556 { 557 components_counts.readonly_dep++; 558 } 559 } 560 561 static if (__traits(hasMember, Sys, "WritableDependencies")) 562 { 563 foreach (str; Sys.WritableDependencies) 564 { 565 components_counts.writable_dep++; 566 } 567 } 568 569 return components_counts; 570 } 571 572 enum ComponentsCounts component_counts = getComponentsCounts(); 573 574 static struct ComponentsIndices(ComponentsCounts counts) 575 { 576 CompInfo[] readonly() 577 { 578 return m_readonly[0 .. m_readonly_counter]; 579 } 580 581 CompInfo[] mutable() 582 { 583 return m_mutable[0 .. m_mutable_counter]; 584 } 585 586 CompInfo[] excluded() 587 { 588 return m_excluded[0 .. m_excluded_counter]; 589 } 590 591 CompInfo[] optional() 592 { 593 return m_optional[0 .. m_optional_counter]; 594 } 595 596 CompInfo[] req() 597 { 598 return m_req[0 .. m_req_counter]; 599 } 600 601 CompInfo[] readonlyDeps() 602 { 603 return m_readonly_dep[0 .. m_readonly_dep_counter]; 604 } 605 606 CompInfo[] writableDeps() 607 { 608 return m_writable_dep[0 .. m_writable_dep_counter]; 609 } 610 611 void addReadonly(CompInfo info) 612 { 613 m_readonly[m_readonly_counter++] = info; 614 } 615 616 void addMutable(CompInfo info) 617 { 618 m_mutable[m_mutable_counter++] = info; 619 } 620 621 void addExcluded(CompInfo info) 622 { 623 m_excluded[m_excluded_counter++] = info; 624 } 625 626 void addOptional(CompInfo info) 627 { 628 m_optional[m_optional_counter++] = info; 629 } 630 631 void addReq(CompInfo info) 632 { 633 m_req[m_req_counter++] = info; 634 } 635 636 void addReadonlyDep(CompInfo info) 637 { 638 m_readonly_dep[m_readonly_dep_counter++] = info; 639 } 640 641 void addWritableDep(CompInfo info) 642 { 643 m_writable_dep[m_writable_dep_counter++] = info; 644 } 645 646 CompInfo[counts.readonly] m_readonly; 647 CompInfo[counts.mutable] m_mutable; 648 CompInfo[counts.excluded] m_excluded; 649 CompInfo[counts.optional] m_optional; 650 CompInfo[counts.req] m_req; 651 CompInfo[counts.readonly_dep] m_readonly_dep; 652 CompInfo[counts.writable_dep] m_writable_dep; 653 654 uint m_readonly_counter; 655 uint m_mutable_counter; 656 uint m_excluded_counter; 657 uint m_optional_counter; 658 uint m_req_counter; 659 uint m_readonly_dep_counter; 660 uint m_writable_dep_counter; 661 662 string entites_array; 663 } 664 665 static void allocateSystemComponents(ComponentsIndices!component_counts components_info)( 666 ref System system) 667 { 668 size_t req = components_info.req.length; 669 size_t opt = components_info.optional.length; 670 size_t excluded = components_info.excluded.length; 671 size_t read_only = components_info.readonly.length; 672 size_t writable = components_info.mutable.length; 673 size_t read_only_deps = components_info.readonlyDeps.length; 674 size_t writable_deps = components_info.writableDeps.length; 675 676 if (req > 0) 677 system.m_components = Mallocator.makeArray!ushort(req); 678 if (opt > 0) 679 system.m_optional_components = Mallocator.makeArray!ushort(opt); 680 if (excluded > 0) 681 system.m_excluded_components = Mallocator.makeArray!ushort(excluded); 682 if (read_only > 0) 683 system.m_read_only_components = Mallocator.makeArray!ushort(read_only); 684 if (writable > 0) 685 system.m_writable_components = Mallocator.makeArray!ushort(writable); 686 if (read_only_deps > 0) 687 system.m_readonly_dependencies = Mallocator.makeArray!ushort(read_only_deps); 688 if (writable_deps > 0) 689 system.m_writable_dependencies = Mallocator.makeArray!ushort(writable_deps); 690 691 } 692 693 static ComponentsIndices!component_counts getComponentsInfo() 694 { 695 ComponentsIndices!component_counts components_info; 696 697 bool checkExcludedComponentsSomething(Sys)() 698 { 699 return __traits(compiles, allSameType!(string, typeof(Sys.ExcludedComponents))) && allSameType!(string, 700 typeof(Sys.ExcludedComponents)) && isExpressions!(Sys.ExcludedComponents); 701 } 702 703 foreach (member; __traits(allMembers, Sys.EntitiesData)) 704 { 705 alias MemberType = typeof(__traits(getMember, Sys.EntitiesData, member)); 706 if (member == "length" || member == "thread_id" || member == "job_id" 707 || is(MemberType == Entity[]) || is(MemberType == const(Entity)[])) 708 { 709 if (is(MemberType == Entity[]) || is(MemberType == const(Entity)[])) 710 components_info.entites_array = member; 711 //continue; 712 } 713 else 714 { 715 string name; 716 static if (isArray!MemberType) 717 { // Workaround. This code is never called with: not an array type, but compiler prints an error 718 // name = fullyQualifiedName!(Unqual!(ForeachType!MemberType)); 719 name = fullName!(Unqual!(typeof(MemberType.init[0]))); 720 //name = Unqual!(ForeachType!MemberType).stringof; 721 } 722 723 bool is_optional; 724 bool is_read_only; 725 726 if (is(CopyConstness!(ForeachType!(MemberType), int) == const(int))) 727 { 728 is_read_only = true; 729 } 730 731 foreach (att; __traits(getAttributes, __traits(getMember, 732 Sys.EntitiesData, member))) 733 { 734 if (att == "optional") 735 { 736 is_optional = true; 737 } 738 if (att == "readonly") 739 { 740 is_read_only = true; 741 } 742 } 743 if (is_read_only) 744 { 745 components_info.addReadonly(CompInfo(member, name)); 746 } 747 else 748 { 749 components_info.addMutable(CompInfo(member, name)); 750 } 751 if (is_optional) 752 { 753 components_info.addOptional(CompInfo(member, name)); 754 } 755 else 756 { 757 components_info.addReq(CompInfo(member, name)); 758 } 759 } 760 } 761 762 static if (__traits(hasMember, Sys, "ExcludedComponents")) 763 { 764 static if (is(Sys.ExcludedComponents == enum)) 765 { 766 foreach (str; __traits(allMembers, Sys.ExcludedComponents)) 767 { 768 components_info.addExcluded(CompInfo(str, str)); 769 } 770 } 771 else //static if (checkExcludedComponentsSomething!Sys) 772 { 773 foreach (str; Sys.ExcludedComponents) 774 { 775 components_info.addExcluded(CompInfo(str.stringof, fullName!str)); 776 // components_info.addExcluded(CompInfo(str.stringof, str.stringof)); 777 } 778 779 } 780 } 781 782 static if (__traits(hasMember, Sys, "ReadOnlyDependencies")) 783 { 784 foreach (str; Sys.ReadOnlyDependencies) 785 { 786 components_info.addReadonlyDep(CompInfo(str, str)); 787 } 788 } 789 790 static if (__traits(hasMember, Sys, "WritableDependencies")) 791 { 792 foreach (str; Sys.WritableDependencies) 793 { 794 components_info.addWritableDep(CompInfo(str, str)); 795 } 796 } 797 798 return components_info; 799 } 800 801 enum ComponentsIndices!component_counts components_info = getComponentsInfo(); 802 803 static void genCompList(ref System system, ref HashMap!(char[], ushort) components_map) 804 { 805 806 foreach (member; __traits(allMembers, Sys.EntitiesData)) 807 { 808 alias MemberType = typeof(__traits(getMember, Sys.EntitiesData, member)); 809 810 static if (isFunction!(__traits(getMember, Sys.EntitiesData, member))) 811 static assert(0, "EntitiesData can't have any function!"); 812 else static if (member == "length") 813 { 814 static assert(isIntegral!(MemberType), 815 "EntitiesData 'length' member must be integral type."); 816 static assert(MemberType.sizeof > 1, 817 "EntitiesData 'length' member can't be byte or ubyte."); 818 } 819 else static if (member == "thread_id") 820 { 821 static assert(isIntegral!(MemberType), 822 "EntitiesData 'thread_id' member must be integral type."); 823 static assert(MemberType.sizeof > 1, 824 "EntitiesData 'thread_id' member can't be byte or ubyte."); 825 } 826 else static if (member == "job_id") 827 { 828 static assert(isIntegral!(MemberType), 829 "EntitiesData 'job_id' member must be integral type."); 830 static assert(MemberType.sizeof > 1, 831 "EntitiesData 'job_id' member can't be byte or ubyte."); 832 } 833 else static if (!(isArray!(MemberType))) 834 static assert(0, "EntitiesData members should be arrays of elements!"); 835 } 836 837 //enum ComponentsIndices components_info = getComponentsInfo(); 838 allocateSystemComponents!(components_info)(system); 839 840 foreach (iii, comp_info; components_info.req) 841 { 842 ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max); 843 version (D_BetterC) 844 assert(comp != ushort.max, 845 "Can't register system \"" ~ SystemName 846 ~ "\" due to non existing component."); 847 else 848 assert(comp != ushort.max, "Can't register system \"" ~ SystemName 849 ~ "\" due to non existing component \"" ~ comp_info.type ~ "\"."); 850 system.m_components[iii] = comp; 851 } 852 foreach (iii, comp_info; components_info.excluded) 853 { 854 ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max); 855 version (D_BetterC) 856 assert(comp != ushort.max, 857 "Can't register system \"" ~ SystemName 858 ~ "\" due to non existing component."); 859 else 860 assert(comp != ushort.max, "Can't register system \"" ~ SystemName 861 ~ "\" due to non existing component \"" ~ comp_info.type ~ "\"."); 862 system.m_excluded_components[iii] = comp; 863 } 864 foreach (iii, comp_info; components_info.optional) 865 { 866 ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max); 867 version (D_BetterC) 868 assert(comp != ushort.max, 869 "Can't register system \"" ~ SystemName 870 ~ "\" due to non existing component."); 871 else 872 assert(comp != ushort.max, "Can't register system \"" ~ SystemName 873 ~ "\" due to non existing component \"" ~ comp_info.type ~ "\"."); 874 system.m_optional_components[iii] = comp; 875 } 876 foreach (iii, comp_info; components_info.readonly) 877 { 878 ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max); 879 version (D_BetterC) 880 assert(comp != ushort.max, 881 "Can't register system \"" ~ SystemName 882 ~ "\" due to non existing component."); 883 else 884 assert(comp != ushort.max, "Can't register system \"" ~ SystemName 885 ~ "\" due to non existing component \"" ~ comp_info.type ~ "\"."); 886 system.m_read_only_components[iii] = comp; 887 } 888 foreach (iii, comp_info; components_info.mutable) 889 { 890 ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max); 891 version (D_BetterC) 892 assert(comp != ushort.max, 893 "Can't register system \"" ~ SystemName 894 ~ "\" due to non existing component."); 895 else 896 assert(comp != ushort.max, "Can't register system \"" ~ SystemName 897 ~ "\" due to non existing component \"" ~ comp_info.type ~ "\"."); 898 system.m_writable_components[iii] = comp; 899 } 900 } 901 902 static void fillInputData(ref Sys.EntitiesData input_data, EntityInfo* info, 903 EntitiesBlock* block, uint offset, uint entities_count, System* system) 904 { 905 //enum ComponentsIndices components_info = getComponentsInfo(); 906 907 static if (components_info.entites_array) 908 { 909 __traits(getMember, input_data, components_info.entites_array) = ( 910 cast(Entity*) block.dataBegin())[offset .. entities_count]; 911 } 912 913 static if (hasMember!(Sys.EntitiesData, "length")) 914 { 915 input_data.length = cast(typeof(input_data.length))(entities_count - offset); 916 } 917 918 /*static if (hasMember!(Sys.EntitiesData, "thread_id")) 919 { 920 input_data.thread_id = cast(typeof(input_data.thread_id))threadID(); 921 }//*/ 922 923 ///FIXME: should be "components_info.req()" but it's not compile with GCC 924 static foreach (iii, comp_info; components_info.m_req[0 925 .. components_info.m_req_counter]) 926 { 927 __traits(getMember, input_data, comp_info.name) = ( 928 cast(typeof( 929 (typeof(__traits(getMember, Sys.EntitiesData, comp_info.name))).init[0] 930 )*)(cast(void*) block + info.deltas[system.m_components[iii]]) 931 )[offset .. entities_count]; 932 } 933 934 static foreach (iii, comp_info; components_info.m_optional[0 935 .. components_info.m_optional_counter]) 936 { 937 if (system.m_optional_components[iii] < info.deltas.length 938 && info.deltas[system.m_optional_components[iii]] != 0) 939 { 940 __traits(getMember, input_data, comp_info.name) = (cast(ForeachType!(typeof(__traits(getMember, 941 Sys.EntitiesData, comp_info.name)))*)(cast( 942 void*) block + info.deltas[system.m_optional_components[iii]]))[offset 943 .. entities_count]; 944 945 } 946 } 947 } 948 949 /*bool checkOnUpdateParams() 950 { 951 bool ret = false; 952 foreach (func; __traits(getOverloads, Sys, "onUpdate")) 953 { 954 if ((Parameters!(func)).length == 1 && is(Parameters!(func)[0] == Sys.EntitiesData)) 955 { 956 ret = true; 957 break; 958 } 959 } 960 return ret; 961 }*/ 962 963 int getOnUpdateOverload()() 964 { 965 int ret = -1; 966 foreach (i, func; __traits(getOverloads, Sys, "onUpdate")) 967 { 968 if ((Parameters!(func)).length == 1 && is(Parameters!(func)[0] == Sys.EntitiesData)) 969 { 970 ret = i; 971 break; 972 } 973 } 974 return ret; 975 } 976 977 static if (hasMember!(Sys, "onUpdate")) 978 enum OnUpdateOverloadNum = getOnUpdateOverload(); 979 else 980 enum OnUpdateOverloadNum = -1; 981 //enum HasOnUpdate = (hasMember!(Sys, "onUpdate") && checkOnUpdateParams()); 982 enum IsEmpty = components_info.req.length == 0 && components_info.optional.length == 0 983 && components_info.excluded.length == 0 && components_info.entites_array.length == 0; 984 985 static if (IsEmpty) 986 system.m_empty = true; 987 988 static if (OnUpdateOverloadNum != -1) 989 { 990 static if (!IsEmpty) 991 { 992 static void callUpdate(ref CallData data) 993 { 994 Sys* s = cast(Sys*) data.system.m_system_pointer; 995 996 Sys.EntitiesData input_data; 997 EntityInfo* info = data.info; //block.type_info; 998 System* system = data.system; 999 1000 EntitiesBlock* block; 1001 if (data.first_block) 1002 block = data.first_block; 1003 else 1004 block = info.first_block; 1005 1006 uint offset = data.begin; 1007 uint entities_count; 1008 uint blocks; 1009 if (data.blocks) 1010 blocks = data.blocks; 1011 else 1012 blocks = uint.max; 1013 1014 while (block !is null && blocks > 0) 1015 { 1016 if (blocks == 1) 1017 { 1018 if (data.end) 1019 entities_count = data.end; 1020 else 1021 entities_count = block.entities_count; 1022 } 1023 else 1024 entities_count = block.entities_count; 1025 1026 if (entities_count > 0) 1027 { 1028 assert(entities_count <= block.entities_count 1029 && offset < block.entities_count); 1030 assert(entities_count > offset); 1031 1032 fillInputData(input_data, info, block, offset, entities_count, system); 1033 1034 static if (hasMember!(Sys.EntitiesData, "thread_id")) 1035 { 1036 input_data.thread_id = cast( 1037 typeof(input_data.thread_id)) data.thread_id; 1038 } 1039 1040 static if (hasMember!(Sys.EntitiesData, "job_id")) 1041 { 1042 input_data.job_id = cast(typeof(input_data.job_id)) data.job_id; 1043 } 1044 1045 //s.onUpdate(input_data); 1046 (cast(typeof(&__traits(getOverloads, s, 1047 "onUpdate")[OnUpdateOverloadNum])) data.update_delegate)( 1048 input_data); 1049 } 1050 block = block.next_block; 1051 offset = 0; 1052 blocks--; 1053 } 1054 } 1055 } 1056 else 1057 { 1058 static void callUpdate(ref CallData data) 1059 { 1060 Sys* s = cast(Sys*) data.system.m_system_pointer; 1061 1062 Sys.EntitiesData input_data; 1063 1064 /*static if (hasMember!(Sys.EntitiesData, "length")) 1065 { 1066 input_data.length = 0; 1067 }//*/ 1068 1069 static if (hasMember!(Sys.EntitiesData, "thread_id")) 1070 { 1071 input_data.thread_id = cast(typeof(input_data.thread_id)) data.thread_id; 1072 } 1073 1074 static if (hasMember!(Sys.EntitiesData, "job_id")) 1075 { 1076 input_data.job_id = cast(typeof(input_data.job_id)) data.job_id; 1077 } 1078 1079 (cast(typeof(&__traits(getOverloads, s, 1080 "onUpdate")[OnUpdateOverloadNum])) data.update_delegate)(input_data); 1081 } 1082 } 1083 1084 system.m_update = &callUpdate; 1085 } 1086 1087 static void catchFunction(string func_name, RetType = void)(void** member) 1088 { 1089 static if (hasMember!(Sys, func_name)) 1090 { 1091 foreach (func; __traits(getOverloads, Sys, func_name)) 1092 { 1093 static if ((Parameters!(func)).length == 0 && is(ReturnType!(func) == RetType)) 1094 { 1095 static RetType callFunc(void* system_pointer) 1096 { 1097 Sys* s = cast(Sys*) system_pointer; 1098 static if (is(RetTyp == void)) 1099 mixin("s." ~ func_name ~ "()"); 1100 else 1101 return mixin("s." ~ func_name ~ "()"); 1102 } 1103 1104 *member = cast(void*)&callFunc; 1105 break; 1106 } 1107 } 1108 } 1109 } 1110 1111 static void catchEntityFunction(string func_name, RetType = void)(void** member) 1112 { 1113 static if (hasMember!(Sys, func_name)) 1114 { 1115 foreach (func; __traits(getOverloads, Sys, func_name)) 1116 { 1117 static if ((Parameters!(func)).length == 1 1118 && is(Parameters!(func)[0] == Sys.EntitiesData) 1119 && is(ReturnType!(func) == RetType)) 1120 { 1121 static RetType callFunc(ref ListenerCallData data) 1122 { 1123 Sys* s = cast(Sys*) data.system.m_system_pointer; 1124 Sys.EntitiesData input_data; 1125 fillInputData(input_data, data.block.type_info, 1126 data.block, data.begin, data.end, data.system); 1127 static if (is(RetTyp == void)) 1128 mixin("s." ~ func_name ~ "(input_data)"); 1129 else 1130 return mixin("s." ~ func_name ~ "(input_data)"); 1131 } 1132 1133 *member = cast(void*)&callFunc; 1134 break; 1135 } 1136 } 1137 } 1138 } 1139 1140 static void catchEntityFilterFunction(string func_name, RetType = void)(void** member) 1141 { 1142 static if (hasMember!(Sys, func_name)) 1143 { 1144 foreach (func; __traits(getOverloads, Sys, func_name)) 1145 { 1146 static if ((Parameters!(func)).length == 1 1147 && is(Parameters!(func)[0] == EntityInfo*) 1148 && is(ReturnType!(func) == RetType)) 1149 { 1150 static RetType callFunc(void* system_pointer, EntityInfo* info) 1151 { 1152 Sys* s = cast(Sys*) system_pointer; 1153 static if (is(RetTyp == void)) 1154 mixin("s." ~ func_name ~ "(info)"); 1155 else 1156 return mixin("s." ~ func_name ~ "(info)"); 1157 } 1158 1159 *member = cast(void*)&callFunc; 1160 break; 1161 } 1162 } 1163 } 1164 } 1165 1166 catchFunction!("onEnable")(&system.m_enable); 1167 catchFunction!("onDisable")(&system.m_disable); 1168 catchFunction!("onCreate")(&system.m_create); 1169 catchFunction!("onDestroy")(&system.m_destroy); 1170 catchFunction!("onBegin", bool)(&system.m_begin); 1171 catchFunction!("onEnd")(&system.m_end); 1172 1173 catchEntityFunction!("onAddEntity")(&system.m_add_entity); 1174 catchEntityFunction!("onRemoveEntity")(&system.m_remove_entity); 1175 catchEntityFunction!("onChangeEntity")(&system.m_change_entity); 1176 1177 catchEntityFilterFunction!("filterEntity", bool)(&system.m_filter_entity); 1178 1179 system.m_system_pointer = cast(void*) Mallocator.make!Sys; 1180 system.m_priority = priority; 1181 //(cast(Sys*) system.m_system_pointer).__ecsInitialize(); 1182 //system.jobs = (cast(Sys*) system.m_system_pointer)._ecs_jobs; 1183 static if (__traits(hasMember, Sys, "__becs_jobs_count")) 1184 system.jobs = Mallocator.makeArray!(Job)(Sys.__becs_jobs_count); 1185 else 1186 system.jobs = Mallocator.makeArray!(Job)(32); 1187 1188 static if (OnUpdateOverloadNum != -1) 1189 { 1190 Sys* s = cast(Sys*) system.m_system_pointer; 1191 system.m_update_delegate = cast(void delegate())&__traits(getOverloads, 1192 s, "onUpdate")[OnUpdateOverloadNum]; 1193 } 1194 1195 genCompList(system, components_map); 1196 1197 foreach (iii, comp_info; components_info.readonlyDeps) 1198 { 1199 ushort comp = external_dependencies_map.get(cast(const(char)[]) comp_info.type, 1200 ushort.max); 1201 version (D_BetterC) 1202 assert(comp != ushort.max, 1203 "Can't register system \"" ~ SystemName 1204 ~ "\" due to non existing dependency."); 1205 else 1206 assert(comp != ushort.max, "Can't register system \"" ~ SystemName 1207 ~ "\" due to non existing dependency \"" ~ comp_info.type ~ "\"."); 1208 system.m_readonly_dependencies[iii] = comp; 1209 } 1210 1211 foreach (iii, comp_info; components_info.writableDeps) 1212 { 1213 ushort comp = external_dependencies_map.get(cast(char[]) comp_info.type, ushort.max); 1214 version (D_BetterC) 1215 assert(comp != ushort.max, 1216 "Can't register system \"" ~ SystemName 1217 ~ "\" due to non existing dependency."); 1218 else 1219 assert(comp != ushort.max, "Can't register system \"" ~ SystemName 1220 ~ "\" due to non existing dependency \"" ~ comp_info.type ~ "\"."); 1221 system.m_writable_dependencies[iii] = comp; 1222 } 1223 1224 ushort sys_id = systems_map.get(cast(char[]) SystemName, ushort.max); 1225 if (sys_id < systems.length) 1226 { 1227 system.m_name = systems[sys_id].m_name; 1228 systems[sys_id].m_name = null; 1229 systems[sys_id].destroy(); 1230 1231 if (system.m_create) 1232 (cast(void function(void*)) system.m_create)(system.m_system_pointer); 1233 1234 system.enable(); 1235 1236 system.m_id = sys_id; 1237 systems[sys_id] = system; 1238 } 1239 else 1240 { 1241 system.m_name = Mallocator.makeArray(cast(char[]) SystemName); 1242 1243 systems_map.add(system.m_name, cast(ushort) systems.length); 1244 1245 system.m_id = cast(ushort)(systems.length); 1246 1247 systems.add(system); 1248 1249 if (system.m_create) 1250 (cast(void function(void*)) system.m_create)(system.m_system_pointer); 1251 1252 systems[$ - 1].enable(); 1253 } 1254 becsID!Sys = system.id; 1255 } 1256 1257 /************************************************************************************************************************ 1258 Return system ECS api by id. 1259 System pointer might become invalid after registration process. It should be get once again after new systems are registered. 1260 */ 1261 export System* getSystem(ushort id) nothrow @nogc 1262 { 1263 if (id >= systems.length) 1264 return null; 1265 return &systems[id]; 1266 } 1267 1268 /************************************************************************************************************************ 1269 Return pointer to system registered in manager 1270 */ 1271 Sys* getSystem(Sys)() nothrow @nogc 1272 { 1273 if (becsID!Sys >= systems.length) 1274 return null; 1275 return cast(Sys*) systems[becsID!Sys].m_system_pointer; 1276 } 1277 1278 export ushort registerPass(const(char)[] name) 1279 { 1280 UpdatePass* pass = Mallocator.make!UpdatePass; 1281 pass.name = Mallocator.makeArray(cast(char[]) name); 1282 passes.add(pass); 1283 passes_map.add(name, cast(ushort)(passes.length - 1)); 1284 return cast(ushort)(passes.length - 1); 1285 } 1286 1287 export void registerDependency(const(char)[] name) 1288 { 1289 return external_dependencies_map.add(name, cast(ushort) external_dependencies_map.length); 1290 } 1291 1292 /************************************************************************************************************************ 1293 Register component into EntityManager. 1294 */ 1295 void registerComponent(Comp)() 1296 { 1297 ComponentInfo info; 1298 1299 // enum ComponentName = fullyQualifiedName!Comp; 1300 enum ComponentName = fullName!Comp; 1301 // enum ComponentName = Comp.stringof; 1302 1303 // static if (!(hasMember!(Comp, "component_id")) || !is(typeof(Comp.component_id) == ushort)) 1304 // { 1305 // static assert(0, "Add \"mixin ECS.Component;\" in top of component structure;"); 1306 // } 1307 1308 static if (hasMember!(Comp, "onDestroy") && isFunction!(Comp.onDestroy) 1309 && is(ReturnType!(Comp.onDestroy) == void) 1310 && Parameters!(Comp.onDestroy).length == 0) 1311 { 1312 static void callDestroy(void* pointer) nothrow @nogc 1313 { 1314 (cast(void delegate() nothrow @nogc)&(cast(Comp*) pointer).onDestroy)(); 1315 } 1316 1317 info.destroy_callback = &callDestroy; 1318 } 1319 1320 static if (hasMember!(Comp, "onCreate") && isFunction!(Comp.onCreate) 1321 && is(ReturnType!(Comp.onCreate) == void) && Parameters!(Comp.onCreate).length == 0) 1322 { 1323 static void callCreate(void* pointer) nothrow @nogc 1324 { 1325 (cast(void delegate() nothrow @nogc)&(cast(Comp*) pointer).onCreate)(); 1326 } 1327 1328 info.create_callback = &callCreate; 1329 } 1330 1331 static if (Comp.sizeof == 1 && Fields!(Comp).length == 0) 1332 info.size = 0; 1333 else 1334 info.size = Comp.sizeof; 1335 info.alignment = Comp.alignof; //8; 1336 info.init_data = Mallocator.makeArray!ubyte(Comp.sizeof); 1337 __gshared Comp init_memory; 1338 memcpy(info.init_data.ptr, &init_memory, Comp.sizeof); 1339 1340 ushort comp_id = components_map.get(cast(char[]) ComponentName, ushort.max); 1341 if (comp_id < components.length) 1342 { 1343 becsID!Comp = comp_id; 1344 if (components[comp_id].init_data) 1345 Mallocator.dispose(components[comp_id].init_data); 1346 components[comp_id] = info; 1347 } 1348 else 1349 { 1350 components.add(info); 1351 becsID!Comp = cast(ushort)(components.length - 1); 1352 char[] name = Mallocator.makeArray(cast(char[]) ComponentName); 1353 components_map.add(name, cast(ushort)(components.length - 1)); 1354 } 1355 } 1356 1357 void registerEvent(Ev)() 1358 { 1359 EventInfo info; 1360 1361 // static if (!(hasMember!(Ev, "event_id")) || !is(typeof(Ev.event_id) == ushort)) 1362 // { 1363 // static assert(0, "Add \"mixin ECS.Event;\" in top of event structure;"); 1364 // } 1365 1366 static if (hasMember!(Ev, "onDestroy") && isFunction!(Ev.onDestroy) 1367 && is(ReturnType!(Ev.onDestroy) == void) && Parameters!(Ev.onDestroy).length == 0) 1368 { 1369 static void callDestroy(void* pointer) 1370 { 1371 (cast(Ev*) pointer).onDestroy(); 1372 } 1373 1374 info.destroy_callback = cast(void function(void*) nothrow @nogc)&callDestroy; 1375 } 1376 1377 info.size = Ev.sizeof; 1378 info.alignment = Ev.alignof; 1379 1380 //ushort event_id = events_map.get(Ev.stringof, ushort.max); 1381 ushort event_id = events_map.get(fullName!Ev, ushort.max); 1382 if (event_id < events.length) 1383 { 1384 becsID!Ev = event_id; 1385 } 1386 else 1387 { 1388 events.add(info); 1389 becsID!Ev = cast(ushort)(events.length - 1); 1390 // events_map.add(Ev.stringof, cast(ushort)(events.length - 1)); 1391 events_map.add(fullName!Ev, cast(ushort)(events.length - 1)); 1392 } 1393 } 1394 1395 export void callEntitiesFunction(Sys, T)(T func) 1396 { 1397 //TODO: check if onUpdate function is good 1398 Sys* s; 1399 static assert(isDelegate!func, "Function must be delegate."); 1400 static assert(__traits(hasMember, Sys, "EntitiesData"), 1401 "Can't call function with system which hasn't EntitesData structure."); 1402 ///TODO: make possibly to call function to group without system with onUpdate function 1403 static assert(__traits(hasMember, Sys, "onUpdate"), 1404 "Can't call function with system which hasn't onUpdate function callback."); 1405 // static assert(is(SetFunctionAttributes!(T, functionLinkage!(s.onUpdate), 1406 // functionAttributes!(s.onUpdate)) == typeof(&s.onUpdate)), 1407 // "Function must match system update function."); FIXME: It's lead to crash on android build 1408 // static assert(__traits(hasMember, Sys, "system_id"), "Sys must be system type."); 1409 1410 System* system = getSystem(becsID!Sys); 1411 assert(system != null, 1412 "System must be registered in EntityManager before any funcion can be called."); 1413 assert(system.isAlive(), "System must be alive (registered) in order to call entities function on its entities"); 1414 1415 if (!system.m_any_system_caller) 1416 return; 1417 1418 foreach (info; system.m_any_system_caller.infos) 1419 { 1420 CallData data = CallData(system.id, system, info, cast(void delegate()) func); 1421 data.update(); 1422 } 1423 } 1424 1425 /************************************************************************************************************************ 1426 Same as "void update(int pass = 0)" but use pass name instead of id. 1427 */ 1428 export void update(const(char)[] pass_name) nothrow @nogc 1429 { 1430 ushort pass = passes_map.get(pass_name, ushort.max); 1431 assert(pass != ushort.max); 1432 update(pass); 1433 } 1434 1435 /************************************************************************************************************************ 1436 Update systems. Should be called only between begin() and end(). 1437 */ 1438 export void update(ushort pass = 0) nothrow @nogc 1439 { 1440 assert(!register_state); 1441 assert(pass < passes.length); 1442 foreach (caller; passes[pass].system_callers) 1443 { 1444 System* sys = &systems[caller.system_id]; 1445 if (sys.enabled && sys.willExecute) 1446 { 1447 if (sys.m_empty) 1448 { 1449 CallData data = CallData(caller.system_id, sys, null, sys.m_update_delegate); 1450 data.update(); 1451 } 1452 else 1453 foreach (info; caller.infos) 1454 { 1455 CallData data = CallData(caller.system_id, sys, info, 1456 sys.m_update_delegate); 1457 data.update(); 1458 } 1459 } 1460 } 1461 } 1462 1463 /************************************************************************************************************************ 1464 Same as "void updateMT(int pass = 0)" but use pass name instead of id. 1465 */ 1466 export void updateMT(const(char)[] pass_name) nothrow @nogc 1467 { 1468 ushort pass = passes_map.get(pass_name, ushort.max); 1469 assert(pass != ushort.max); 1470 updateMT(pass); 1471 } 1472 1473 export void updateMT(ushort pass = 0) nothrow @nogc 1474 { 1475 assert(!register_state); 1476 assert(pass < passes.length); 1477 assert(m_dispatch_jobs, 1478 "Can't update with multithreading without JobDispatch function. Please use setJobDispatchFunc()."); 1479 Vector!CallData tmp_datas; 1480 tmp_datas.reserve(8); 1481 1482 foreach (caller; passes[pass].system_callers) 1483 { 1484 System* sys = &systems[caller.system_id]; 1485 if (sys.enabled && sys.willExecute) 1486 { 1487 uint job_id = 0; 1488 void nextJob() 1489 { 1490 CallData[] callers = m_call_data_allocator.getCallData( 1491 cast(uint) tmp_datas.length); 1492 //callers[0 .. $] = tmp_datas[0 .. $]; 1493 memcpy(callers.ptr, &tmp_datas[0], CallData.sizeof * tmp_datas.length); 1494 tmp_datas.clear(); 1495 sys.jobs[job_id].callers = callers; 1496 sys.jobs[job_id].id = job_id; 1497 job_id++; 1498 } 1499 1500 if (sys.m_empty) 1501 { 1502 tmp_datas.add(CallData(caller.system_id, sys, null, sys.m_update_delegate)); 1503 nextJob(); 1504 caller.job_group.jobs = sys.jobs[0 .. 1]; 1505 (cast(void delegate(JobGroup) nothrow @nogc) m_dispatch_jobs)(caller.job_group); 1506 continue; 1507 } 1508 uint entities_count = 0; 1509 foreach (info; caller.infos) 1510 { 1511 uint blocks_count = info.nonEmptyBlocksCount(); 1512 if (blocks_count == 0) 1513 continue; 1514 if (blocks_count > 1) 1515 entities_count += (blocks_count - 1) * info.max_entities; 1516 entities_count += info.last_block.entities_count; 1517 } 1518 1519 if (!entities_count) 1520 continue; 1521 1522 uint jobs_count = cast(uint) sys.jobs.length; 1523 uint entities_per_job = entities_count / jobs_count + 1; 1524 1525 if (entities_per_job <= 4) 1526 { 1527 jobs_count = entities_count / 4; 1528 if (jobs_count == 0) 1529 jobs_count = 1; 1530 entities_per_job = entities_count / jobs_count + 1; 1531 } 1532 1533 entities_count = 0; 1534 1535 foreach (info; caller.infos) 1536 { 1537 uint blocks_count = info.nonEmptyBlocksCount(); 1538 EntitiesBlock* first_block = info.first_block; 1539 uint first_elem = 0; 1540 begin: 1541 if (first_block is null || blocks_count == 0) 1542 continue; 1543 1544 //if this info will fill job 1545 if ((blocks_count - 1) * info.max_entities + entities_count 1546 + info.last_block.entities_count - first_elem >= entities_per_job) 1547 { 1548 int reamaining_entities = (entities_per_job - entities_count - ( 1549 first_block.entities_count - first_elem)); 1550 1551 //if first block don't fill job 1552 if (reamaining_entities > 0) 1553 { 1554 //take as many full blocks as possible 1555 int full_blocks_count = reamaining_entities / info.max_entities; 1556 EntitiesBlock* block = first_block; 1557 foreach (i; 0 .. full_blocks_count + 1) 1558 block = block.next_block; 1559 1560 //if full block + actual contained entities + remaining entities form first block > entities count per job 1561 if (full_blocks_count * info.max_entities + entities_count + ( 1562 first_block.entities_count - first_elem) >= entities_per_job) 1563 { 1564 assert(entities_per_job == full_blocks_count * info.max_entities + entities_count + ( 1565 first_block.entities_count - first_elem)); 1566 CallData data = CallData(caller.system_id, sys, 1567 info, sys.m_update_delegate, first_block, 1568 cast(ushort)(full_blocks_count + 1), 1569 cast(ushort) first_elem, 0); 1570 tmp_datas.add(data); 1571 first_elem = 0; 1572 blocks_count -= full_blocks_count + 1; 1573 first_block = block; 1574 } 1575 else 1576 { 1577 entities_count += full_blocks_count * info.max_entities + ( 1578 first_block.entities_count - first_elem); // - first_elem; 1579 uint last_elem = entities_per_job - entities_count; // + first_elem - 1; 1580 assert(last_elem > 0); 1581 assert(last_elem <= block.entities_count); 1582 CallData data = CallData(caller.system_id, sys, 1583 info, sys.m_update_delegate, first_block, 1584 cast(ushort)(full_blocks_count + 2), 1585 cast(ushort) first_elem, cast(ushort) last_elem); 1586 tmp_datas.add(data); 1587 first_elem = last_elem; 1588 blocks_count -= full_blocks_count + 1; 1589 first_block = block; 1590 if (last_elem == block.entities_count) 1591 { 1592 assert(block.next_block == null); 1593 first_block = null; 1594 } 1595 } 1596 } 1597 else 1598 { 1599 uint last_elem = entities_per_job - entities_count; 1600 assert(last_elem > 0); 1601 CallData data = CallData(caller.system_id, sys, 1602 info, sys.m_update_delegate, first_block, 1, 1603 cast(ushort) first_elem, cast(ushort)(first_elem + last_elem)); 1604 tmp_datas.add(data); 1605 first_elem += last_elem; 1606 assert(first_elem <= first_block.entities_count); 1607 //if job takes every entity, take next block 1608 if (first_elem == first_block.entities_count) 1609 { 1610 first_elem = 0; 1611 first_block = first_block.next_block; 1612 blocks_count--; 1613 } 1614 } 1615 nextJob(); 1616 entities_count = 0; 1617 goto begin; 1618 } 1619 else 1620 { 1621 //take whole info blocks 1622 CallData data = CallData(caller.system_id, sys, info, sys.m_update_delegate, 1623 first_block, cast(ushort) blocks_count, cast(ushort) first_elem); 1624 tmp_datas.add(data); 1625 entities_count += (blocks_count - 1) * info.max_entities 1626 + info.last_block.entities_count - first_elem; 1627 } 1628 } 1629 nextJob(); 1630 1631 caller.job_group.jobs = sys.jobs[0 .. job_id]; 1632 (cast(void delegate(JobGroup) nothrow @nogc) m_dispatch_jobs)(caller.job_group); //sys.jobs[0 .. job_id]); 1633 } 1634 } 1635 } 1636 1637 export void setMultithreadingCallbacks(void delegate(JobGroup) dispatch_callback, 1638 uint delegate() get_id_callback) 1639 { 1640 m_dispatch_jobs = cast(void delegate(JobGroup jobs) nothrow @nogc) dispatch_callback; 1641 m_thread_id_func = cast(uint delegate() nothrow @nogc) get_id_callback; 1642 } 1643 1644 /************************************************************************************************************************ 1645 Return size of single page (block). Every entity data block has size of page. 1646 */ 1647 uint pageSize() 1648 { 1649 return m_page_size; 1650 } 1651 1652 /************************************************************************************************************************ 1653 Return number of pages in single block allocation. Library allocate defined number of pages at once and assign it's 1654 for entities. 1655 */ 1656 uint pagesInBlock() 1657 { 1658 return m_pages_in_block; 1659 } 1660 1661 static void alignNum(ref ushort num, ushort alignment) nothrow @nogc pure 1662 { 1663 num = cast(ushort)((num + alignment - 1) & (-cast(int) alignment)); //num += alignment - (num & (alignment - 1)); 1664 } 1665 1666 extern (C) static int compareUShorts(const void* a, const void* b) nothrow @nogc 1667 { 1668 ushort _a = *cast(ushort*) a; 1669 ushort _b = *cast(ushort*) b; 1670 if (_a < _b) 1671 return -1; 1672 else if (_a == _b) 1673 return 0; 1674 else 1675 return 1; 1676 } 1677 1678 /************************************************************************************************************************ 1679 Allocate EntityTemplate with all components from entity witch it's data and returns pointer to it. 1680 1681 Params: 1682 entity_id = ID of entity from which should be created template 1683 fill_default = if true, components will be filled with default data, instead entity data will be taken 1684 */ 1685 export EntityTemplate* allocateTemplate(EntityID entity_id, bool fill_default = false) 1686 { 1687 Entity* entity = getEntity(entity_id); 1688 EntitiesBlock* block = getMetaData(entity); 1689 EntityInfo* info = block.type_info; 1690 1691 EntityTemplate* temp = Mallocator.make!EntityTemplate; 1692 temp.entity_data = Mallocator.makeArray!ubyte(info.size); 1693 temp.info = info; 1694 1695 if (fill_default) 1696 { 1697 //fill components with default data 1698 foreach (comp; info.components) 1699 { 1700 if (components[comp].size == 0) 1701 continue; 1702 memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], 1703 components[comp].init_data.ptr, components[comp].size); 1704 } 1705 } 1706 else 1707 { 1708 ushort index = block.entityIndex(entity); 1709 foreach (comp; info.components) 1710 { 1711 if (components[comp].size == 0) 1712 continue; 1713 memcpy(cast(void*) temp.entity_data.ptr + info.tmpl_deltas[comp], 1714 cast(void*) block + info.deltas[comp] + components[comp].size * index, 1715 components[comp].size); 1716 } 1717 } 1718 1719 return temp; 1720 } 1721 1722 /************************************************************************************************************************ 1723 Allocate EntityTemplate with specifed components and returns pointer to it. 1724 1725 Params: 1726 components_ids = array of components allocated with template 1727 */ 1728 export EntityTemplate* allocateTemplate(ushort[] components_ids) 1729 { 1730 1731 ushort[] ids = (cast(ushort*) alloca(ushort.sizeof * components_ids.length))[0 1732 .. components_ids.length]; 1733 memcpy(ids.ptr, components_ids.ptr, ushort.sizeof * components_ids.length); 1734 //ids[0 .. $] = components_ids[]; 1735 qsort(ids.ptr, ids.length, ushort.sizeof, &compareUShorts); 1736 { 1737 uint j = 1; 1738 foreach (i; 1 .. ids.length) 1739 { 1740 assert(ids[i] != ushort.max); 1741 if (ids[i] != ids[j - 1]) 1742 { 1743 ids[j] = ids[i]; 1744 j++; 1745 } 1746 //else 1747 // debug assert(0, "Duplicated components in template!!!"); 1748 } 1749 ids = ids[0 .. j]; 1750 } 1751 1752 EntityInfo* info = getEntityInfo(ids); 1753 1754 EntityTemplate* temp = Mallocator.make!EntityTemplate; 1755 temp.entity_data = Mallocator.makeArray!ubyte(info.size); 1756 temp.info = info; 1757 1758 //fill components with default data 1759 foreach (comp; info.components) 1760 { 1761 if (components[comp].size == 0) 1762 continue; 1763 memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], 1764 components[comp].init_data.ptr, components[comp].size); 1765 } 1766 1767 return temp; 1768 } 1769 1770 /************************************************************************************************************************ 1771 Allocate EntityTemplate from basic Template with modifications by adding and removing some components and returns pointer to it. 1772 Arrays of components needen't to be checked for repeated components, as function itself check if components exist in base template. 1773 1774 Params: 1775 base_tmpl = template from which components sould be copied 1776 components_ids = array of new components to add 1777 remove_components_ids = array of components to remove from base template 1778 */ 1779 export EntityTemplate* allocateTemplate(EntityTemplate* base_tmpl, 1780 ushort[] components_ids, ushort[] remove_components_ids = null) 1781 { 1782 size_t len = base_tmpl.info.components.length + components_ids.length; 1783 ushort[] ids = (cast(ushort*) alloca(ushort.sizeof * len))[0 .. len]; 1784 memcpy(ids.ptr, base_tmpl.info.components.ptr, 1785 ushort.sizeof * base_tmpl.info.components.length); 1786 memcpy(ids.ptr + base_tmpl.info.components.length, components_ids.ptr, 1787 ushort.sizeof * components_ids.length); 1788 1789 qsort(ids.ptr, ids.length, ushort.sizeof, &compareUShorts); 1790 qsort(remove_components_ids.ptr, remove_components_ids.length, 1791 ushort.sizeof, &compareUShorts); 1792 { 1793 uint k = 0; 1794 uint j = 1; 1795 foreach (i; 1 .. ids.length) 1796 { 1797 assert(ids[i] != ushort.max); 1798 if (k < remove_components_ids.length) 1799 { 1800 while (k < remove_components_ids.length && remove_components_ids[k] < ids[i]) 1801 { 1802 k++; 1803 } 1804 if (k < remove_components_ids.length) 1805 { 1806 if (remove_components_ids[k] == ids[i]) 1807 continue; 1808 } 1809 } 1810 if (ids[i] != ids[j - 1]) 1811 { 1812 ids[j] = ids[i]; 1813 j++; 1814 } 1815 //else 1816 // debug assert(0, "Duplicated components in template!!!"); 1817 } 1818 ids = ids[0 .. j]; 1819 } 1820 1821 EntityInfo* info = getEntityInfo(ids); 1822 1823 EntityTemplate* temp = Mallocator.make!EntityTemplate; 1824 temp.entity_data = Mallocator.makeArray!ubyte(info.size); 1825 temp.info = info; 1826 1827 //fill components with default data and copy from base template 1828 foreach (comp; info.components) 1829 { 1830 if (comp < base_tmpl.info.tmpl_deltas.length 1831 && base_tmpl.info.tmpl_deltas[comp] != ushort.max) //copy data from base component 1832 { 1833 if (components[comp].size == 0) 1834 continue; 1835 memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], 1836 base_tmpl.entity_data.ptr + base_tmpl.info.tmpl_deltas[comp], 1837 components[comp].size); 1838 } 1839 else //fill with default data 1840 { 1841 if (components[comp].size == 0) 1842 continue; 1843 memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], 1844 components[comp].init_data.ptr, components[comp].size); 1845 } 1846 } 1847 1848 return temp; 1849 } 1850 1851 /************************************************************************************************************************ 1852 Allocate EntityTemplate copy. 1853 1854 Params: 1855 copy_tmpl = template which should be copied 1856 */ 1857 export EntityTemplate* allocateTemplate(EntityTemplate* copy_tmpl) 1858 { 1859 assert(copy_tmpl, "copy_tmpl can't be null"); 1860 EntityTemplate* tmpl = Mallocator.make!EntityTemplate; 1861 tmpl.info = copy_tmpl.info; 1862 tmpl.entity_data = Mallocator.makeArray(copy_tmpl.entity_data); 1863 return tmpl; 1864 } 1865 1866 /************************************************************************************************************************ 1867 Returns entity type info. 1868 1869 Params: 1870 ids = array of components 1871 */ 1872 export EntityInfo* getEntityInfo(ushort[] ids) 1873 { 1874 if(ids.length == 0)ids = null; 1875 EntityInfo* info = entities_infos.get(ids, null); 1876 if (info is null) 1877 { 1878 info = Mallocator.make!EntityInfo; 1879 1880 info.size = EntityID.sizeof; 1881 info.alignment = EntityID.alignof; 1882 1883 if(ids is null) 1884 { 1885 uint block_memory = cast(uint)( 1886 m_page_size - EntitiesBlock.sizeof - info.size); 1887 uint entites_in_block = block_memory / info.size; 1888 info.max_entities = cast(ushort) entites_in_block; 1889 } 1890 else 1891 { 1892 uint components_size = EntityID.sizeof; 1893 1894 info.components = Mallocator.makeArray(ids); 1895 info.deltas = Mallocator.makeArray!ushort(ids[$ - 1] + 1); 1896 info.tmpl_deltas = Mallocator.makeArray!ushort(ids[$ - 1] + 1, ushort.max); 1897 1898 foreach (i, id; ids) 1899 { 1900 info.alignment = max(info.alignment, components[id].alignment); 1901 alignNum(info.size, components[id].alignment); 1902 info.tmpl_deltas[id] = info.size; 1903 info.size += components[id].size; 1904 components_size += components[id].size; 1905 } 1906 alignNum(info.size, info.alignment); 1907 1908 uint block_memory = cast(uint)( 1909 m_page_size - EntitiesBlock.sizeof - (info.size - components_size)); 1910 //uint entity_comps_size = EntityID.sizeof; 1911 uint mem_begin = EntitiesBlock.sizeof; 1912 1913 uint entites_in_block = block_memory / info.size; //entity_comps_size; 1914 info.max_entities = cast(ushort) entites_in_block; 1915 ushort current_delta = cast(ushort)(mem_begin + entites_in_block * EntityID.sizeof); 1916 1917 foreach (i, id; ids) 1918 { 1919 if (current_delta == 0) 1920 current_delta = ushort.max; 1921 alignNum(current_delta, components[id].alignment); 1922 info.deltas[id] = cast(ushort) current_delta; 1923 current_delta += entites_in_block * components[id].size; 1924 } 1925 1926 info.comp_add_info = Mallocator.makeArray!(EntityInfo*)(info.deltas.length); 1927 info.comp_rem_info = Mallocator.makeArray!(EntityInfo*)(info.deltas.length); 1928 1929 foreach (comp; info.components) 1930 { 1931 info.comp_add_info[comp] = info; 1932 info.comp_rem_info[comp] = null; 1933 } 1934 } 1935 1936 info.systems = Mallocator.makeArray!bool(systems.length); 1937 1938 foreach (i, ref system; systems) 1939 { 1940 if (system.isAlive() == false) 1941 continue; 1942 if (system.m_empty) 1943 continue; 1944 if (system.m_update is null) 1945 { 1946 if (system.m_add_entity || system.m_remove_entity 1947 || system.m_change_entity || system.m_event_callers.length) 1948 connectListenerToEntityInfo(*info, cast(uint) i); 1949 continue; 1950 } 1951 addSystemCaller(*info, cast(uint) i); 1952 } 1953 1954 entities_infos.add(info.components, info); 1955 1956 generateListeners(info); 1957 } 1958 return info; 1959 } 1960 1961 private void generateListeners(EntityInfo* info) nothrow 1962 { 1963 if (info.add_listeners) 1964 { 1965 Mallocator.dispose(info.add_listeners); 1966 info.add_listeners = null; 1967 } 1968 if (info.remove_listeners) 1969 { 1970 Mallocator.dispose(info.remove_listeners); 1971 info.remove_listeners = null; 1972 } 1973 if (info.change_listeners) 1974 { 1975 Mallocator.dispose(info.change_listeners); 1976 info.change_listeners = null; 1977 } 1978 //allocate local data 1979 ushort[] tmp_add = (cast(ushort*) alloca(systems.length * ushort.sizeof))[0 1980 .. systems.length]; 1981 ushort[] tmp_rem = (cast(ushort*) alloca(systems.length * ushort.sizeof))[0 1982 .. systems.length]; 1983 ushort[] tmp_ch = (cast(ushort*) alloca(systems.length * ushort.sizeof))[0 .. systems 1984 .length]; 1985 int add_len = 0; 1986 int rem_len = 0; 1987 int ch_len = 0; 1988 //assign listeners to lists 1989 foreach (i; 0 .. systems.length) 1990 { 1991 if (info.systems[i]) 1992 { 1993 System* system = &systems[i]; 1994 //onAddEntity listener 1995 if (system.m_add_entity) 1996 { 1997 //find listener position by priority 1998 int j; 1999 for (j = 0; j < add_len; j++) 2000 { 2001 if (systems[i].priority < systems[tmp_add[j]].priority) 2002 break; 2003 } 2004 add_len++; 2005 //move elements after new listener 2006 if (add_len < tmp_add.length) 2007 for (int k = add_len; k > j; k--) 2008 { 2009 tmp_add[k] = tmp_add[k - 1]; 2010 } 2011 //assign listener 2012 tmp_add[j] = cast(ushort) i; 2013 } 2014 //onRemoveEntity listener 2015 if (system.m_remove_entity) 2016 { 2017 //find listener position by priority 2018 int j; 2019 for (j = 0; j < rem_len; j++) 2020 { 2021 if (systems[i].priority < systems[tmp_rem[j]].priority) 2022 break; 2023 } 2024 rem_len++; 2025 //move elements after new listener 2026 if (rem_len < tmp_add.length) 2027 for (int k = rem_len; k > j; k--) 2028 { 2029 tmp_rem[k] = tmp_rem[k - 1]; 2030 } 2031 //assign listener 2032 tmp_rem[j] = cast(ushort) i; 2033 } 2034 //onChangeEntity listener 2035 if (system.m_change_entity) 2036 { 2037 //find listener position by priority 2038 int j; 2039 for (j = 0; j < ch_len; j++) 2040 { 2041 if (systems[i].priority < systems[tmp_ch[j]].priority) 2042 break; 2043 } 2044 ch_len++; 2045 //move elements after new listener 2046 if (ch_len < tmp_add.length) 2047 for (int k = ch_len; k > j; k--) 2048 { 2049 tmp_ch[k] = tmp_ch[k - 1]; 2050 } 2051 //assign listener 2052 tmp_ch[j] = cast(ushort) i; 2053 } 2054 } 2055 } 2056 2057 if (add_len) 2058 { 2059 info.add_listeners = Mallocator.makeArray!ushort(add_len); 2060 memcpy(info.add_listeners.ptr, tmp_add.ptr, add_len * ushort.sizeof); 2061 } 2062 2063 if (rem_len) 2064 { 2065 info.remove_listeners = Mallocator.makeArray!ushort(rem_len); 2066 memcpy(info.remove_listeners.ptr, tmp_rem.ptr, rem_len * ushort.sizeof); 2067 } 2068 2069 if (ch_len) 2070 { 2071 info.change_listeners = Mallocator.makeArray!ushort(ch_len); 2072 memcpy(info.change_listeners.ptr, tmp_ch.ptr, ch_len * ushort.sizeof); 2073 } 2074 } 2075 2076 export void connectListenerToEntityInfo(ref EntityInfo entity, uint system_id) nothrow @nogc 2077 { 2078 System* system = &systems[system_id]; 2079 2080 if (system.m_excluded_components) 2081 { 2082 foreach (id; system.m_excluded_components) 2083 { 2084 foreach (id2; entity.components) 2085 { 2086 if (id == id2) 2087 return; 2088 } 2089 } 2090 } 2091 2092 foreach (id; system.m_components) 2093 { 2094 foreach (i2, id2; entity.components) 2095 { 2096 if (id2 == id) 2097 goto is_; 2098 } 2099 return; 2100 is_: 2101 } 2102 2103 ///call Custom Entity Filter test if function exists 2104 if (system.m_filter_entity && !(cast(bool function(void* system_pointer, EntityInfo* info) @nogc nothrow) system 2105 .m_filter_entity)(system, &entity)) 2106 return; 2107 2108 entity.systems[system_id] = true; 2109 } 2110 2111 export void addSystemCaller(uint system_id) nothrow @nogc 2112 { 2113 System* system = &systems[system_id]; 2114 2115 uint index = 0; 2116 for (; index < passes[system.m_pass].system_callers.length; index++) 2117 { 2118 if (passes[system.m_pass].system_callers[index].system_id == system_id) 2119 return; 2120 } 2121 2122 bool added = false; 2123 foreach (i, caller; passes[system.m_pass].system_callers) 2124 { 2125 if (systems[caller.system_id].priority > system.priority) 2126 { 2127 SystemCaller* sys_caller = Mallocator.make!SystemCaller; 2128 sys_caller.system_id = system.id; 2129 sys_caller.job_group.caller = sys_caller; 2130 system.m_any_system_caller = sys_caller; 2131 passes[system.m_pass].system_callers.add(sys_caller, i); 2132 added = true; 2133 break; 2134 } 2135 } 2136 if (!added) 2137 { 2138 SystemCaller* sys_caller = Mallocator.make!SystemCaller; 2139 sys_caller.system_id = system.id; 2140 sys_caller.job_group.caller = sys_caller; 2141 system.m_any_system_caller = sys_caller; 2142 passes[system.m_pass].system_callers.add(sys_caller); 2143 } 2144 } 2145 2146 export void addSystemCaller(ref EntityInfo info, uint system_id) nothrow @nogc 2147 { 2148 System* system = &systems[system_id]; 2149 2150 connectListenerToEntityInfo(info, system_id); 2151 if (!info.systems[system_id]) 2152 return; 2153 2154 uint index = 0; 2155 for (; index < passes[system.m_pass].system_callers.length; index++) 2156 { 2157 if (passes[system.m_pass].system_callers[index].system_id == system_id) 2158 break; 2159 } 2160 2161 if (index < passes[system.m_pass].system_callers.length) 2162 { 2163 passes[system.m_pass].system_callers[index].infos.add(&info); 2164 } 2165 2166 } 2167 2168 /************************************************************************************************************************ 2169 Returns pointer to entity. 2170 2171 Params: 2172 id = ID of entity 2173 */ 2174 export Entity* getEntity(EntityID id) nothrow @nogc 2175 { 2176 return cast(Entity*) id_manager.getEntityPointer(id); 2177 } 2178 2179 /************************************************************************************************************************ 2180 Remove components from entity by IDs. Components will be removed on end of frame. 2181 2182 Params: 2183 entity_id = ID of entity 2184 del_ids = array of components IDs 2185 */ 2186 export void removeComponents(EntityID entity_id, ushort[] del_ids) nothrow @nogc 2187 { 2188 ThreadData* data = &threads[threadID]; 2189 uint num = cast(uint) del_ids.length; 2190 data.changeEntitiesList.add(0); 2191 data.changeEntitiesList.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]); 2192 data.changeEntitiesList.add((cast(ubyte*)&num)[0 .. uint.sizeof]); 2193 data.changeEntitiesList.add((cast(ubyte*) del_ids.ptr)[0 .. num * 2]); 2194 } 2195 2196 private void __removeComponents(EntityID entity_id, ushort[] del_ids) 2197 { 2198 Entity* entity = id_manager.getEntityPointer(entity_id); 2199 if (!entity) 2200 return; 2201 EntitiesBlock* block = getMetaData(entity); 2202 EntityInfo* info = block.type_info; 2203 2204 //remove non-existing components 2205 uint num = cast(uint) del_ids.length; 2206 foreach_reverse (i; 0 .. num) 2207 { 2208 if (info.deltas.length <= del_ids[i] || info.deltas[del_ids[i]] == 0) 2209 { 2210 num--; 2211 del_ids[i] = del_ids[num]; 2212 } 2213 } 2214 2215 if (num == 0) 2216 return; 2217 del_ids = del_ids[0 .. num]; 2218 2219 //sort components 2220 qsort(del_ids.ptr, del_ids.length, ushort.sizeof, &compareUShorts); 2221 2222 EntityInfo* new_info = info; 2223 2224 foreach (id; del_ids) 2225 { 2226 new_info = new_info.getNewInfoRemove(id); 2227 } 2228 2229 /*if (new_info == info) 2230 return;*/ 2231 2232 EntitiesBlock* new_block = findBlockWithFreeSpace(new_info); 2233 updateEntityInfoBlocks(new_info); 2234 assert(new_block.added_count == 0); 2235 2236 void* start = new_block.dataBegin() + new_block.entities_count * EntityID.sizeof; 2237 2238 Entity* new_entity = cast(Entity*) start; 2239 new_entity.id = entity.id; 2240 id_manager.update(*new_entity); 2241 2242 uint ind = block.entityIndex(entity); 2243 2244 if (info.remove_listeners) 2245 { 2246 foreach (listener; info.remove_listeners) 2247 { 2248 if (!new_info.systems[listener]) 2249 { 2250 callRemoveEntityListener(&systems[listener], info, block, ind, ind + 1); 2251 } 2252 } 2253 } 2254 2255 foreach (comp; new_info.components) 2256 { 2257 uint comp_size = components[comp].size; 2258 if (comp_size == 0) 2259 continue; 2260 memcpy(cast(void*) new_block + new_info.deltas[comp] + new_block.entities_count * comp_size, 2261 cast(void*) block + info.deltas[comp] + ind * comp_size, comp_size); 2262 } 2263 2264 new_block.entities_count++; 2265 if (new_block != new_info.update_block) 2266 new_info.update_block = new_block; 2267 2268 if (new_info.add_listeners) 2269 { 2270 foreach (listener; new_info.add_listeners) 2271 { 2272 if (!info.systems[listener]) 2273 { 2274 callAddEntityListener(&systems[listener], new_info, new_block, 2275 new_block.entities_count - 1, new_block.entities_count); 2276 } 2277 } 2278 } 2279 2280 if (new_info.change_listeners) 2281 { 2282 foreach (listener; new_info.change_listeners) 2283 { 2284 if (info.systems[listener]) 2285 { 2286 callChangeEntityListener(&systems[listener], new_info, new_block, 2287 new_block.entities_count - 1, new_block.entities_count, del_ids); 2288 } 2289 } 2290 } 2291 2292 removeEntityNoID(entity, block); 2293 } 2294 2295 /************************************************************************************************************************ 2296 Remove coponents from entity. 2297 2298 Params: 2299 Components = components types to remove 2300 entity_id = ID of entity 2301 */ 2302 void removeComponents(Components...)(EntityID entity_id) 2303 { 2304 const uint num = Components.length; 2305 ushort[num] del_ids; 2306 static foreach (i, comp; Components) 2307 { 2308 del_ids[i] = becsID!comp; 2309 } 2310 2311 removeComponents(entity_id, del_ids); 2312 } 2313 2314 private void __addComponents(EntityID entity_id, ushort[] new_ids, void*[] data_pointers) 2315 { 2316 uint num = cast(uint) new_ids.length; 2317 Entity* entity = id_manager.getEntityPointer(entity_id); 2318 if (!entity) 2319 return; 2320 EntitiesBlock* block = getMetaData(entity); 2321 EntityInfo* info = block.type_info; 2322 2323 foreach_reverse (i; 0 .. num) 2324 { 2325 if (info.deltas.length > new_ids[i] && info.deltas[new_ids[i]] != 0) 2326 { 2327 num--; 2328 new_ids[i] = new_ids[num]; 2329 data_pointers[i] = data_pointers[num]; 2330 } 2331 } 2332 2333 if (num == 0) 2334 return; 2335 new_ids = new_ids[0 .. num]; 2336 2337 foreach (int i; 0 .. num) 2338 { 2339 ushort min = new_ids[i]; 2340 int pos = i; 2341 foreach (int j; i .. num) 2342 { 2343 if (new_ids[j] < min) 2344 { 2345 min = new_ids[j]; 2346 pos = j; 2347 } 2348 } 2349 if (pos != i) 2350 { 2351 ushort id = new_ids[i]; 2352 new_ids[i] = new_ids[pos]; 2353 new_ids[pos] = id; 2354 void* ptr = data_pointers[i]; 2355 data_pointers[i] = data_pointers[pos]; 2356 data_pointers[pos] = ptr; 2357 } 2358 } 2359 2360 EntityInfo* new_info = info; 2361 2362 foreach (id; new_ids) 2363 { 2364 new_info = new_info.getNewInfoAdd(id); 2365 } 2366 2367 assert(new_info != info); 2368 /*if (new_info == info) 2369 return;*/ 2370 2371 //EntityInfo* new_info = getEntityInfo(ids[0 .. len]); 2372 2373 EntitiesBlock* new_block = findBlockWithFreeSpace(new_info); 2374 updateEntityInfoBlocks(new_info); 2375 assert(new_block.added_count == 0); 2376 2377 void* start = new_block.dataBegin() + new_block.entities_count * EntityID.sizeof; 2378 2379 Entity* new_entity = cast(Entity*) start; 2380 new_entity.id = entity.id; 2381 id_manager.update(*new_entity); 2382 2383 uint j = 0; 2384 uint k = 0; 2385 uint ind = block.entityIndex(entity); 2386 2387 if (info.remove_listeners) 2388 { 2389 foreach (listener; info.remove_listeners) 2390 { 2391 if (!new_info.systems[listener]) 2392 { 2393 callRemoveEntityListener(&systems[listener], info, block, ind, ind + 1); 2394 } 2395 } 2396 } 2397 2398 foreach (id; new_info.components) //ids[0 .. len]) 2399 { 2400 uint size = components[id].size; 2401 void* dst = void; 2402 if (size != 0) 2403 dst = cast(void*) new_block + new_info.deltas[id] + (new_block.entities_count) 2404 * size; 2405 2406 if (k >= new_ids.length) 2407 { 2408 if (size != 0) 2409 memcpy(dst, cast(void*) block + info.deltas[id] + ind * size, size); 2410 j++; 2411 } 2412 else if (j >= info.components.length || id == new_ids[k]) 2413 { 2414 if (size != 0) 2415 memcpy(dst, data_pointers[k], size); 2416 k++; 2417 } 2418 else 2419 { 2420 assert(id != new_ids[0]); 2421 if (size != 0) 2422 memcpy(dst, cast(void*) block + info.deltas[id] + ind * size, size); 2423 j++; 2424 } 2425 } 2426 2427 new_block.entities_count++; 2428 if (new_block != new_info.update_block) 2429 new_info.update_block = new_block; 2430 2431 if (new_info.add_listeners) 2432 { 2433 foreach (listener; new_info.add_listeners) 2434 { 2435 if (!info.systems[listener]) 2436 { 2437 callAddEntityListener(&systems[listener], new_info, new_block, 2438 new_block.entities_count - 1, new_block.entities_count); 2439 } 2440 } 2441 } 2442 2443 if (new_info.change_listeners) 2444 { 2445 foreach (listener; new_info.change_listeners) 2446 { 2447 if (info.systems[listener]) 2448 { 2449 callChangeEntityListener(&systems[listener], new_info, new_block, 2450 new_block.entities_count - 1, new_block.entities_count, new_ids); 2451 } 2452 } 2453 } 2454 2455 removeEntityNoID(entity, block); 2456 } 2457 2458 /************************************************************************************************************************ 2459 Add components to entity. Components will be added on end of frame. 2460 2461 Params: 2462 entity_id = ID of entity to remove 2463 comps = components to add 2464 */ 2465 void addComponents(Components...)(const EntityID entity_id, Components comps) nothrow @nogc 2466 { 2467 const uint num = Components.length; 2468 2469 ComponentRef[num] _comps; 2470 static foreach (i, comp; comps) 2471 { 2472 _comps[i] = ComponentRef(&comp, becsID!(typeof(comp))); 2473 } 2474 addComponents(entity_id, _comps); 2475 2476 } 2477 2478 export void addComponents(const EntityID entity_id, ComponentRef[] comps) nothrow @nogc 2479 { 2480 uint num = cast(uint) comps.length; 2481 ThreadData* data = &threads[threadID]; 2482 data.changeEntitiesList.add(cast(ubyte) 1u); 2483 data.changeEntitiesList.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]); 2484 data.changeEntitiesList.add((cast(ubyte*)&num)[0 .. uint.sizeof]); 2485 foreach (ref_; comps) 2486 { 2487 data.changeEntitiesList.add((cast(ubyte*)&ref_.component_id)[0 .. ushort.sizeof]); 2488 } 2489 foreach (ref_; comps) 2490 { 2491 if (components[ref_.component_id].size != 0) 2492 data.changeEntitiesList.add( 2493 (cast(ubyte*) ref_.ptr)[0 .. components[ref_.component_id].size]); 2494 } 2495 } 2496 2497 /************************************************************************************************************************ 2498 Free template memory. 2499 2500 Params: 2501 template_ = pointer entity template allocated by EntityManager. 2502 */ 2503 export void freeTemplate(EntityTemplate* template_) @nogc nothrow 2504 { 2505 Mallocator.dispose(template_.entity_data); 2506 Mallocator.dispose(template_); 2507 } 2508 2509 /************************************************************************************************************************ 2510 Add copy of entity to system and returns pointer to it. Added copy has same data as copied entity. Returen pointer is 2511 valid only before one from commit(), begin() or end() will be called. To save entity to further use you should save ID 2512 instead of pointer. 2513 * 2514 *Params: 2515 *id = ID of entity to be copyied. 2516 */ 2517 export Entity* addEntityCopy(EntityID id) 2518 { 2519 Entity* entity = getEntity(id); 2520 EntitiesBlock* block = getMetaData(entity); 2521 EntityInfo* info = block.type_info; 2522 2523 ushort index = block.entityIndex(entity); 2524 2525 ushort new_index = 0; 2526 EntitiesBlock* new_block; 2527 do 2528 { 2529 new_block = findBlockWithFreeSpaceMT(info); 2530 new_index = new_block.added_count.atomicOp!"+="(1); 2531 } 2532 while (new_block.entities_count + new_index > info.max_entities); 2533 2534 ushort new_id = cast(ushort)(new_block.entities_count + new_index - 1); 2535 const void* data_begin = new_block.dataBegin(); 2536 const void* start = data_begin + EntityID.sizeof * new_id; 2537 2538 foreach (i, comp; info.components) 2539 { 2540 ushort size = components[comp].size; 2541 if (size != 0) 2542 memcpy(cast(void*) new_block + info.deltas[comp] + new_id * size, 2543 cast(void*) block + info.deltas[comp] + size * index, size); 2544 2545 if (components[comp].create_callback) 2546 { 2547 components[comp].create_callback( 2548 cast(void*) new_block + info.deltas[comp] + new_id * size); 2549 } 2550 } 2551 2552 if (new_index == 1 && info.update_block == new_block) 2553 threads[threadID].infosToUpdate.add(info); 2554 2555 Entity* new_entity = cast(Entity*) start; 2556 new_entity.id = id_manager.getNewID(); 2557 id_manager.update(*new_entity); 2558 2559 return new_entity; 2560 } 2561 2562 /************************************************************************************************************************ 2563 Add entity to system. Returen pointer is valid only before one from commit(), begin() or end() will be called. To save entity to further 2564 use you should save ID instead of pointer. 2565 2566 Params: 2567 tmpl = pointer entity template allocated by EntityManager. Can be null in which case empty entity would be added (entity without components) 2568 */ 2569 export Entity* addEntity(EntityTemplate* tmpl) 2570 { 2571 return addEntity(tmpl, null); 2572 } 2573 2574 /************************************************************************************************************************ 2575 Add entity to system. Returen pointer is valid only before one from commit(), begin() or end() will be called. To save entity to further 2576 use you should save ID instead of pointer. 2577 2578 Params: 2579 tmpl = pointer entity template allocated by EntityManager. Can be null in which case empty entity would be added (entity without components) 2580 replacement = list of components references to used. Memory form list replace data from template inside new entity. Should be used only for data which vary between most entities (like 3D position etc.) 2581 */ 2582 export Entity* addEntity(EntityTemplate* tmpl, ComponentRef[] replacement) 2583 { 2584 EntityInfo* info = void; 2585 if(tmpl)info = tmpl.info; 2586 else info = getEntityInfo(null); 2587 2588 ushort index = 0; 2589 EntitiesBlock* block; 2590 do 2591 { 2592 block = findBlockWithFreeSpaceMT(info); 2593 index = block.added_count.atomicOp!"+="(1); 2594 } 2595 while (block.entities_count + index > info.max_entities); 2596 2597 uint id = (block.entities_count + index - 1); //block.added_count); 2598 2599 void* data_begin = block.dataBegin(); 2600 void* start = data_begin + EntityID.sizeof * id; 2601 2602 foreach (comp; info.components) 2603 { 2604 uint size = components[comp].size; 2605 if (size != 0) 2606 memcpy(cast(void*) block + info.deltas[comp] + size * id, 2607 tmpl.entity_data.ptr + info.tmpl_deltas[comp], size); 2608 } 2609 2610 foreach (comp; replacement) 2611 { 2612 if (comp.component_id < info.deltas.length) 2613 { 2614 ushort delta = info.deltas[comp.component_id]; 2615 if (delta != 0) 2616 { 2617 uint size = components[comp.component_id].size; 2618 if (size != 0) 2619 memcpy(cast(void*) block + delta + size * id, comp.ptr, size); 2620 } 2621 } 2622 } 2623 2624 foreach (i, comp; info.components) 2625 { 2626 if (components[comp].create_callback) 2627 { 2628 components[comp].create_callback( 2629 cast(void*) block + info.deltas[comp] + id * components[comp].size); 2630 } 2631 } 2632 2633 if (index == 1 && info.update_block == block) 2634 threads[threadID].infosToUpdate.add(info); 2635 2636 Entity* entity = cast(Entity*) start; 2637 entity.id = id_manager.getNewID(); 2638 id_manager.update(*entity); 2639 2640 return entity; 2641 } 2642 2643 /************************************************************************************************************************ 2644 Return block with free space for selected EntityInfo. 2645 */ 2646 private EntitiesBlock* findBlockWithFreeSpace(EntityInfo* info) nothrow @nogc 2647 { 2648 EntitiesBlock* block = info.last_block; 2649 2650 if (block is null) 2651 { 2652 block = cast(EntitiesBlock*) allocator.getBlock(); 2653 *block = EntitiesBlock(info); 2654 block.id = 0; 2655 info.first_block = block; 2656 info.last_block = block; 2657 info.update_block = block; 2658 } 2659 else if (block.entities_count >= info.max_entities) 2660 { 2661 EntitiesBlock* new_block = cast(EntitiesBlock*) allocator.getBlock(); 2662 *new_block = EntitiesBlock(info); 2663 new_block.prev_block = block; 2664 block.next_block = new_block; 2665 new_block.id = cast(ushort)(block.id + 1); 2666 block = new_block; 2667 info.last_block = block; 2668 ///make sure that update_block point to unfilled block 2669 if (info.update_block.entities_count == info.max_entities) 2670 { 2671 assert(!info.update_block.added_count); 2672 info.update_block = block; 2673 } 2674 } 2675 return block; 2676 } 2677 2678 /************************************************************************************************************************ 2679 Return block with free space for selected EntityInfo. Additional this function is multithread safe. 2680 */ 2681 private EntitiesBlock* findBlockWithFreeSpaceMT(EntityInfo* info) 2682 { 2683 EntitiesBlock* block = info.last_block; 2684 2685 if (block is null) 2686 { 2687 entity_block_alloc_mutex.lock(); 2688 scope (exit) 2689 entity_block_alloc_mutex.unlock(); 2690 2691 if (info.last_block != null) 2692 return info.last_block; 2693 2694 block = cast(EntitiesBlock*) allocator.getBlock(); 2695 *block = EntitiesBlock(info); 2696 block.id = 0; 2697 info.first_block = block; 2698 info.last_block = block; 2699 info.update_block = block; 2700 } 2701 else if (block.entities_count + block.added_count >= info.max_entities) 2702 { 2703 EntitiesBlock* last_block = info.last_block; 2704 2705 entity_block_alloc_mutex.lock(); 2706 scope (exit) 2707 entity_block_alloc_mutex.unlock(); 2708 2709 if (info.last_block !is last_block) 2710 return info.last_block; 2711 2712 EntitiesBlock* new_block = cast(EntitiesBlock*) allocator.getBlock(); 2713 *new_block = EntitiesBlock(info); 2714 new_block.prev_block = block; 2715 block.next_block = new_block; 2716 new_block.id = cast(ushort)(block.id + 1); 2717 block = new_block; 2718 info.last_block = block; 2719 ///make sure that update_block point to unfilled block 2720 if (info.update_block.entities_count == info.max_entities) 2721 { 2722 assert(!info.update_block.added_count); 2723 info.update_block = block; 2724 } 2725 } 2726 return block; 2727 } 2728 2729 /************************************************************************************************************************ 2730 Remove entity by ID. Entity will be removed on frame end. 2731 2732 Params: 2733 id = id of entity to remove 2734 */ 2735 export void removeEntity(EntityID id) 2736 { 2737 threads[threadID].entitesToRemove.add(id); 2738 } 2739 2740 private void __removeEntity(EntityID id) nothrow @nogc 2741 { 2742 //get entity and block meta data pointers 2743 Entity* entity = id_manager.getEntityPointer(id); 2744 2745 if (entity is null) 2746 return; //return if entity doesn't exist 2747 2748 EntitiesBlock* block = getMetaData(entity); 2749 2750 EntityInfo* info = block.type_info; 2751 if (info.remove_listeners) 2752 { 2753 uint pos = block.entityIndex(entity); 2754 2755 callRemoveEntityListeners(info, block, pos, pos + 1); 2756 } 2757 2758 id_manager.releaseID(id); //release id from manager 2759 2760 removeEntityNoID(entity, block, true); 2761 } 2762 2763 private void removeEntityNoID(Entity* entity, EntitiesBlock* block, 2764 bool call_destructors = false) nothrow @nogc 2765 { 2766 EntityInfo* info = block.type_info; 2767 2768 updateEntityInfoBlocks(info); 2769 2770 assert(info.last_block.added_count == 0); 2771 assert(info.last_block.entities_count > 0); 2772 2773 info.last_block.entities_count--; 2774 2775 uint pos = block.entityIndex(entity); 2776 2777 if (call_destructors) 2778 { 2779 foreach (comp; info.components) 2780 { 2781 if (components[comp].destroy_callback) 2782 { 2783 components[comp].destroy_callback(cast( 2784 void*) block + info.deltas[comp] + pos * components[comp].size); 2785 } 2786 } 2787 } 2788 2789 if (block !is info.last_block || pos != block.entities_count) 2790 { 2791 foreach (comp; info.components) 2792 { 2793 uint size = components[comp].size; 2794 if (size == 0) 2795 continue; 2796 void* src = cast(void*) info.last_block + info.deltas[comp]; 2797 void* dst = cast(void*) block + info.deltas[comp]; 2798 memcpy(dst + pos * size, src + info.last_block.entities_count * size, size); 2799 } 2800 2801 block = info.last_block; 2802 entity.id = *cast(EntityID*)(block.dataBegin() + block.entities_count * EntityID.sizeof); 2803 2804 id_manager.update(*entity); 2805 } 2806 2807 block = info.last_block; 2808 if (block.entities_count == 0) 2809 { 2810 assert(info.update_block is block); 2811 info.last_block = block.prev_block; 2812 if (info.first_block is block) 2813 { 2814 info.first_block = null; 2815 } 2816 if (block.prev_block) 2817 { 2818 block.prev_block.next_block = null; 2819 info.update_block = block.prev_block; 2820 assert(block.prev_block.added_count == 0); 2821 //block.prev_block.added_count.atomicStore(cast(ushort)0); 2822 } 2823 allocator.freeBlock(block); 2824 } 2825 } 2826 2827 /************************************************************************************************************************ 2828 functions return MetaData of page. 2829 2830 Params: 2831 pointer = pointer to any data of entity (i.e. component data pointer) 2832 */ 2833 export EntitiesBlock* getMetaData(const void* pointer) nothrow @nogc 2834 { 2835 return cast(EntitiesBlock*)(cast(size_t) pointer & (~cast(size_t)(m_page_size - 1))); 2836 } 2837 2838 private bool changeEntities() 2839 { 2840 bool has_work = false; 2841 //foreach (ref ThreadData thread; threads)thread.swapToChange(); 2842 foreach (ref ThreadData thread; threads) 2843 { 2844 uint index = 0; 2845 uint len = cast(uint) thread.changeEntitiesListPrev.length; 2846 if (len) 2847 has_work = true; 2848 void*[32] pointers; // = (cast(void**) alloca(num * (void*).sizeof))[0 .. num]; 2849 while (index < len) 2850 { 2851 if (!thread.changeEntitiesListPrev[index++]) 2852 { 2853 EntityID id = *cast(EntityID*)&thread.changeEntitiesListPrev[index]; 2854 index += EntityID.sizeof; 2855 uint num = *cast(uint*)&thread.changeEntitiesListPrev[index]; 2856 index += uint.sizeof; 2857 ushort[] ids; // = (cast(ushort*) alloca(num * ushort.sizeof))[0 .. num]; 2858 ids = (cast(ushort*)&thread.changeEntitiesListPrev[index])[0 .. num]; 2859 index += ushort.sizeof * num; 2860 __removeComponents(id, ids); 2861 } 2862 else 2863 { 2864 EntityID id = *cast(EntityID*)&thread.changeEntitiesListPrev[index]; 2865 index += EntityID.sizeof; 2866 uint num = *cast(uint*)&thread.changeEntitiesListPrev[index]; 2867 index += uint.sizeof; 2868 ushort[] ids; // = (cast(ushort*) alloca(num * ushort.sizeof))[0 .. num]; 2869 ids = (cast(ushort*)&thread.changeEntitiesListPrev[index])[0 .. num]; 2870 index += ushort.sizeof * num; 2871 //void*[] pointers = (cast(void**) alloca(num * (void*).sizeof))[0 .. num]; 2872 foreach (i; 0 .. num) 2873 { 2874 pointers[i] = &thread.changeEntitiesListPrev[index]; 2875 index += components[ids[i]].size; 2876 } 2877 2878 __addComponents(id, ids, pointers[0 .. num]); 2879 } 2880 } 2881 thread.changeEntitiesListPrev.clear(); 2882 } 2883 return has_work; 2884 } 2885 2886 private void callAddEntityListeners(EntityInfo* info, EntitiesBlock* block, int begin, int end) @nogc nothrow 2887 { 2888 foreach (listener; info.add_listeners) 2889 { 2890 System* system = &systems[listener]; 2891 callAddEntityListener(system, info, block, begin, end); 2892 } 2893 } 2894 2895 private static void callAddEntityListener(System* system, EntityInfo* info, 2896 EntitiesBlock* block, int begin, int end) @nogc nothrow 2897 { 2898 ListenerCallData data; 2899 data.system = system; 2900 data.block = block; 2901 data.begin = begin; 2902 data.end = end; 2903 (cast(void function(ref ListenerCallData) nothrow @nogc) system.m_add_entity)(data); 2904 } 2905 2906 private void callRemoveEntityListeners(EntityInfo* info, EntitiesBlock* block, int begin, 2907 int end) @nogc nothrow 2908 { 2909 foreach (listener; info.remove_listeners) 2910 { 2911 System* system = &systems[listener]; 2912 callRemoveEntityListener(system, info, block, begin, end); 2913 } 2914 } 2915 2916 private static void callRemoveEntityListener(System* system, 2917 EntityInfo* info, EntitiesBlock* block, int begin, int end) @nogc nothrow 2918 { 2919 ListenerCallData data; 2920 data.system = system; 2921 data.block = block; 2922 data.begin = begin; 2923 data.end = end; 2924 (cast(void function(ref ListenerCallData) nothrow @nogc) system.m_remove_entity)(data); 2925 } 2926 2927 private void callChangeEntityListener(System* system, EntityInfo* info, 2928 EntitiesBlock* block, int begin, int end, ushort[] ch_ids) @nogc nothrow 2929 { 2930 int i = 0; 2931 int j = 0; 2932 bool is_ = false; 2933 while (1) 2934 { 2935 if (ch_ids[i] == system.m_optional_components[j]) 2936 { 2937 is_ = true; 2938 break; 2939 } 2940 else if (ch_ids[i] > system.m_optional_components[j]) 2941 { 2942 j++; 2943 if (j >= system.m_optional_components.length) 2944 break; 2945 } 2946 else 2947 { 2948 i++; 2949 if (i >= ch_ids.length) 2950 break; 2951 } 2952 } 2953 if (!is_) 2954 return; 2955 2956 ListenerCallData data; 2957 data.system = system; 2958 data.block = block; 2959 data.begin = begin; 2960 data.end = end; 2961 (cast(void function(ref ListenerCallData) nothrow @nogc) system.m_change_entity)(data); 2962 } 2963 2964 private void updateEntityInfoBlocks(EntityInfo* info) nothrow @nogc 2965 { 2966 while (info.last_block.added_count) 2967 { 2968 EntitiesBlock* block = info.update_block; 2969 assert(block !is null); 2970 if (block.entities_count == info.max_entities) 2971 { 2972 assert(!block.added_count); 2973 block = block.next_block; 2974 } 2975 assert(!block.prev_block || !block.prev_block.added_count); 2976 info.update_block = info.last_block; 2977 2978 while (block) 2979 { 2980 assert(block.added_count.atomicLoad() > 0); 2981 updateBlock(block); 2982 block = block.next_block; 2983 } 2984 } 2985 assert(info.last_block is info.update_block); 2986 } 2987 2988 private void updateBlock(EntitiesBlock* block) @nogc nothrow 2989 { 2990 //if(block.added_count == 0)return; 2991 assert(block.added_count != 0); 2992 EntityInfo* info = block.type_info; 2993 ushort entities_count = block.entities_count; 2994 block.entities_count += block.added_count; 2995 if (block.entities_count > block.type_info.max_entities) 2996 { 2997 block.entities_count = block.type_info.max_entities; 2998 } 2999 block.added_count.atomicStore(cast(ushort) 0); 3000 3001 if (info.add_listeners) 3002 { 3003 callAddEntityListeners(info, block, entities_count, block.entities_count); 3004 } 3005 } 3006 3007 private bool updateBlocks() 3008 { 3009 bool has_work = false; 3010 foreach (ref ThreadData thread; threads) 3011 { 3012 if (thread.infosToUpdatePrev.length) 3013 has_work = true; 3014 foreach (info; thread.infosToUpdatePrev) 3015 { 3016 updateEntityInfoBlocks(info); 3017 } 3018 thread.infosToUpdatePrev.clear(); 3019 } 3020 return has_work; 3021 } 3022 3023 private bool removeEntities() nothrow @nogc 3024 { 3025 bool has_work = false; 3026 //foreach (ref ThreadData thread; threads)thread.swapToRemove(); 3027 foreach (ref ThreadData thread; threads) 3028 { 3029 if (thread.entitiesToRemovePrev.length) 3030 has_work = true; 3031 foreach (id; thread.entitiesToRemovePrev) 3032 { 3033 __removeEntity(id); 3034 } 3035 thread.entitiesToRemovePrev.clear(); 3036 } 3037 return has_work; 3038 } 3039 3040 private bool updateEvents() nothrow @nogc 3041 { 3042 bool has_work = false; 3043 // bool empty = true; 3044 //while (1) 3045 //{ 3046 //event_manager.swapCurrent(); 3047 uint current_index; 3048 if (event_manager.current_index == 0) 3049 current_index = cast(uint) threads.length; 3050 else 3051 current_index = 0; 3052 foreach (i, event; event_manager.events) 3053 { 3054 foreach (first_block; event.first_blocks[current_index .. current_index + threads 3055 .length]) 3056 { 3057 EventManager.EventBlock* block = first_block; 3058 if (block) 3059 has_work = true; 3060 // { 3061 // has_work = true; 3062 // //empty = false; 3063 // } 3064 while (block) 3065 { 3066 EventCallData call_data; 3067 void* event_pointer = cast(void*) block + event.data_offset; 3068 foreach (j; 0 .. block.count) 3069 { 3070 call_data.event = event_pointer + EntityID.sizeof; 3071 EntityID entity_id = *cast(EntityID*)(event_pointer); 3072 Entity* entity = id_manager.getEntityPointer(entity_id); 3073 if (entity) 3074 { 3075 call_data.block = getMetaData(entity); 3076 call_data.id = call_data.block.entityIndex(entity); 3077 call_data.entity = entity; 3078 3079 foreach (caller; events[i].callers) 3080 { 3081 if (call_data.block.type_info.systems[caller.system.m_id] == false 3082 || !caller.system.enabled || !caller.system.willExecute) 3083 continue; 3084 call_data.system_pointer = caller.system.m_system_pointer; 3085 (cast(void function(ref EventCallData) nothrow @nogc) caller 3086 .callback)(call_data); 3087 } 3088 } 3089 if (events[i].destroy_callback) 3090 events[i].destroy_callback(event_pointer); 3091 event_pointer += events[i].size + EntityID.sizeof; 3092 } 3093 block = block.next; 3094 } 3095 } 3096 } 3097 // if (empty) 3098 // break; 3099 // empty = true; 3100 //} 3101 return has_work; 3102 } 3103 3104 private void swapData() nothrow @nogc 3105 { 3106 event_manager.swapCurrent(); 3107 foreach (ref ThreadData thread; threads) 3108 { 3109 thread.swapData(); 3110 } 3111 } 3112 3113 export void commit() 3114 { 3115 bool has_work = true; 3116 while (has_work) 3117 { 3118 swapData(); 3119 3120 has_work = false; 3121 has_work |= updateBlocks(); 3122 // has_work |= changeEntities(); 3123 // has_work |= removeEntities(); 3124 has_work |= updateEvents(); 3125 3126 id_manager.optimize(); 3127 has_work |= updateBlocks(); 3128 has_work |= changeEntities(); 3129 has_work |= removeEntities(); 3130 } 3131 event_manager.clearEvents(); 3132 } 3133 3134 /************************************************************************************************************************ 3135 Begin of update process. Should be called before any update is called. 3136 */ 3137 export void begin() 3138 { 3139 3140 commit(); 3141 m_call_data_allocator.clear(); 3142 3143 foreach (ref system; systems) 3144 { 3145 if (system.isAlive() == false) 3146 continue; 3147 if (system.enabled && system.m_begin) 3148 system.m_execute = (cast(bool function(void*)) system.m_begin)( 3149 system.m_system_pointer); 3150 } 3151 } 3152 3153 /************************************************************************************************************************ 3154 End of update process. Should be called after every update function. 3155 */ 3156 export void end() 3157 { 3158 3159 foreach (ref system; systems) 3160 { 3161 if (system.isAlive() == false) 3162 continue; 3163 3164 if (system.enabled && system.m_end) 3165 (cast(void function(void*)) system.m_end)(system.m_system_pointer); 3166 } 3167 3168 commit(); 3169 } 3170 3171 /*private void getThreadID() nothrow @nogc 3172 { 3173 if (m_thread_id_func) 3174 thread_id = (cast(uint delegate() nothrow @nogc) m_thread_id_func)(); 3175 else 3176 thread_id = 0; 3177 }*/ 3178 3179 void sendEvent(Ev)(EntityID id, Ev event) nothrow @nogc 3180 { 3181 event_manager.sendEvent(id, event, threadID); 3182 } 3183 3184 private void generateDependencies() nothrow @nogc 3185 { 3186 foreach (pass_id, pass; passes) 3187 { 3188 foreach (caller; pass.system_callers) 3189 { 3190 caller.system = &systems[caller.system_id]; 3191 if (caller.exclusion) 3192 Mallocator.dispose(caller.exclusion); 3193 if (caller.dependencies) 3194 Mallocator.dispose(caller.dependencies); 3195 } 3196 uint index = 0; 3197 SystemCaller*[] exclusion; 3198 exclusion = (cast(SystemCaller**) alloca((SystemCaller*) 3199 .sizeof * pass.system_callers.length))[0 .. pass.system_callers.length]; 3200 foreach (caller; pass.system_callers) 3201 { 3202 index = 0; 3203 ///gets systems which are excluding each other 3204 out_for: foreach (caller2; pass.system_callers) 3205 { 3206 if (caller is caller2) 3207 continue; 3208 3209 ///check for external dependencies 3210 foreach (cmp; caller.system.m_readonly_dependencies) 3211 { 3212 foreach (cmp2; caller2.system.m_writable_dependencies) 3213 { 3214 if (cmp == cmp2) 3215 { 3216 exclusion[index++] = caller2; 3217 continue out_for; 3218 } 3219 } 3220 } 3221 foreach (cmp; caller.system.m_writable_dependencies) 3222 { 3223 foreach (cmp2; caller2.system.m_readonly_dependencies) 3224 { 3225 if (cmp == cmp2) 3226 { 3227 exclusion[index++] = caller2; 3228 continue out_for; 3229 } 3230 } 3231 foreach (cmp2; caller2.system.m_writable_dependencies) 3232 { 3233 if (cmp == cmp2) 3234 { 3235 exclusion[index++] = caller2; 3236 continue out_for; 3237 } 3238 } 3239 } 3240 3241 ///check for component dependencies 3242 foreach (cmp; caller.system.m_read_only_components) 3243 { 3244 foreach (cmp2; caller2.system.m_writable_components) 3245 { 3246 if (cmp == cmp2) 3247 { 3248 exclusion[index++] = caller2; 3249 continue out_for; 3250 } 3251 } 3252 } 3253 foreach (cmp; caller.system.m_writable_components) 3254 { 3255 foreach (cmp2; caller2.system.m_read_only_components) 3256 { 3257 if (cmp == cmp2) 3258 { 3259 exclusion[index++] = caller2; 3260 continue out_for; 3261 } 3262 } 3263 foreach (cmp2; caller2.system.m_writable_components) 3264 { 3265 if (cmp == cmp2) 3266 { 3267 exclusion[index++] = caller2; 3268 continue out_for; 3269 } 3270 } 3271 } 3272 } 3273 3274 if (index > 0) 3275 { 3276 caller.exclusion = Mallocator.makeArray(exclusion[0 .. index]); 3277 } 3278 else 3279 caller.exclusion = null; 3280 } 3281 3282 extern (C) static int compareSystems(const void* a, const void* b) 3283 { 3284 SystemCaller* _a = *cast(SystemCaller**) a; 3285 SystemCaller* _b = *cast(SystemCaller**) b; 3286 if (_a.system.priority < _b.system.priority) 3287 return -1; 3288 else if (_a.system.priority == _b.system.priority) 3289 { 3290 if (_a.exclusion.length < _b.exclusion.length) 3291 return -1; 3292 else if (_a.exclusion.length == _b.exclusion.length) 3293 return 0; 3294 else 3295 return 1; 3296 } 3297 else 3298 return 1; 3299 } 3300 3301 qsort(pass.system_callers.array.ptr, pass.system_callers.length, 3302 (SystemCaller*).sizeof, &compareSystems); 3303 3304 foreach (i, caller; pass.system_callers) 3305 caller.job_group.id = cast(uint) i; 3306 3307 int priority = int.min; 3308 uint beg = 0; 3309 index = 0; 3310 foreach (i, caller; pass.system_callers) 3311 { 3312 index = 0; 3313 foreach (ex; caller.exclusion) 3314 { 3315 if (ex.job_group.id > caller.job_group.id) 3316 continue; 3317 3318 exclusion[index++] = ex; 3319 } 3320 3321 if (index > 0) 3322 { 3323 caller.dependencies = Mallocator.makeArray(exclusion[0 .. index]); 3324 caller.job_group.dependencies = Mallocator.makeArray!(JobGroup*)(index); 3325 3326 foreach (j, dep; caller.dependencies) 3327 { 3328 caller.job_group.dependencies[j] = &dep.job_group; 3329 } 3330 } 3331 else 3332 caller.dependencies = null; 3333 } 3334 } 3335 } 3336 3337 const(UpdatePass)* getPass(const(char)[] name) 3338 { 3339 ushort id = getPassID(name); 3340 if (id == ushort.max) 3341 return null; 3342 return passes[id]; 3343 } 3344 3345 ushort getPassID(const(char)[] name) 3346 { 3347 return passes_map.get(name, ushort.max); 3348 } 3349 3350 /************************************************************************************************************************ 3351 Component info; 3352 */ 3353 struct ComponentInfo 3354 { 3355 export ~this() nothrow @nogc 3356 { 3357 } 3358 ///Component size 3359 ushort size; 3360 ///Component data alignment 3361 ushort alignment; 3362 ///Initialization data 3363 ubyte[] init_data; 3364 ///Pointer to component destroy callback 3365 void function(void* pointer) nothrow @nogc destroy_callback; 3366 //void* destroy_callback; 3367 ///Pointer to component create callback 3368 void function(void* pointer) nothrow @nogc create_callback; 3369 //void* create_callback; 3370 } 3371 3372 struct EventCaller 3373 { 3374 System* system; 3375 void* callback; 3376 } 3377 3378 struct EventCallData 3379 { 3380 EntitiesBlock* block; 3381 void* system_pointer; 3382 void* event; 3383 Entity* entity; 3384 ushort id; 3385 } 3386 3387 struct EventInfo 3388 { 3389 ushort size; 3390 ushort alignment; 3391 EventCaller[] callers; 3392 void function(void* pointer) nothrow @nogc destroy_callback; 3393 } 3394 3395 /************************************************************************************************************************ 3396 Entity type info. 3397 */ 3398 struct EntityInfo 3399 { 3400 ///Returns number of blocks 3401 uint blocksCount() nothrow @nogc 3402 { 3403 if (last_block) 3404 return last_block.id + 1; 3405 else 3406 return 0; 3407 } 3408 3409 ///Returns number of non empty blocks 3410 uint nonEmptyBlocksCount() nothrow @nogc 3411 { 3412 EntitiesBlock* block = last_block; 3413 while (1) 3414 { 3415 if (block is null) 3416 return 0; 3417 if (block.entities_count == 0) 3418 block = block.prev_block; 3419 else 3420 return block.id + 1; 3421 } 3422 } 3423 3424 EntityInfo* getNewInfoAdd(ushort id) 3425 { 3426 if (comp_add_info.length <= id) 3427 { 3428 EntityInfo*[] new_infos = Mallocator.makeArray!(EntityInfo*)( 3429 gEntityManager.components.length); 3430 if (comp_add_info !is null) 3431 { 3432 //new_infos[0 .. comp_add_info.length] = comp_add_info[0 .. $]; 3433 memcpy(new_infos.ptr, comp_add_info.ptr, (EntityInfo*) 3434 .sizeof * comp_add_info.length); 3435 Mallocator.dispose(comp_add_info); 3436 } 3437 comp_add_info = new_infos; 3438 } 3439 if (comp_add_info[id]) 3440 return comp_add_info[id]; 3441 3442 ushort[] ids = (cast(ushort*) alloca(ushort.sizeof * (components.length + 1)))[0 3443 .. components.length + 1]; 3444 uint len = 0; 3445 3446 foreach (comp; components) 3447 { 3448 if (id > comp) 3449 { 3450 ids[len++] = comp; 3451 } 3452 else 3453 { 3454 ids[len++] = id; 3455 ids[len++] = comp; 3456 foreach (comp2; components[len - 1 .. $]) 3457 { 3458 ids[len++] = comp2; 3459 } 3460 break; 3461 } 3462 } 3463 if (components.length == 0 || id > components[$ - 1]) 3464 ids[len++] = id; 3465 3466 assert(len == components.length + 1); 3467 3468 EntityInfo* new_info = gEntityManager.getEntityInfo(ids); 3469 3470 comp_add_info[id] = new_info; 3471 return new_info; 3472 } 3473 3474 EntityInfo* getNewInfoRemove(ushort id) return 3475 { 3476 /*if (comp_rem_info.length <= id) 3477 { 3478 EntityInfo*[] new_infos = Mallocator.makeArray!(EntityInfo*)( 3479 gEntityManager.components.length, &this); 3480 if (comp_rem_info !is null) 3481 { 3482 //new_infos[0 .. comp_rem_info.length] = comp_rem_info[0 .. $]; 3483 memcpy(new_infos.ptr, comp_rem_info.ptr, (EntityInfo*) 3484 .sizeof * comp_rem_info.length); 3485 Mallocator.dispose(comp_rem_info); 3486 } 3487 comp_rem_info = new_infos; 3488 }*/ 3489 if (comp_rem_info[id]) 3490 return comp_rem_info[id]; 3491 3492 ushort[] ids = (cast(ushort*) alloca(ushort.sizeof * (components.length - 1)))[0 3493 .. components.length - 1]; 3494 uint len = 0; 3495 3496 foreach (comp; components) 3497 { 3498 if (id != comp) 3499 { 3500 ids[len++] = comp; 3501 } 3502 } 3503 assert(len != components.length); 3504 //if (len == components.length) 3505 // return &this; 3506 3507 assert(len == components.length - 1); 3508 3509 EntityInfo* new_info = gEntityManager.getEntityInfo(ids[0 .. len]); 3510 3511 comp_rem_info[id] = new_info; 3512 return new_info; 3513 } 3514 3515 export bool hasComponent(ushort component_id) 3516 { 3517 if (component_id >= deltas.length || !deltas[component_id]) 3518 return false; 3519 return true; 3520 } 3521 3522 export ~this() @nogc nothrow 3523 { 3524 if (components) 3525 Mallocator.dispose(components); 3526 if (deltas) 3527 Mallocator.dispose(deltas); 3528 if (tmpl_deltas) 3529 Mallocator.dispose(tmpl_deltas); 3530 if (comp_add_info) 3531 Mallocator.dispose(comp_add_info); 3532 if (comp_rem_info) 3533 Mallocator.dispose(comp_rem_info); 3534 if (systems) 3535 Mallocator.dispose(systems); 3536 if (add_listeners) 3537 Mallocator.dispose(add_listeners); 3538 if (remove_listeners) 3539 Mallocator.dispose(remove_listeners); 3540 if (change_listeners) 3541 Mallocator.dispose(change_listeners); 3542 } 3543 3544 ///entity components 3545 ushort[] components; 3546 3547 ///deltas in memory for components in EntitiesBlock 3548 ushort[] deltas; 3549 ///deltas in memory for components in EntityTemplate 3550 ushort[] tmpl_deltas; 3551 3552 ///cached new infos after adding component 3553 EntityInfo*[] comp_add_info; 3554 ///cached new infos after removing component 3555 EntityInfo*[] comp_rem_info; 3556 3557 ///alignment of whole entity 3558 ushort alignment; //unused in linear-layout TODO: to remove 3559 ///size of entity (with alignment respect) 3560 ushort size; 3561 ///max number of entities in block 3562 ushort max_entities; 3563 3564 ///array of systems which will update this entity 3565 bool[] systems; 3566 ///systems which are listening for added entities 3567 ushort[] add_listeners; 3568 ///systems which are listening for removed entities 3569 ushort[] remove_listeners; 3570 ///systems which are listening for changed entities (changed in term of contained components) 3571 ushort[] change_listeners; 3572 3573 ///pointer to first block/page 3574 EntitiesBlock* first_block; 3575 ///pointer to last block 3576 EntitiesBlock* last_block; 3577 ///pointer to last updated block 3578 EntitiesBlock* update_block; 3579 } 3580 3581 /************************************************************************************************************************ 3582 Meta data of every block of entities (contained at the begining of block). 3583 */ 3584 struct EntitiesBlock 3585 { 3586 ///return pointer to first element in block 3587 export void* dataBegin() nothrow @nogc pure return 3588 { 3589 ushort dif = EntitiesBlock.sizeof; 3590 return cast(void*)&this + dif; 3591 } 3592 3593 export ushort entityIndex(const(Entity)* entity) nothrow @nogc pure 3594 { 3595 static if (EntityID.sizeof == 8) 3596 return cast(ushort)((cast(void*) entity - dataBegin()) >> 3); 3597 else 3598 return cast(ushort)((cast(void*) entity - dataBegin()) / EntityID.sizeof()); 3599 } 3600 3601 ///pointer to Entity type info 3602 EntityInfo* type_info = null; 3603 ///number of entities in block 3604 ushort entities_count = 0; 3605 ///number of new entities in block 3606 shared ushort added_count = 0; 3607 //ushort added_count = 0; 3608 ///block id 3609 ushort id = 0; 3610 ///maximum number of entities in block 3611 //ushort max_entities = 0; 3612 ///pointer to next block/page 3613 EntitiesBlock* next_block = null; 3614 ///pointer to next block/page 3615 EntitiesBlock* prev_block = null; 3616 //there is a loooot of data (some kB of memory, pure magic) 3617 } 3618 3619 /************************************************************************************************************************ 3620 Structure with data used to calling System calls. 3621 3622 <i>first_block</i>, <i>begin</i>, <i>end</i>, <i>blocks</i> parameters are used 3623 to call partial info update 3624 */ 3625 struct CallData 3626 { 3627 export void update() nothrow @nogc 3628 { 3629 (cast(SytemFuncType) system.m_update)(this); 3630 } 3631 3632 ///system ID. Used to update system pointer after system reload. 3633 uint system_id; 3634 ///pointer to used system 3635 System* system; 3636 ///poiner to Entity type info 3637 EntityManager.EntityInfo* info; 3638 ///delegate function to call (by default it's delegate to onUpdate call) 3639 void delegate() update_delegate; 3640 3641 ///pointer to first block into process (if 0 then first block will be used) 3642 EntitiesBlock* first_block; 3643 ///number of blocks to update (if 0 then update all) 3644 ushort blocks; 3645 ///index of first element in first block 3646 ushort begin; 3647 ///index of last element in last block 3648 ushort end; 3649 ///current thread index 3650 uint thread_id; 3651 //current job index 3652 uint job_id; 3653 } 3654 3655 struct ListenerCallData 3656 { 3657 System* system; 3658 EntitiesBlock* block; 3659 uint begin; 3660 uint end; 3661 } 3662 3663 struct Job 3664 { 3665 CallData[] callers; 3666 uint id; 3667 3668 export void execute() nothrow @nogc 3669 { 3670 foreach (ref caller; callers) 3671 { 3672 caller.thread_id = gEntityManager.threadID(); 3673 caller.job_id = id; 3674 caller.update(); 3675 } 3676 } 3677 } 3678 3679 struct JobGroup 3680 { 3681 Job[] jobs; 3682 JobGroup*[] dependencies; 3683 uint id; 3684 SystemCaller* caller; 3685 //uint max_jobs; 3686 } 3687 3688 struct SystemCaller 3689 { 3690 export ~this() nothrow @nogc 3691 { 3692 if (dependencies) 3693 { 3694 Mallocator.dispose(dependencies); 3695 } 3696 if (exclusion) 3697 { 3698 Mallocator.dispose(exclusion); 3699 } 3700 if (job_group.dependencies) 3701 Mallocator.dispose(job_group.dependencies); 3702 } 3703 3704 uint system_id; 3705 System* system; 3706 Vector!(EntityInfo*) infos; 3707 SystemCaller*[] dependencies; 3708 SystemCaller*[] exclusion; 3709 JobGroup job_group; 3710 } 3711 3712 struct ThreadData 3713 { 3714 ref Vector!EntityID entitesToRemove() @nogc nothrow return 3715 { 3716 return entities_to_remove[data_index]; 3717 } 3718 3719 ref SimpleVector changeEntitiesList() @nogc nothrow return 3720 { 3721 return change_entities_list[data_index]; 3722 } 3723 3724 ref Vector!(EntityInfo*) infosToUpdate() @nogc nothrow return 3725 { 3726 return infos_to_update[data_index]; 3727 } 3728 3729 ref Vector!EntityID entitiesToRemovePrev() @nogc nothrow return 3730 { 3731 return entities_to_remove[1 - data_index]; 3732 } 3733 3734 ref SimpleVector changeEntitiesListPrev() @nogc nothrow return 3735 { 3736 return change_entities_list[1 - data_index]; 3737 } 3738 3739 ref Vector!(EntityInfo*) infosToUpdatePrev() @nogc nothrow return 3740 { 3741 return infos_to_update[1 - data_index]; 3742 } 3743 3744 private: 3745 3746 void swapData() @nogc nothrow 3747 { 3748 data_index = cast(ubyte)(1 - data_index); 3749 } 3750 3751 Vector!EntityID[2] entities_to_remove; 3752 SimpleVector[2] change_entities_list; 3753 Vector!(EntityInfo*)[2] infos_to_update; 3754 3755 ubyte data_index = 0; 3756 } 3757 3758 export struct UpdatePass 3759 { 3760 export ~this() nothrow @nogc 3761 { 3762 assert(name); 3763 if (name) 3764 Mallocator.dispose(name); 3765 foreach (caller; system_callers) 3766 { 3767 Mallocator.dispose(caller); 3768 } 3769 system_callers.clear(); 3770 } 3771 3772 char[] name; 3773 Vector!(SystemCaller*) system_callers; 3774 } 3775 3776 export uint threadID() @nogc nothrow 3777 { 3778 if (m_thread_id_func) 3779 return m_thread_id_func(); 3780 else 3781 return 0; 3782 } 3783 3784 ThreadData[] threads; 3785 3786 Vector!(UpdatePass*) passes; 3787 3788 bool register_state = false; 3789 3790 alias SytemFuncType = void function(ref EntityManager.CallData data) nothrow @nogc; 3791 3792 ///Single page size. Must be power of two. 3793 int m_page_size = 32768; //32768; //4096; 3794 ///Number of pages in block. 3795 int m_pages_in_block = 128; 3796 3797 IDManager id_manager; 3798 BlockAllocator allocator; 3799 3800 EventManager event_manager; 3801 3802 void delegate(JobGroup jobs) nothrow @nogc m_dispatch_jobs; 3803 uint delegate() nothrow @nogc m_thread_id_func; 3804 3805 HashMap!(ushort[], EntityInfo*) entities_infos; 3806 HashMap!(char[], ushort) systems_map; 3807 HashMap!(char[], ushort) components_map; 3808 HashMap!(const(char)[], ushort) events_map; 3809 HashMap!(const(char)[], ushort) passes_map; 3810 HashMap!(const(char)[], ushort) external_dependencies_map; 3811 Vector!System systems; 3812 Vector!ComponentInfo components; 3813 Vector!EventInfo events; 3814 3815 //Mutex add_mutex; 3816 Mutex* entity_block_alloc_mutex; 3817 3818 CallDataAllocator m_call_data_allocator; 3819 struct CallDataAllocator 3820 { 3821 struct Block 3822 { 3823 CallData[256] data; 3824 uint allocated = 0; 3825 } 3826 3827 export ~this() nothrow @nogc 3828 { 3829 foreach (block; blocks) 3830 { 3831 Mallocator.dispose(block); 3832 } 3833 blocks.clear(); 3834 } 3835 3836 Vector!(Block*) blocks; 3837 uint id; 3838 3839 void clear() nothrow @nogc 3840 { 3841 if (blocks.length > 0) 3842 foreach (block; blocks[0 .. id + 1]) 3843 { 3844 block.allocated = 0; 3845 } 3846 id = 0; 3847 //blocks.clear(); 3848 } 3849 3850 CallData[] getCallData(uint num) nothrow @nogc 3851 { 3852 if (blocks.length == 0) 3853 { 3854 Block* new_block = Mallocator.make!Block; 3855 blocks.add(new_block); 3856 } 3857 3858 Block* block = blocks[id]; 3859 if (block.allocated + num >= 256) 3860 { 3861 id++; 3862 if (id == blocks.length) 3863 { 3864 Block* new_block = Mallocator.make!Block; 3865 blocks.add(new_block); 3866 } 3867 block = blocks[id]; 3868 } 3869 3870 CallData[] ret = block.data[block.allocated .. block.allocated + num]; 3871 block.allocated += num; 3872 return ret; 3873 } 3874 } 3875 3876 }