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