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