1 module tests.basic;
2 
3 import bubel.ecs.core;
4 import bubel.ecs.manager;
5 import bubel.ecs.system;
6 import bubel.ecs.attributes;
7 
8 version(GNU)
9 {
10 	pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a)
11 	{
12 		return a;
13 	}
14 }
15 else import std.array : staticArray;
16 
17 struct CInt
18 {
19     // mixin ECS.Component;
20 
21     alias value this;
22 
23     int value = 1;
24 }
25 
26 struct CFloat
27 {
28     // mixin ECS.Component;
29 
30     alias value this;
31 
32     float value = 2.0;
33 }
34 
35 struct CDouble
36 {
37     // mixin ECS.Component;
38 
39     alias value this;
40 
41     double value = 3.0;
42 }
43 
44 struct CLong
45 {
46     // mixin ECS.Component;
47 
48     alias value this;
49 
50     long value = 10;
51 }
52 
53 struct CShort
54 {
55     // mixin ECS.Component;
56 
57     alias value this;
58 
59     short value = 12;
60 }
61 
62 struct CUnregistered
63 {
64     // mixin ECS.Component;
65 
66     alias value this;
67 
68     short value = 12;
69 }
70 
71 struct CFlag
72 {
73     // mixin ECS.Component;
74 }
75 
76 struct LongAddSystem
77 {
78     mixin ECS.System;
79 
80     struct EntitiesData
81     {
82         int length;
83 
84         CLong[] long_;
85     }
86 
87     void onUpdate(EntitiesData data)
88     {
89         updates_count++;
90         foreach(i;0..data.length)
91         {
92             data.long_[i] += 1;
93         }
94     }
95 
96     int updates_count = 0;
97 }
98 
99 struct EmptySystem
100 {
101     mixin ECS.System!16;
102 
103     struct EntitiesData
104     {
105         int thread_id;
106     }
107 
108     void onUpdate(EntitiesData data)
109     {
110         count++;
111     }
112 
113     int count = 0;
114 }
115 
116 void beforeEveryTest()
117 {
118     becsID!CUnregistered = ushort.max;
119     gEntityManager.initialize(0);
120 
121     gEntityManager.beginRegister();
122 
123     gEntityManager.registerComponent!CInt;
124     gEntityManager.registerComponent!CFloat;
125     gEntityManager.registerComponent!CDouble;
126     gEntityManager.registerComponent!CLong;
127     gEntityManager.registerComponent!CShort;
128     gEntityManager.registerComponent!CFlag;
129 
130     gEntityManager.endRegister();
131 }
132 
133 void afterEveryTest()
134 {
135     gEntityManager.destroy();
136 }
137 
138 @("EntityMeta")
139 unittest
140 {
141     EntityTemplate* tmpl_ = gEntityManager.allocateTemplate([becsID!CInt, becsID!CFloat, becsID!CFlag].staticArray);
142     scope(exit)gEntityManager.freeTemplate(tmpl_);
143     Entity* entity = gEntityManager.addEntity(tmpl_);
144     EntityMeta meta = entity.getMeta();
145     assert(meta.hasComponent(becsID!CInt));
146     assert(meta.getComponent!CInt);
147     assert(meta.hasComponent(becsID!CFloat));
148     assert(meta.getComponent!CFloat);
149     assert(!meta.getComponent!CLong);
150     assert(!meta.hasComponent(becsID!CLong));
151     assert(!meta.getComponent!CUnregistered);
152     assert(!meta.hasComponent(becsID!CUnregistered));
153     assert(*meta.getComponent!CInt == 1);
154     assert(*meta.getComponent!CFloat == 2.0);
155 }
156 
157 @("AddEntity")
158 unittest
159 {
160     EntityTemplate* tmpl_ = gEntityManager.allocateTemplate([becsID!CInt, becsID!CFloat, becsID!CFlag].staticArray);
161     scope(exit)gEntityManager.freeTemplate(tmpl_);
162     assert(tmpl_.info.components.length == 3);
163     assert(tmpl_.info.size == (CInt.sizeof + CFloat.sizeof + EntityID.sizeof));
164     assert(tmpl_.getComponent!CInt);
165     assert(tmpl_.getComponent!CFloat);
166     assert(tmpl_.getComponent!CFlag);
167     assert(!tmpl_.getComponent!CLong);
168     assert(!tmpl_.getComponent!CUnregistered);
169     assert(*tmpl_.getComponent!CInt == 1);
170     assert(*tmpl_.getComponent!CFloat == 2.0);
171 
172     Entity* entity = gEntityManager.addEntity(tmpl_);
173     assert(entity.getComponent!CInt);
174     assert(entity.getComponent!CFloat);
175     assert(*entity.getComponent!CInt == 1);
176     assert(*entity.getComponent!CFloat == 2.0);
177     *entity.getComponent!CInt = 2;
178 
179     Entity* entity2 = gEntityManager.addEntityCopy(entity.id);
180     assert(entity2.getComponent!CInt);
181     assert(entity2.getComponent!CFloat);
182     assert(*entity2.getComponent!CInt == 2);
183     assert(*entity2.getComponent!CFloat == 2.0);
184 
185     CInt int1 = CInt(10);
186     CLong long1 = CLong();
187     CFlag flag1 = CFlag();
188     Entity* entity3 = gEntityManager.addEntity(tmpl_, [ComponentRef(&int1, becsID(int1)), ComponentRef(&long1, becsID(long1)), ComponentRef(&flag1, becsID(flag1))].staticArray);
189     EntityID id = entity3.id;
190     assert(entity3.hasComponent(becsID!CInt));
191     assert(entity3.hasComponent(becsID!CFloat));
192     assert(*entity3.getComponent!CInt == 10);
193     assert(*entity3.getComponent!CFloat == 2.0);
194 
195     CShort short1 = CShort(2);
196     gEntityManager.addComponents(entity3.id, [ComponentRef(&flag1, becsID(flag1)),ComponentRef(&short1, becsID(short1))].staticArray);
197     gEntityManager.commit();
198     entity3 = gEntityManager.getEntity(id);
199     assert(entity3.getComponent!CInt);
200     assert(entity3.getComponent!CFloat);
201     assert(entity3.getComponent!CFlag);
202     assert(entity3.getComponent!CShort);
203     assert(*entity3.getComponent!CInt == 10);
204     assert(*entity3.getComponent!CFloat == 2.0);
205     assert(*entity3.getComponent!CShort == 2);
206 
207     gEntityManager.removeComponents(entity3.id, [becsID!CFlag,becsID!CShort].staticArray);
208     gEntityManager.commit();
209     entity3 = gEntityManager.getEntity(id);
210     assert(entity3.getComponent!CInt);
211     assert(entity3.getComponent!CFloat);
212     assert(!entity3.getComponent!CFlag);
213     assert(!entity3.getComponent!CShort);
214     assert(*entity3.getComponent!CInt == 10);
215     assert(*entity3.getComponent!CFloat == 2.0);
216 
217     gEntityManager.addComponents(entity3.id, [ComponentRef(&flag1, becsID(flag1)),ComponentRef(&short1, becsID(short1))].staticArray);
218     gEntityManager.removeComponents(entity3.id, [becsID!CUnregistered].staticArray);
219     gEntityManager.commit();
220     entity3 = gEntityManager.getEntity(id);
221     assert(entity3.getComponent!CInt);
222     assert(entity3.getComponent!CFloat);
223     assert(entity3.getComponent!CFlag);
224     assert(entity3.getComponent!CShort);
225     assert(*entity3.getComponent!CInt == 10);
226     assert(*entity3.getComponent!CFloat == 2.0);
227     assert(*entity3.getComponent!CShort == 2);
228 
229     gEntityManager.beginRegister();
230 
231     gEntityManager.registerComponent!CUnregistered;
232 
233     gEntityManager.endRegister();
234 
235     CUnregistered unregistered1 = CUnregistered(4);
236     gEntityManager.addComponents(entity3.id, [ComponentRef(&unregistered1, becsID(unregistered1))].staticArray);
237     gEntityManager.commit();
238     entity3 = gEntityManager.getEntity(id);
239     assert(entity3.getComponent!CUnregistered);
240     assert(*entity3.getComponent!CUnregistered == 4);
241 
242     gEntityManager.removeComponents(entity3.id, [becsID!CUnregistered].staticArray);
243     gEntityManager.commit();
244     entity3 = gEntityManager.getEntity(id);
245     assert(!entity3.getComponent!CUnregistered);
246 
247 }
248 
249 //allocate templates
250 @("AllocateTemplates")
251 unittest
252 {
253     //basic template allocation
254     ushort[2] ids = [becsID!CInt, becsID!CFloat];
255     EntityTemplate* tmpl_ = gEntityManager.allocateTemplate(ids);
256     EntityTemplate* tmpl_d = gEntityManager.allocateTemplate([becsID!CFloat, becsID!CInt, becsID!CFloat].staticArray);
257     EntityTemplate* tmpl_cp = gEntityManager.allocateTemplate(tmpl_);
258     assert(tmpl_d.info == tmpl_.info);
259     assert(tmpl_cp.info == tmpl_cp.info);
260     assert(tmpl_.info.components.length == 2);
261     assert(tmpl_.getComponent!CInt);
262     assert(tmpl_.getComponent!CFloat);
263     assert(*tmpl_.getComponent!CInt == 1);
264     assert(*tmpl_.getComponent!CFloat == 2.0);
265     assert(tmpl_cp.getComponent!CFloat);
266     assert(tmpl_cp.getComponent!CInt);
267     assert(tmpl_.getComponent!CInt != tmpl_cp.getComponent!CInt);
268     assert(tmpl_.getComponent!CFloat != tmpl_cp.getComponent!CFloat);
269     assert(*tmpl_.getComponent!CInt == *tmpl_cp.getComponent!CInt);
270     assert(*tmpl_.getComponent!CFloat == *tmpl_cp.getComponent!CFloat);
271     *tmpl_.getComponent!CInt = 4;
272     *tmpl_.getComponent!CFloat = 5.0;
273 
274     //allocate template from template with additional components
275     ushort[2] ids2 = [becsID!CDouble,becsID!CFlag];
276     EntityTemplate* tmpl_2 = gEntityManager.allocateTemplate(tmpl_, ids2);
277     assert(tmpl_2.info.components.length == 4);
278     assert(tmpl_2.getComponent!CInt);
279     assert(tmpl_2.getComponent!CFloat);
280     assert(tmpl_2.getComponent!CDouble);
281     assert(tmpl_2.getComponent!CFlag);
282     assert(*tmpl_2.getComponent!CInt == 4);
283     assert(*tmpl_2.getComponent!CFloat == 5.0);
284     assert(*tmpl_2.getComponent!CDouble == 3.0);
285 
286     assert(tmpl_.info.blocksCount() == 0);
287 
288     Entity* entity = gEntityManager.addEntity(tmpl_);
289     gEntityManager.addComponents(entity.id, CFloat(100));
290     gEntityManager.addComponents(entity.id, CDouble(8.0), CFloat(100));
291 
292     assert(tmpl_.info.blocksCount() == 1);
293 
294     //apply entity changes
295     gEntityManager.commit();
296 
297     assert(tmpl_.info.blocksCount() == 0);
298 
299     //allocate template as entity copy
300     EntityTemplate* tmpl_3 = gEntityManager.allocateTemplate(entity.id);
301     assert(tmpl_3.info.components.length == 3);
302     assert(tmpl_3.getComponent!CInt);
303     assert(tmpl_3.getComponent!CFloat);
304     assert(tmpl_3.getComponent!CDouble);
305     assert(*tmpl_3.getComponent!CInt == 4);
306     assert(*tmpl_3.getComponent!CFloat == 5.0);
307     assert(*tmpl_3.getComponent!CDouble == 8.0);
308 
309     //allocate template with entity data but default values
310     EntityTemplate* tmpl_4 = gEntityManager.allocateTemplate(entity.id, true);
311     assert(tmpl_4.info.components.length == 3);
312     assert(tmpl_4.getComponent!CInt);
313     assert(tmpl_4.getComponent!CFloat);
314     assert(tmpl_4.getComponent!CDouble);
315     assert(*tmpl_4.getComponent!CInt == 1);
316     assert(*tmpl_4.getComponent!CFloat == 2.0);
317     assert(*tmpl_4.getComponent!CDouble == 3.0);
318 
319     //allocate template from template with three additional component
320     ushort[3] ids3 = [becsID!CDouble, becsID!CLong, becsID!CShort];
321     EntityTemplate* tmpl_5 = gEntityManager.allocateTemplate(tmpl_2, ids3);
322     assert(tmpl_5.info.components.length == 6);
323     assert(tmpl_5.getComponent!CInt);
324     assert(tmpl_5.getComponent!CFloat);
325     assert(tmpl_5.getComponent!CDouble);
326     assert(tmpl_5.getComponent!CLong);
327     assert(tmpl_5.getComponent!CShort);
328     assert(*tmpl_5.getComponent!CInt == 4);
329     assert(*tmpl_5.getComponent!CFloat == 5.0);
330     assert(*tmpl_5.getComponent!CDouble == 3.0);
331     assert(*tmpl_5.getComponent!CLong == 10);
332     assert(*tmpl_5.getComponent!CShort == 12);
333 
334     //allocate template from template without one component
335     ushort[1] rem_ids = [becsID!CFloat];
336     EntityTemplate* tmpl_6 = gEntityManager.allocateTemplate(tmpl_, null, rem_ids);
337     assert(tmpl_6.info.components.length == 1);
338     assert(tmpl_6.getComponent!CInt);
339     assert(*tmpl_6.getComponent!CInt == 4);
340 
341     //allocate template from template without one component and two additional
342     EntityTemplate* tmpl_7 = gEntityManager.allocateTemplate(tmpl_, ids3, rem_ids);
343     assert(tmpl_7.info.components.length == 4);
344     assert(tmpl_7.getComponent!CInt);
345     assert(tmpl_7.getComponent!CDouble);
346     assert(tmpl_7.getComponent!CLong);
347     assert(*tmpl_7.getComponent!CInt == 4);
348     assert(*tmpl_7.getComponent!CDouble == 3.0);
349     assert(*tmpl_7.getComponent!CLong == 10);
350 
351     gEntityManager.freeTemplate(tmpl_d);
352     gEntityManager.freeTemplate(tmpl_cp);
353     gEntityManager.freeTemplate(tmpl_);
354     gEntityManager.freeTemplate(tmpl_2);
355     gEntityManager.freeTemplate(tmpl_3);
356     gEntityManager.freeTemplate(tmpl_4);
357     gEntityManager.freeTemplate(tmpl_5);
358     gEntityManager.freeTemplate(tmpl_6);
359     gEntityManager.freeTemplate(tmpl_7);
360 }
361 
362 @("UnsortedComponentIDs")
363 unittest
364 {
365     //basic template allocation
366     ushort[2] ids = [becsID!CFloat, becsID!CInt];
367     ushort[2] ids2 = [becsID!CInt, becsID!CFloat];
368     EntityTemplate* tmpl_ = gEntityManager.allocateTemplate(ids);
369     EntityTemplate* tmpl_2 = gEntityManager.allocateTemplate(ids2);
370     assert(tmpl_.info.components.length == 2);
371     assert(tmpl_.getComponent!CInt);
372     assert(tmpl_.getComponent!CFloat);
373     assert(*tmpl_.getComponent!CInt == 1);
374     assert(*tmpl_.getComponent!CFloat == 2.0);
375     assert(tmpl_.info == tmpl_2.info);
376     gEntityManager.freeTemplate(tmpl_);
377     gEntityManager.freeTemplate(tmpl_2);
378 }
379 
380 @("MultiRegister")
381 unittest
382 {
383     gEntityManager.beginRegister();
384 
385     gEntityManager.endRegister();
386 
387     gEntityManager.beginRegister();
388 
389     gEntityManager.registerComponent!CLong;
390     gEntityManager.registerComponent!CShort;
391 
392     gEntityManager.endRegister();
393 }
394 
395 @("EmptySystem")
396 unittest
397 {
398     gEntityManager.beginRegister();
399 
400     gEntityManager.registerSystem!EmptySystem(0);
401 
402     gEntityManager.endRegister();
403 
404     EmptySystem* system = gEntityManager.getSystem!EmptySystem;
405     assert(system !is null);
406     assert(system.count == 0);
407 
408     System* ecs_system = gEntityManager.getSystem(becsID!EmptySystem);
409     assert(ecs_system !is null);
410     assert(ecs_system.id == becsID!EmptySystem);
411     assert(ecs_system.name == "tests.basic.EmptySystem");
412 
413     gEntityManager.begin();
414 
415     gEntityManager.update();
416 
417     gEntityManager.end();
418 
419     assert(system.count == 1);
420 }
421 
422 @("SystemCallbacks")
423 unittest
424 {
425     struct TestSystem
426     {
427         mixin ECS.System!16;
428 
429         mixin ECS.ExcludedComponents!(CShort);
430 
431         struct EntitiesData
432         {
433             int length;
434             CLong[] long_;
435             @optional CInt[] int_;
436         }
437 
438         void onCreate()
439         {
440             create++;
441         }
442 
443         void onDestroy()
444         {
445             if(destroy)(*destroy)++;
446         }
447 
448         void onEnable()
449         {
450             enable++;
451         }
452 
453         void onDisable()
454         {
455             disable++;
456         }
457 
458         bool onBegin()
459         {
460             begin++;
461             update = 0;
462             return pass;
463         }
464 
465         void onEnd()
466         {
467             end++;
468         }
469 
470         void onUpdate(EntitiesData data)
471         {
472             update++;
473         }
474 
475         int create = 0;
476         int* destroy;
477         int update = 0;
478         int begin = 0;
479         int end = 0;
480         int enable = 0;
481         int disable = 0;
482         bool pass = true;
483     }
484 
485     gEntityManager.beginRegister();
486 
487     gEntityManager.registerSystem!TestSystem(0);
488 
489     gEntityManager.endRegister();
490 
491     TestSystem* system = gEntityManager.getSystem!TestSystem;
492     int destroy = 0;
493     system.destroy = &destroy;
494 
495     gEntityManager.beginRegister();
496 
497     gEntityManager.registerSystem!TestSystem(0);
498 
499     gEntityManager.endRegister();
500 
501     system = gEntityManager.getSystem!TestSystem;
502     system.destroy = &destroy;
503     assert(system !is null);
504     assert(system.create == 1);
505     assert(system.begin == 0);
506     assert(system.end == 0);
507     assert(system.enable == 1);
508     assert(system.disable == 0);
509     //FIXME: currently destroy is only called with Manager.destory which is bug, but there is no workaround for this by now
510     //assert(destroy == 1);
511 
512     System* ecs_system = gEntityManager.getSystem(system.becsID);
513 
514     ecs_system.enable();
515     assert(system.enable == 1);
516     ecs_system.disable();
517     ecs_system.disable();
518     ecs_system.enable();
519     assert(system.enable == 2);
520     assert(system.disable == 1);
521 
522 
523     ushort[2] ids = [becsID!CLong,becsID!CFloat];
524     EntityTemplate* tmpl = gEntityManager.allocateTemplate(ids);
525     scope (exit) gEntityManager.freeTemplate(tmpl);
526     gEntityManager.addEntity(tmpl);
527 
528     gEntityManager.begin();
529     assert(system.begin == 1);
530 
531     gEntityManager.update();
532     assert(system.update == 1);
533 
534     gEntityManager.end();
535     assert(system.end == 1);
536 
537     ushort[2] ids2 = [becsID!CLong, becsID!CInt];
538     EntityTemplate* tmpl2 = gEntityManager.allocateTemplate(ids2);
539     scope (exit) gEntityManager.freeTemplate(tmpl2);
540     gEntityManager.addEntity(tmpl2);
541     gEntityManager.addEntity(tmpl2);
542 
543     gEntityManager.begin();
544     assert(system.begin == 2);
545 
546     gEntityManager.update();
547     assert(system.update == 2);//system is updated number of entity blocks times
548 
549     gEntityManager.end();
550     assert(system.end == 2);
551 
552     ushort[2] ids3 = [becsID!CLong, becsID!CShort];
553     EntityTemplate* tmpl3 = gEntityManager.allocateTemplate(ids3);
554     scope (exit) gEntityManager.freeTemplate(tmpl3);
555     gEntityManager.addEntity(tmpl3);
556 
557     //entity with excluded component shouldn't be updated
558     gEntityManager.begin();
559     assert(system.begin == 3);
560 
561     gEntityManager.update();
562     assert(system.update == 2);
563 
564     gEntityManager.end();
565     assert(system.end == 3);
566 
567     //system can be disable form update in onBegin() callback, onEnd() callback is called
568     system.pass = false;
569     gEntityManager.begin();
570     assert(system.begin == 4);
571 
572     gEntityManager.update();
573     assert(system.update == 0);
574 
575     gEntityManager.end();
576     assert(system.end == 4);
577     system.pass = true;
578 
579     //disabled system is't called
580     ecs_system.disable();
581     gEntityManager.begin();
582     assert(system.begin == 4);
583 
584     gEntityManager.update();
585     assert(system.update == 0);
586 
587     gEntityManager.end();
588     assert(system.end == 4);
589     ecs_system.enable();
590     system.destroy = null;
591 }
592 
593 @("CustomPass")
594 unittest
595 {
596     gEntityManager.beginRegister();
597 
598     gEntityManager.registerPass("custom");
599     gEntityManager.registerSystem!LongAddSystem(-1,"custom");
600 
601     gEntityManager.endRegister();
602 
603     assert(gEntityManager.getPass("custom"));
604     assert(!gEntityManager.getPass("custommm"));
605 
606 
607     LongAddSystem* system = gEntityManager.getSystem!LongAddSystem;
608     assert(system !is null);
609     assert(system.updates_count == 0);
610 
611     System* ecs_system = gEntityManager.getSystem(becsID!LongAddSystem);
612     assert(ecs_system !is null);
613     assert(ecs_system.id == becsID!LongAddSystem);
614     assert(ecs_system.priority == -1);
615     assert(ecs_system.name == "tests.basic.LongAddSystem");
616 
617     ushort[1] ids = [becsID!CLong];
618     EntityTemplate* tmpl = gEntityManager.allocateTemplate(ids);
619     scope (exit) gEntityManager.freeTemplate(tmpl);
620     gEntityManager.addEntity(tmpl);
621 
622     gEntityManager.begin();
623 
624     gEntityManager.update();
625     assert(system.updates_count == 0);
626     gEntityManager.update("custom");
627     assert(system.updates_count == 1);
628 
629     gEntityManager.end();
630 }
631 
632 @("SystemEntityCallbacks")
633 unittest
634 {
635     static int add_order = 0;
636     static int rem_order = 0;
637     static int change_order = 0;
638     struct TestSystem
639     {
640         mixin ECS.System!16;
641 
642         mixin ECS.ExcludedComponents!(CShort);
643 
644         struct EntitiesData
645         {
646             int length;
647             CLong[] long_;
648             @optional CInt[] int_;
649         }
650 
651         void onAddEntity(EntitiesData data)
652         {
653             add++;
654             assert(add_order == 1);  
655             add_order++; 
656         }
657         
658         void onRemoveEntity(EntitiesData data)
659         {
660             remove++;
661             assert(rem_order == 1);  
662             rem_order++; 
663         }
664         
665         void onChangeEntity(EntitiesData data)
666         {
667             change++;
668             assert(change_order == 1);  
669             change_order++; 
670         }
671 
672         void onUpdate(EntitiesData data)
673         {
674         }
675 
676         int add = 0;
677         int remove = 0;
678         int change = 0;
679     }
680 
681     struct TestSystem2
682     {
683         mixin ECS.System!16;
684 
685         mixin ECS.ExcludedComponents!(CShort);
686 
687         struct EntitiesData
688         {
689             int length;
690             CLong[] long_;
691             @optional CInt[] int_;
692         }
693 
694         void onAddEntity(EntitiesData data)
695         {
696             assert(add_order == 2);
697             add_order = 0;
698         }
699         
700         void onRemoveEntity(EntitiesData data)
701         {
702             assert(rem_order == 2);  
703             rem_order = 0 ;
704         }
705         
706         void onChangeEntity(EntitiesData data)
707         {
708             assert(change_order == 2);  
709             change_order = 0;
710         }
711 
712         void onUpdate(EntitiesData data)
713         {
714         }
715     }
716 
717     struct TestSystem3
718     {
719         mixin ECS.System!16;
720 
721         mixin ECS.ExcludedComponents!(CShort);
722 
723         struct EntitiesData
724         {
725             int length;
726             CLong[] long_;
727             @optional CInt[] int_;
728         }
729 
730         void onAddEntity(EntitiesData data)
731         {
732             assert(add_order == 0);
733             add_order++;
734         }
735         
736         void onRemoveEntity(EntitiesData data)
737         {
738             assert(rem_order == 0);  
739             rem_order++; 
740         }
741         
742         void onChangeEntity(EntitiesData data)
743         {
744             assert(change_order == 0);  
745             change_order++; 
746         }
747 
748         void onUpdate(EntitiesData data)
749         {
750         }
751     }
752 
753     gEntityManager.beginRegister();
754 
755     gEntityManager.registerSystem!TestSystem3(-1);
756     gEntityManager.registerSystem!TestSystem(0);
757     gEntityManager.registerSystem!TestSystem2(1);
758 
759     gEntityManager.endRegister();
760 
761     TestSystem* system = gEntityManager.getSystem!TestSystem;
762     assert(system !is null);
763     assert(system.add == 0);
764     assert(system.remove == 0);
765     assert(system.change == 0);
766 
767     EntityTemplate* tmpl = gEntityManager.allocateTemplate([becsID!CLong,becsID!CFloat].staticArray);
768     scope (exit) gEntityManager.freeTemplate(tmpl);
769     EntityID id0 = gEntityManager.addEntity(tmpl).id;
770     gEntityManager.commit();
771     assert(system.add == 1);
772 
773     EntityTemplate* tmpl2 = gEntityManager.allocateTemplate([becsID!CLong, becsID!CInt].staticArray);
774     scope (exit) gEntityManager.freeTemplate(tmpl2);
775     EntityID id1 = gEntityManager.addEntity(tmpl2).id;
776     gEntityManager.commit();
777     assert(system.add == 2);
778 
779     EntityTemplate* tmpl3 = gEntityManager.allocateTemplate([becsID!CLong, becsID!CShort].staticArray);
780     scope (exit) gEntityManager.freeTemplate(tmpl3);
781     EntityID id2 = gEntityManager.addEntity(tmpl3).id;
782     gEntityManager.commit();
783     assert(system.add == 2);
784 
785     gEntityManager.beginRegister();
786     gEntityManager.endRegister();
787 
788     gEntityManager.removeComponents(id0, [becsID!CFloat].staticArray);
789     gEntityManager.commit();
790     assert(system.add == 2);
791     assert(system.remove == 0);
792     assert(system.change == 0);
793 
794     gEntityManager.removeComponents(id1, [becsID!CInt].staticArray);
795     gEntityManager.commit();
796     assert(system.add == 2);
797     assert(system.remove == 0);
798     assert(system.change == 1);
799 
800     gEntityManager.removeComponents(id2, [becsID!CShort].staticArray);
801     gEntityManager.commit();
802     assert(system.add == 3);
803     assert(system.remove == 0);
804     assert(system.change == 1);
805 
806     gEntityManager.addComponents(id2, CShort(1));
807     gEntityManager.commit();
808     assert(system.add == 3);
809     assert(system.remove == 1);
810     assert(system.change == 1);
811 
812     gEntityManager.removeEntity(id0);
813     gEntityManager.commit();
814     assert(system.add == 3);
815     assert(system.remove == 2);
816     assert(system.change == 1);
817 
818     gEntityManager.addComponents(id1, CInt(1));
819     gEntityManager.commit();
820     assert(system.add == 3);
821     assert(system.remove == 2);
822     assert(system.change == 2);
823 
824     gEntityManager.addComponents(id0, CFloat(1));
825     gEntityManager.commit();
826     assert(system.add == 3);
827     assert(system.remove == 2);
828     assert(system.change == 2);
829 
830     gEntityManager.removeEntity(id1);
831     gEntityManager.commit();
832     assert(system.add == 3);
833     assert(system.remove == 3);
834     assert(system.change == 2);
835 
836     gEntityManager.removeEntity(id2);
837     gEntityManager.commit();
838     assert(system.add == 3);
839     assert(system.remove == 3);
840     assert(system.change == 2);
841 }
842 
843 @("TemplateCoverage")
844 unittest
845 {
846     struct TestSystem 
847     {
848         mixin ECS.System;
849 
850         struct EntitiesData
851         {
852             int length;
853             Entity[] entity;
854             @readonly CLong[] long_;
855             @optional CInt[] int_;
856             @readonly CFloat[] float_;
857         }
858 
859         mixin ECS.ExcludedComponents!(CUnregistered);
860     
861         void onUpdate(EntitiesData data)
862         {
863 
864         }
865     }
866 
867     gEntityManager.beginRegister();
868 
869     gEntityManager.registerComponent!CUnregistered;
870 
871     gEntityManager.registerSystem!TestSystem(0);
872 
873     gEntityManager.endRegister();
874 }
875 
876 @("UnregisteredSystem")
877 unittest
878 {
879     struct TestSystem 
880     {
881         mixin ECS.System;
882 
883         struct EntitiesData
884         {
885             int length;
886             Entity[] entity;
887             @readonly CLong[] long_;
888             @optional CInt[] int_;
889             @readonly CFloat[] float_;
890         }
891     
892         void onUpdate(EntitiesData data)
893         {
894 
895         }
896     }
897 
898     assert(gEntityManager.getSystem!TestSystem is null);
899     assert(gEntityManager.getSystem(becsID!TestSystem) is null);
900 }
901 
902 @("MultithreadedUpdate")
903 unittest
904 {
905     struct TestSystem 
906     {
907         mixin ECS.System!64;
908 
909         struct EntitiesData
910         {
911             int length;
912             Entity[] entity;
913             @readonly CLong[] long_;
914             @optional CInt[] int_;
915             @readonly CFloat[] float_;
916         }
917     
918         void onUpdate(EntitiesData data)
919         {
920             update++;
921             entities += data.length;
922         }
923 
924         int update = 0;
925         int entities = 0;
926     }
927 
928     struct TestEmptySystem 
929     {
930         mixin ECS.System;
931 
932         struct EntitiesData
933         {
934             int length;
935         }
936     
937         void onUpdate(EntitiesData data)
938         {
939             update++;
940         }
941 
942         int update = 0;
943     }
944 
945     void dispatch(EntityManager.JobGroup grp)
946     {
947         foreach(job; grp.jobs)
948         {
949             job.execute();
950         }
951     }
952 
953     uint getID()
954     {
955         return 0;
956     }
957 
958     gEntityManager.setMultithreadingCallbacks(&dispatch, &getID);
959 
960     gEntityManager.beginRegister();
961 
962     gEntityManager.registerPass("custom");
963     gEntityManager.registerSystem!TestSystem(-1,"custom");
964     gEntityManager.registerSystem!TestEmptySystem(1,"custom");
965 
966     gEntityManager.endRegister();
967 
968     TestSystem* system = gEntityManager.getSystem!TestSystem;
969     TestEmptySystem* empty_system = gEntityManager.getSystem!TestEmptySystem;
970 
971     ushort[2] ids = [becsID!CLong,becsID!CFloat];
972     EntityTemplate* tmpl = gEntityManager.allocateTemplate(ids);
973     scope (exit) gEntityManager.freeTemplate(tmpl);
974     EntityTemplate* tmpl2 = gEntityManager.allocateTemplate([becsID!CLong,becsID!CInt,becsID!CShort,becsID!CFloat].staticArray);
975     scope (exit) gEntityManager.freeTemplate(tmpl2);
976 
977     gEntityManager.begin();
978 
979     gEntityManager.updateMT("custom");
980     
981     gEntityManager.end();
982 
983     assert(system.update == 0);
984     assert(system.entities == 0);
985     assert(empty_system.update == 1);
986 
987     gEntityManager.addEntity(tmpl);
988 
989     gEntityManager.begin();
990 
991     gEntityManager.updateMT("custom");
992     
993     gEntityManager.end();
994 
995     assert(system.update == 1);
996     assert(system.entities == 1);
997     assert(empty_system.update == 2);
998     system.entities = 0;
999 
1000     foreach(i;0..2000)gEntityManager.addEntity(tmpl);
1001 
1002     gEntityManager.begin();
1003 
1004     gEntityManager.updateMT("custom");
1005     
1006     gEntityManager.end();
1007 
1008     assert(system.update > 2);
1009     assert(system.entities == 2001);
1010     assert(empty_system.update == 3);
1011     system.entities = 0;
1012 
1013     // foreach(i;0..10000)gEntityManager.addEntity(tmpl);
1014 
1015     // gEntityManager.begin();
1016 
1017     // gEntityManager.updateMT("custom");
1018     
1019     // gEntityManager.end();
1020 
1021     // assert(system.entities == 12001);
1022 
1023     void clearEntities(TestSystem.EntitiesData data)
1024     {
1025         foreach(i;0..data.length)
1026         {
1027             gEntityManager.removeEntity(data.entity[i].id);
1028         }
1029     }
1030     gEntityManager.callEntitiesFunction!TestSystem(&clearEntities);
1031     gEntityManager.commit();
1032 
1033     foreach(i;0..2000)
1034     {
1035         gEntityManager.addEntity(tmpl);
1036 
1037         gEntityManager.begin();
1038         gEntityManager.updateMT("custom");
1039         gEntityManager.end();
1040 
1041         assert(system.entities == i+1);
1042         system.entities = 0;
1043     }
1044 
1045     foreach(i;0..90000)gEntityManager.addEntity(tmpl);
1046 
1047     foreach(i;0..2000)
1048     {
1049         gEntityManager.addEntity(tmpl);
1050 
1051         gEntityManager.begin();
1052         gEntityManager.updateMT("custom");
1053         gEntityManager.end();
1054 
1055         assert(system.entities == i+92001);
1056         system.entities = 0;
1057     }
1058 }
1059 
1060 unittest
1061 {
1062     assert(gEntityManager.pageSize == 32768);
1063     assert(gEntityManager.pagesInBlock == 128);
1064 }
1065 
1066 @("AddRemoveEntities")
1067 unittest
1068 {
1069     ushort[3] ids = [becsID!CLong,becsID!CFloat,becsID!CShort];
1070     EntityTemplate* tmpl = gEntityManager.allocateTemplate(ids);
1071     scope (exit) gEntityManager.freeTemplate(tmpl);
1072 
1073     EntityID[5000] entities;
1074 
1075     foreach(i;0..4)
1076     {
1077         foreach(j;0..5000)
1078         {
1079             entities[j] = gEntityManager.addEntity(tmpl).id;
1080         }
1081         gEntityManager.commit();
1082         foreach(j;0..5000)
1083         {
1084             gEntityManager.removeEntity(entities[j]);
1085         }
1086         gEntityManager.commit();
1087     }
1088 }
1089 
1090 @("ChangeEntityComponents")
1091 unittest
1092 {
1093     gEntityManager.beginRegister();
1094 
1095     gEntityManager.registerComponent!CUnregistered;
1096 
1097     gEntityManager.endRegister();
1098 
1099     ushort[1] ids = [becsID!CLong];
1100     EntityTemplate* tmpl = gEntityManager.allocateTemplate(ids);
1101     scope (exit) gEntityManager.freeTemplate(tmpl);
1102 
1103     EntityID id = gEntityManager.addEntity(tmpl).id;
1104     gEntityManager.commit();
1105     Entity* entity = gEntityManager.getEntity(id);
1106     assert(entity.id == id);
1107     assert(entity.getComponent!CLong !is null);
1108     assert(entity.getComponent!CFloat is null);
1109     assert(entity.getComponent!CUnregistered is null);
1110     assert(entity.getComponent!CShort is null);
1111     assert(entity.getComponent!CInt is null);
1112     assert(*entity.getComponent!CLong == 10);
1113 
1114     gEntityManager.addComponents(id, CShort(15), CFloat(13));
1115     gEntityManager.commit();
1116 
1117     entity = gEntityManager.getEntity(id);
1118     assert(entity.id == id);
1119     assert(entity.getComponent!CLong !is null);
1120     assert(entity.getComponent!CFloat !is null);
1121     assert(entity.getComponent!CUnregistered is null);
1122     assert(entity.getComponent!CShort !is null);
1123     assert(entity.getComponent!CInt is null);
1124     assert(*entity.getComponent!CLong == 10);
1125     assert(*entity.getComponent!CShort == 15);
1126     assert(*entity.getComponent!CFloat == 13);
1127 
1128     ushort[3] ids2 = [becsID!CFloat, becsID!CLong, becsID!CUnregistered];
1129     gEntityManager.removeComponents(id, ids2);
1130     gEntityManager.commit();
1131 
1132     entity = gEntityManager.getEntity(id);
1133     assert(entity.id == id);
1134     assert(entity.getComponent!CLong is null);
1135     assert(entity.getComponent!CFloat is null);
1136     assert(entity.getComponent!CUnregistered is null);
1137     assert(entity.getComponent!CShort !is null);
1138     assert(entity.getComponent!CInt is null);
1139     assert(*entity.getComponent!CShort == 15);
1140 
1141     gEntityManager.removeComponents(id, ids2);
1142     gEntityManager.addComponents(id, CShort(11), CLong(2)); //wrong order of components 
1143     gEntityManager.commit();
1144 
1145     entity = gEntityManager.getEntity(id);
1146     assert(entity.id == id);
1147     assert(entity.getComponent!CLong !is null);
1148     assert(entity.getComponent!CFloat is null);
1149     assert(entity.getComponent!CUnregistered is null);
1150     assert(entity.getComponent!CShort !is null);
1151     assert(entity.getComponent!CInt is null);
1152     assert(*entity.getComponent!CLong == 2);
1153     assert(*entity.getComponent!CShort == 15);
1154 
1155     gEntityManager.removeEntity(id);
1156 
1157     entity = gEntityManager.getEntity(id);
1158     assert(entity !is null);
1159     assert(entity.id == id);
1160 
1161     gEntityManager.commit();
1162     entity = gEntityManager.getEntity(id);
1163     assert(entity is null);
1164 }
1165 
1166 @("EventCallbacks")
1167 unittest
1168 {
1169     struct ETest
1170     {
1171         // mixin ECS.Event;
1172     }
1173 
1174     struct ETest2
1175     {
1176         // mixin ECS.Event;
1177         
1178         void onDestroy()
1179         {
1180             destory++;
1181         }
1182         
1183         int super_liczba = 0;
1184         static int destory = 0;
1185     }
1186 
1187     struct TestSystem 
1188     {
1189         mixin ECS.System;
1190 
1191         struct EntitiesData
1192         {
1193             int length;
1194             Entity[] entity;
1195             @readonly CLong[] long_;
1196             @optional CInt[] int_;
1197         }
1198     
1199         void onUpdate(EntitiesData data)
1200         {
1201 
1202         }
1203 
1204         void handleEvent(Entity* entity, ETest event)
1205         {
1206             CLong* long_ = entity.getComponent!CLong;
1207             CInt* int_ = entity.getComponent!CInt;
1208             *long_ += 16;
1209             if(int_)*int_ += 6;
1210         }
1211 
1212         void handleEvent(Entity* entity, ETest2 event)
1213         {
1214             CLong* long_ = entity.getComponent!CLong;
1215             CInt* int_ = entity.getComponent!CInt;
1216             *long_ += event.super_liczba * 2;
1217             if(int_)*int_ += event.super_liczba * 4;
1218         }
1219     }
1220 
1221     struct TestSystem2
1222     {
1223         mixin ECS.System;
1224 
1225         struct EntitiesData
1226         {
1227             int length;
1228             Entity[] entity;
1229             CShort[] short_;
1230             @optional CInt[] int_;
1231         }
1232     
1233         void handleEvent(Entity* entity, ETest event)
1234         {
1235             CShort* short_ = entity.getComponent!CShort;
1236             CInt* int_ = entity.getComponent!CInt;
1237             *short_ += 8;
1238             if(int_)*int_ += 2;
1239         }
1240 
1241         void handleEvent(Entity* entity, ETest2 event)
1242         {
1243             CShort* short_ = entity.getComponent!CShort;
1244             CInt* int_ = entity.getComponent!CInt;
1245             *short_ += event.super_liczba;
1246             if(int_)*int_ *= event.super_liczba;
1247         }
1248     }
1249 
1250     gEntityManager.beginRegister();
1251 
1252     gEntityManager.registerEvent!ETest;
1253     gEntityManager.registerEvent!ETest2;
1254 
1255     gEntityManager.registerEvent!ETest;
1256     gEntityManager.registerEvent!ETest2;
1257 
1258     gEntityManager.registerSystem!TestSystem2(1);
1259     gEntityManager.registerSystem!TestSystem(0);
1260 
1261     gEntityManager.endRegister();
1262 
1263     ushort[1] ids = [becsID!CLong];
1264     EntityTemplate* tmpl = gEntityManager.allocateTemplate(ids);
1265     scope (exit) gEntityManager.freeTemplate(tmpl);
1266     ushort[1] ids2 = [becsID!CShort];
1267     EntityTemplate* tmpl2 = gEntityManager.allocateTemplate(ids2);
1268     scope (exit) gEntityManager.freeTemplate(tmpl2);
1269 
1270     Entity* entity = gEntityManager.addEntity(tmpl);
1271     EntityID id = entity.id;
1272     assert(*entity.getComponent!CLong == 10);
1273     Entity* entity2 = gEntityManager.addEntity(tmpl2);
1274     EntityID id2 = entity2.id;
1275     assert(*entity2.getComponent!CShort == 12);
1276 
1277     gEntityManager.sendEvent(id,ETest());
1278     gEntityManager.sendEvent(id,ETest2(10));
1279     gEntityManager.sendEvent(id2,ETest());
1280     gEntityManager.sendEvent(id2,ETest2(12));
1281     gEntityManager.commit();
1282     assert(ETest2.destory == 2);
1283 
1284     entity = gEntityManager.getEntity(id);
1285     entity2 = gEntityManager.getEntity(id2);
1286     assert(*entity.getComponent!CLong == 46);
1287     assert(*entity2.getComponent!CShort == 32);
1288 
1289     gEntityManager.addComponents(id, CInt(2), CShort(1));
1290     gEntityManager.sendEvent(id,ETest());
1291     gEntityManager.sendEvent(id,ETest2(2));
1292     gEntityManager.commit();
1293     assert(ETest2.destory == 3);
1294 
1295     entity = gEntityManager.getEntity(id);
1296     assert(*entity.getComponent!CLong == 66);
1297     assert(*entity.getComponent!CInt == 2);//36);
1298 
1299     //test for multiple event blocks
1300     long result = *entity.getComponent!CLong;
1301     foreach(i;0..10000)
1302     {
1303         gEntityManager.sendEvent(id,ETest());
1304         gEntityManager.sendEvent(id,ETest2(4));
1305         result += 16;
1306         result += 8;
1307     }
1308     gEntityManager.commit();
1309     assert(ETest2.destory == 10003);
1310     entity = gEntityManager.getEntity(id);
1311     assert(*entity.getComponent!CLong == result);
1312 
1313     //cover funcion to clearEvents before destroying manager
1314     gEntityManager.sendEvent(id,ETest());
1315 }
1316 
1317 @("EntitiesFunction")
1318 unittest
1319 {
1320     struct TestSystem
1321     {
1322         mixin ECS.System;
1323 
1324         struct EntitiesData
1325         {
1326             uint length;
1327             CInt[] int_;
1328         }
1329 
1330         void onUpdate(EntitiesData entities)
1331         {
1332             
1333         }
1334     }
1335 
1336     void func1(TestSystem.EntitiesData entities)
1337     {
1338         foreach(i;0 .. entities.length)
1339         {
1340             entities.int_[i] += entities.int_[i] / 2;
1341         }
1342     }
1343 
1344     void func2(TestSystem.EntitiesData entities)
1345     {
1346         foreach(i;0 .. entities.length)
1347         {
1348             entities.int_[i] += 8;
1349         }
1350     }
1351 
1352     gEntityManager.beginRegister();
1353 
1354     gEntityManager.registerSystem!TestSystem(1);
1355 
1356     gEntityManager.endRegister();
1357 
1358     EntityTemplate* tmpl = gEntityManager.allocateTemplate([becsID!CInt].staticArray);
1359     scope (exit) gEntityManager.freeTemplate(tmpl);
1360     EntityID id1 = gEntityManager.addEntity(tmpl).id;
1361 
1362     EntityTemplate* tmpl2 = gEntityManager.allocateTemplate([becsID!CInt, becsID!CLong].staticArray);
1363     scope (exit) gEntityManager.freeTemplate(tmpl2);
1364     EntityID id2 = gEntityManager.addEntity(tmpl2).id;
1365 
1366     gEntityManager.begin();
1367 
1368     Entity* entity1 = gEntityManager.getEntity(id1);
1369     Entity* entity2 = gEntityManager.getEntity(id2);
1370     assert(*entity1.getComponent!CInt == 1);
1371     assert(*entity2.getComponent!CInt == 1);
1372 
1373     gEntityManager.callEntitiesFunction!TestSystem(&func2);
1374     assert(*entity1.getComponent!CInt == 9);
1375     assert(*entity2.getComponent!CInt == 9);
1376 
1377     gEntityManager.callEntitiesFunction!TestSystem(&func1);
1378     assert(*entity1.getComponent!CInt == 13);
1379     assert(*entity2.getComponent!CInt == 13);
1380 
1381     gEntityManager.end();
1382 }
1383 
1384 @("SystemDependencies")
1385 unittest
1386 {
1387     struct TestSystem
1388     {
1389         mixin ECS.System;
1390 
1391         struct EntitiesData
1392         {
1393             uint length;
1394             @readonly CInt[] int_;
1395         }
1396 
1397         void onUpdate(EntitiesData entities)
1398         {
1399             
1400         }
1401     }
1402 
1403     struct TestSystem2
1404     {
1405         mixin ECS.System;
1406 
1407         struct EntitiesData
1408         {
1409             uint length;
1410             CInt[] int_;
1411         }
1412 
1413         void onUpdate(EntitiesData entities)
1414         {
1415             
1416         }
1417     }
1418 
1419     struct TestSystem3
1420     {
1421         mixin ECS.System;
1422 
1423         struct EntitiesData
1424         {
1425             uint length;
1426             @readonly CInt[] int_;
1427         }
1428 
1429         void onUpdate(EntitiesData entities)
1430         {
1431             
1432         }
1433     }
1434 
1435     struct TestSystem4
1436     {
1437         mixin ECS.System;
1438 
1439         struct EntitiesData
1440         {
1441             uint length;
1442             CInt[] int_;
1443             CLong[] long_;
1444         }
1445 
1446         void onUpdate(EntitiesData entities)
1447         {
1448             
1449         }
1450     }
1451 
1452     struct TestSystem5
1453     {
1454         mixin ECS.System;
1455 
1456         struct EntitiesData
1457         {
1458             uint length;
1459             @readonly CLong[] int_;
1460         }
1461 
1462         void onUpdate(EntitiesData entities)
1463         {
1464             
1465         }
1466     }
1467 
1468     gEntityManager.beginRegister();
1469 
1470     gEntityManager.registerSystem!TestSystem(0);
1471     gEntityManager.registerSystem!TestSystem2(1);
1472     gEntityManager.registerSystem!TestSystem3(2);
1473     gEntityManager.registerSystem!TestSystem4(3);
1474     gEntityManager.registerSystem!TestSystem5(4);
1475 
1476     gEntityManager.endRegister();
1477 
1478     const (EntityManager.UpdatePass)* pass = gEntityManager.getPass("update");
1479     assert(pass != null);
1480     assert(pass.system_callers.length == 5);
1481     assert(pass.system_callers[0].system_id == becsID!TestSystem);
1482     assert(pass.system_callers[1].system_id == becsID!TestSystem2);
1483     assert(pass.system_callers[2].system_id == becsID!TestSystem3);
1484     assert(pass.system_callers[3].system_id == becsID!TestSystem4);
1485     assert(pass.system_callers[4].system_id == becsID!TestSystem5);
1486     assert(pass.system_callers[0].dependencies.length == 0);
1487     assert(pass.system_callers[1].dependencies.length == 1);
1488     assert(pass.system_callers[2].dependencies.length == 1);
1489     assert(pass.system_callers[3].dependencies.length == 3);
1490     assert(pass.system_callers[4].dependencies.length == 1);
1491     assert(pass.system_callers[1].dependencies[0].system_id == becsID!TestSystem);
1492     assert(pass.system_callers[2].dependencies[0].system_id == becsID!TestSystem2);
1493     assert(pass.system_callers[3].dependencies[0].system_id == becsID!TestSystem);
1494     assert(pass.system_callers[3].dependencies[1].system_id == becsID!TestSystem2);
1495     assert(pass.system_callers[3].dependencies[2].system_id == becsID!TestSystem3);
1496     assert(pass.system_callers[4].dependencies[0].system_id == becsID!TestSystem4);
1497 }
1498 
1499 @("ExternalSystemDependencies")
1500 unittest
1501 {
1502     enum TestDependency = "TestDepencency";
1503     
1504     struct TestSystem
1505     {
1506         mixin ECS.System;
1507 
1508         mixin ECS.ReadOnlyDependencies!(TestDependency);
1509 
1510         struct EntitiesData
1511         {
1512             uint length;
1513             @readonly CInt[] int_;
1514         }
1515 
1516         void onUpdate(EntitiesData entities)
1517         {
1518             
1519         }
1520     }
1521 
1522     struct TestSystem2
1523     {
1524         mixin ECS.System;
1525 
1526         mixin ECS.WritableDependencies!(TestDependency);
1527 
1528         struct EntitiesData
1529         {
1530             uint length;
1531             @readonly CInt[] int_;
1532         }
1533 
1534         void onUpdate(EntitiesData entities)
1535         {
1536             
1537         }
1538     }
1539 
1540     struct TestSystem3
1541     {
1542         mixin ECS.System;
1543         
1544         mixin ECS.ReadOnlyDependencies!(TestDependency);
1545 
1546         struct EntitiesData
1547         {
1548             uint thread_id;
1549         }
1550 
1551         void onUpdate(EntitiesData entities)
1552         {
1553             
1554         }
1555     }
1556 
1557     struct TestSystem4
1558     {
1559         mixin ECS.System;
1560 
1561         mixin ECS.WritableDependencies!(TestDependency);
1562 
1563         struct EntitiesData
1564         {
1565             uint length;
1566             @readonly CInt[] int_;
1567         }
1568 
1569         void onUpdate(EntitiesData entities)
1570         {
1571             
1572         }
1573     }
1574 
1575     struct TestSystem5
1576     {
1577         mixin ECS.System;
1578 
1579         mixin ECS.ReadOnlyDependencies!(TestDependency);
1580 
1581         struct EntitiesData
1582         {
1583             uint length;
1584             @readonly CLong[] int_;
1585         }
1586 
1587         void onUpdate(EntitiesData entities)
1588         {
1589             
1590         }
1591     }
1592 
1593     gEntityManager.beginRegister();
1594 
1595     gEntityManager.registerDependency(TestDependency);
1596 
1597     gEntityManager.registerSystem!TestSystem(0);
1598     gEntityManager.registerSystem!TestSystem2(1);
1599     gEntityManager.registerSystem!TestSystem3(2);
1600     gEntityManager.registerSystem!TestSystem4(3);
1601     gEntityManager.registerSystem!TestSystem5(4);
1602 
1603     gEntityManager.endRegister();
1604 
1605     const (EntityManager.UpdatePass)* pass = gEntityManager.getPass("update");
1606     assert(pass != null);
1607     assert(pass.system_callers.length == 5);
1608     assert(pass.system_callers[0].system_id == becsID!TestSystem);
1609     assert(pass.system_callers[1].system_id == becsID!TestSystem2);
1610     assert(pass.system_callers[2].system_id == becsID!TestSystem3);
1611     assert(pass.system_callers[3].system_id == becsID!TestSystem4);
1612     assert(pass.system_callers[4].system_id == becsID!TestSystem5);
1613     assert(pass.system_callers[0].dependencies.length == 0);
1614     assert(pass.system_callers[1].dependencies.length == 1);
1615     assert(pass.system_callers[2].dependencies.length == 1);
1616     assert(pass.system_callers[3].dependencies.length == 3);
1617     assert(pass.system_callers[4].dependencies.length == 2);
1618     assert(pass.system_callers[1].dependencies[0].system_id == becsID!TestSystem);
1619     assert(pass.system_callers[2].dependencies[0].system_id == becsID!TestSystem2);
1620     assert(pass.system_callers[3].dependencies[0].system_id == becsID!TestSystem);
1621     assert(pass.system_callers[3].dependencies[1].system_id == becsID!TestSystem2);
1622     assert(pass.system_callers[3].dependencies[2].system_id == becsID!TestSystem3);
1623     assert(pass.system_callers[4].dependencies[0].system_id == becsID!TestSystem2);
1624     assert(pass.system_callers[4].dependencies[1].system_id == becsID!TestSystem4);
1625 }
1626 
1627 
1628 @("CustomFilter")
1629 unittest
1630 {
1631     struct TestSystem
1632     {
1633         mixin ECS.System;
1634 
1635         struct EntitiesData
1636         {
1637             uint length;
1638             @optional CInt[] int_;
1639             @optional CLong[] long_;
1640             @optional CFloat[] float_;
1641             @optional CDouble[] double_;
1642         }
1643 
1644         bool filterEntity(EntityManager.EntityInfo* info)
1645         {
1646             if(!info.hasComponent(becsID!CInt))return false;
1647             int one_from = 0;
1648             if(info.hasComponent(becsID!CLong))one_from++;
1649             if(info.hasComponent(becsID!CFloat))one_from++;
1650             if(info.hasComponent(becsID!CDouble))one_from++;
1651             if(one_from == 1)return true;
1652             return false;
1653         }
1654 
1655         void onUpdate(EntitiesData entities)
1656         {
1657             updates++;
1658         }
1659 
1660         uint updates = 0;
1661     }
1662 
1663     struct TestSystem2
1664     {
1665         mixin ECS.System;
1666 
1667         struct EntitiesData
1668         {
1669             uint length;
1670             @optional CInt[] int_;
1671             @optional CLong[] long_;
1672             @optional CFloat[] float_;
1673             @optional CDouble[] double_;
1674         }
1675 
1676         bool filterEntity(EntityManager.EntityInfo* info)
1677         {
1678             if(info.hasComponent(becsID!CInt) && info.hasComponent(becsID!CFloat) && !info.hasComponent(becsID!CLong) && !info.hasComponent(becsID!CDouble))return true;
1679             if(info.hasComponent(becsID!CLong) && info.hasComponent(becsID!CDouble) && !info.hasComponent(becsID!CInt) && !info.hasComponent(becsID!CFloat))return true;
1680             return false;
1681         }
1682 
1683         void onUpdate(EntitiesData entities)
1684         {
1685             updates++;
1686         }
1687 
1688         uint updates = 0;
1689     }
1690 
1691     gEntityManager.beginRegister();
1692 
1693     gEntityManager.registerSystem!TestSystem(0);
1694     gEntityManager.registerSystem!TestSystem2(1);
1695 
1696     gEntityManager.endRegister();
1697 
1698 
1699     EntityTemplate* tmpl_ = gEntityManager.allocateTemplate([becsID!CInt, becsID!CLong, becsID!CFloat, becsID!CDouble].staticArray);
1700     scope(exit)gEntityManager.freeTemplate(tmpl_);
1701     EntityTemplate* tmpl_2 = gEntityManager.allocateTemplate([becsID!CInt, becsID!CFloat].staticArray);
1702     scope(exit)gEntityManager.freeTemplate(tmpl_2);
1703     EntityTemplate* tmpl_3 = gEntityManager.allocateTemplate([becsID!CLong, becsID!CDouble].staticArray);
1704     scope(exit)gEntityManager.freeTemplate(tmpl_3);
1705     EntityTemplate* tmpl_4 = gEntityManager.allocateTemplate([becsID!CInt, becsID!CLong, becsID!CDouble].staticArray);
1706     scope(exit)gEntityManager.freeTemplate(tmpl_4);
1707     EntityTemplate* tmpl_5 = gEntityManager.allocateTemplate([becsID!CInt, becsID!CDouble].staticArray);
1708     scope(exit)gEntityManager.freeTemplate(tmpl_5);
1709     EntityTemplate* tmpl_6 = gEntityManager.allocateTemplate([becsID!CDouble].staticArray);
1710     scope(exit)gEntityManager.freeTemplate(tmpl_6);
1711 
1712     gEntityManager.addEntity(tmpl_);
1713     gEntityManager.addEntity(tmpl_2);
1714     gEntityManager.addEntity(tmpl_3);
1715     gEntityManager.addEntity(tmpl_4);
1716     gEntityManager.addEntity(tmpl_5);
1717     gEntityManager.addEntity(tmpl_6);
1718 
1719     TestSystem* test_system = gEntityManager.getSystem!TestSystem;
1720     TestSystem2* test_system2 = gEntityManager.getSystem!TestSystem2;
1721     
1722     gEntityManager.begin();
1723     gEntityManager.update();
1724     gEntityManager.end();
1725 
1726     assert(test_system.updates == 2);
1727     assert(test_system2.updates == 2);
1728 }