1 module decs.ecs;
2 
3 import core.stdc.stdio;
4 import core.stdc.stdlib;
5 import core.stdc..string;
6 import core.lifetime;
7 
8 import decs.collections;
9 import decs.dbg;
10 import decs.memory;
11 
12 // commit: f9cf1322ddaf1e9b80349fcc2623babb79f83538
13 // https://github.com/prime31/zig-ecs
14 
15 struct SparseSet(SparseT)
16 {
17     enum page_size = 32_768;
18     enum entity_per_page = page_size / SparseT.sizeof;
19 
20     Array!(SparseT[]) sparse;
21     Array!SparseT dense;
22     SparseT entity_mask;
23     Allocator* allocator;
24 
25     static SparseSet create(Allocator* allocator)
26     {
27         SparseSet ret;
28         ret.sparse = Array!(SparseT[]).createWith(allocator);
29         ret.dense = Array!(SparseT).createWith(allocator);
30         assert(ret.sparse.capacity == 16);
31         assert(ret.dense.capacity == 16);
32 
33         ret.entity_mask = SparseT.max;
34         ret.allocator = allocator;
35         return ret;
36     }
37 
38     void destroy()
39     {
40         sparse.expandToCapacity();
41         foreach(size_t i, array; sparse)
42         {
43             if(array)
44                 sparse.allocator.free(array.ptr);
45         }
46     }
47 
48     size_t page(SparseT sparse)
49     {
50         return (sparse & entity_mask) / entity_per_page;
51     }
52 
53     size_t offset(SparseT sparse)
54     {
55         return sparse & (entity_per_page - 1);
56     }
57 
58     SparseT index(SparseT sparse)
59     {
60         assert((contains(sparse)));
61 
62         return this.sparse.get(page(sparse))[offset(sparse)];
63     }
64 
65     bool contains(SparseT sparse)
66     {
67         // TODO: not sure about the null on array thing
68         // i just replaced with length  rn
69         auto curr = page(sparse);
70         return curr < this.sparse.length  &&
71         this.sparse.get(curr) != null &&
72         this.sparse.get(curr)[offset(sparse)] != SparseT.max;
73     }
74 
75     void add(SparseT sparse)
76     {
77 
78         assert(!contains(sparse));
79   
80         // assure(page(entt))[offset(entt)] = packed.size()
81 
82         size_t pos = page(sparse);
83         size_t off = offset(sparse);
84 
85         assure(pos)[off] = cast(SparseT) dense.length;
86         
87         dense.add(sparse);
88     }
89 
90     void remove(SparseT sparse)
91     {
92        assert(contains(sparse));
93 
94        auto curr = page(sparse);
95        auto pos = offset(sparse);
96        auto last_dense = dense.get(dense.length - 1);
97 
98        dense.set(this.sparse.get(curr)[pos], last_dense);
99        this.sparse.get(page(last_dense))[offset(last_dense)] = this.sparse.get(curr)[pos];
100        this.sparse.get(curr)[pos] = SparseT.max;
101 
102        dense.pop();
103     }
104 
105     SparseT[] assure(size_t pos) {
106         if (pos >= sparse.length) {
107             //printf("resize! %llu\n", pos);
108             auto start_pos = sparse.length;
109             sparse.resize(pos + 1);
110             sparse.expandToCapacity();
111             sparse._items[start_pos .. $] = null;
112         }
113 
114         if ( sparse._items[pos].ptr) { // TODO: not good to get stuff from u_ array
115             return sparse._items[pos];
116         }
117 
118         //printf("alloc bad\n");
119         auto new_page = sparse.allocator.alloc_array!SparseT(entity_per_page); 
120         new_page[0 .. $] = SparseT.max;
121 
122         sparse.set(pos, new_page);
123 
124         return sparse.get(pos);
125     }
126 
127     size_t len()
128     {
129         return dense.length;
130     }
131 }
132 
133 struct EntityTraitsDefinition(EntityType, IndexType, VersionType) 
134 {
135     EntityType entity_type;
136     IndexType index_type;
137     VersionType version_type;
138     /// Mask to use to get the entity index number out of an identifier
139     EntityType entity_mask = IndexType.max;
140     /// Mask to use to get the version out of an identifier
141     EntityType version_mask = VersionType.max;
142 }
143 
144 
145 struct ComponentStorage(Component, Entity)
146 {
147     // TODO: in zig-ecs they have to create a dummy component with a field u1
148     // because of some weird thing about empty struct
149     // this problem doesn't exist with D, i'll have to create tests with empty struct!!!!
150 
151     SparseSet!(Entity)* set;
152     Array!(Component) instances;
153     Allocator* allocator;
154     /// doesnt really belong here...used to denote group ownership
155     size_t superr = 0;
156     void delegate(ComponentStorage*) safeDeinit;
157     void delegate(ComponentStorage*, Entity, Entity, bool) safeSwap;
158     void delegate(ComponentStorage*, Entity) safeRemoveIfContains;
159 
160     // TODO: implement signals
161     //Signal!(Entity) construction;
162     //Signal!(Entity) update;
163     //Signal!(Entity) destruction;
164 
165     static ComponentStorage* createPtr(Allocator* allocator)
166     {
167         ComponentStorage* ret = allocator.create!ComponentStorage();
168         ret.set = allocator.create!(SparseSet!Entity);
169         *ret.set = SparseSet!(Entity).create(allocator);
170         assert(ret.set.dense.capacity == 16);
171         assert(ret.set.sparse.capacity == 16);
172         // TODO: check empty struct
173         ret.instances = Array!(Component).createWith(allocator);
174         ret.allocator = allocator;
175         ret.superr = 0;
176         ret.safeDeinit = (ComponentStorage* c) {
177             printf("YOOOOOO safeDeinit\n");
178             c.instances.destroy();
179         };
180         ret.safeSwap = (ComponentStorage* c, Entity a, Entity b , bool instances_only) {
181             printf("YOOOOOO safeSwap\n");
182             if(!instances_only)
183             {
184                 
185             }
186         };
187         ret.safeRemoveIfContains = (ComponentStorage* c, Entity e) {
188             printf("YOOOOOO safeRemvoeIfContains\n");
189             if(c.contains(e))
190                 c.remove(e);
191         };
192 
193         return ret;
194     }
195 
196     void add(Entity entity, Component component)
197     {
198         // TODO: check empty struct when i sort this stuff out
199         instances.add(component);
200         set.add(entity);
201         // TODO: signal construction
202     }
203 
204     Component* get(Entity entity)
205     {
206         assert(contains(entity));
207         return &instances._items[set.index(entity)];
208     }
209 
210     bool contains(Entity entity)
211     {
212         return set.contains(entity);
213     }
214 
215     void remove(Entity entity)
216     {
217         // TODO: signal destruction
218         instances.removeSwap(set.index(entity));
219         set.remove(entity);
220     }
221 
222     void removeIfContains(Entity entity)
223     {
224         // TODO: need figure out why this
225         static if( is(Component == bool) )
226         {
227 
228         }
229 
230         if(contains(entity))
231         {
232             remove(entity);
233         }
234     }
235 
236     size_t len()
237     {
238         return set.len();
239     }
240 }
241 
242 struct Optional(T)
243 {
244     enum undefined = Optional.init;
245 
246     private T value;
247     private bool defined = false;
248 
249     void opAssign ( T rhs )
250     {
251         defined = true;
252         value = rhs;
253     }
254 }
255 
256 struct Handles(HandleType, IndexType, VersionType)
257 {
258     HandleType[] handles;
259     IndexType append_cursor = 0;
260     Optional!IndexType last_destroyed;
261 
262     Allocator* allocator;
263 
264     enum invalid_id = IndexType.max;
265 
266     static Handles createWith(Allocator* allocator, size_t capacity = 32)
267     {
268         Handles ret;
269         ret.handles = allocator.alloc_array!HandleType(capacity);
270         ret.allocator = allocator;
271         return ret;
272     }
273 
274     bool alive(HandleType handle)
275     {
276         auto id = extractId(handle);
277         return id < append_cursor && handles[id] == handle;
278     }
279 
280     HandleType create()
281     {
282         //printf("# create entity\n");
283         if(!last_destroyed.defined)
284         {
285             //printf("\tlast destroyed null\n");
286             // ensure capacity and grow if needed
287             if(handles.length - 1 == append_cursor)
288             {
289                 //printf("\treallocate handles\n");
290                 handles = allocator.reallocate_array(handles.ptr, handles.length * 2);
291             }
292 
293             auto id = append_cursor;
294             auto handle = forge(append_cursor, 0);
295             handles[id] = handle;
296 
297             append_cursor += 1;
298 
299             
300             //printf("\tnew handle: %llu\n", handle);
301             return handle;
302         }
303 
304         auto versionn = extractVersion(handles[last_destroyed.value]);
305         auto destroyed_id =  extractId(handles[last_destroyed.value]);
306 
307         auto handle = forge(last_destroyed.value, versionn);
308         handles[last_destroyed.value] = handle;
309 
310         // TODO: redo this optional bullshit
311         last_destroyed = (destroyed_id == invalid_id) ? Optional!(IndexType)() : Optional!(IndexType)(destroyed_id, true); // destroyed_id;
312 
313         //printf("\thandle: %llu, destroyed_id: %lu,  last_destroyed: %lu :: invalid: %lu\n", handle, destroyed_id, last_destroyed.value, invalid_id);
314         return handle;
315     }
316 
317     void remove(HandleType handle)
318     {
319         //printf("# remove handle: %llu\n", handle);
320         //printf("\tinvalid is: %lu\n", invalid_id);
321         
322         auto id = extractId(handle);
323         if (id > append_cursor || handles[id] != handle)
324             panic("RemovedInvalidHandle");
325 
326         //printf("\tid: %lu append_cursor: %lu\n", id, append_cursor);
327 
328         auto next_id = (last_destroyed.defined) ? last_destroyed.value : invalid_id;
329         if (next_id == id)
330             panic("ExhaustedEntityRemoval");
331 
332         //printf("\tnext_id: %lu\n", next_id);
333 
334         auto versionn = extractVersion(handle);
335         handles[id] = forge(next_id, versionn + 1);
336 
337         last_destroyed = id;
338 
339         //printf("\tremove: %llu %lu %lu %lu %llu\n", handle, id, next_id, versionn, handles[id]);
340     }
341 
342     IndexType extractId(HandleType handle)
343     {
344         // TODO: cast should be fine, but double check if errors occurs
345         return cast(IndexType) handle;
346     }
347 
348     VersionType extractVersion(HandleType handle)
349     {   
350         // TODO: cast should be fine, but double check if errors occurs
351         return cast(VersionType) (handle >> (IndexType.sizeof * 8) );
352     }
353 
354     HandleType forge(IndexType id, VersionType versionn)
355     {
356         // HandleType, IndexType, VersionType
357         //      ulong,      uint,        uint
358         return id | cast(HandleType) (versionn) << (IndexType.sizeof * 8);
359     }
360 }
361 
362 struct TypeStore
363 {
364     HashMap!(uint, void*) map;
365     Allocator* allocator;
366 
367     static TypeStore create(Allocator* allocator)
368     {
369         TypeStore ret;
370         ret.map = HashMap!(uint, void*).create(allocator);
371         ret.allocator = allocator;
372         return ret;
373     }
374 }
375 
376 
377 alias EntityTraitsDefinition_large = EntityTraitsDefinition!(ulong, uint, uint);
378 alias EntityHandle_large = Handles!(ulong, uint, uint);
379 alias Entity = ulong; // 1st
380 alias Storage(T) = ComponentStorage!(T, Entity);
381 
382 struct GroupData
383 {
384      ulong hash;
385      ubyte size;
386      /// optional. there will be an entity_set for non-owning groups and current for owning
387      SparseSet!(Entity) entity_set;
388      uint[] owned;
389      uint[] include;
390      uint[] exclude;
391      Registry* registry;
392      size_t current;
393 
394      static GroupData create(Allocator* allocator, Registry* registry, ulong hash, uint[] owned, uint[] include, uint[] exclude)
395      {
396          GroupData ret;
397          ret.hash = hash;
398          ret.size = cast(ubyte) (owned.length + include.length + exclude.length);
399          ret.registry = registry;
400          if(owned.length == 0)
401             ret.entity_set = SparseSet!(Entity).create(allocator);
402         ret.owned = dupe(allocator, owned);
403         ret.include = dupe(allocator, include);
404         ret.exclude = dupe(allocator, exclude);
405         ret.registry = registry;
406         ret.current = 0;
407         return ret;
408      }
409 }
410 
411 struct Registry
412 {
413     EntityHandle_large handles;
414     HashMap!(ulong, size_t) components;
415     HashMap!(uint, size_t) contexts;
416     Array!(GroupData*) groups;
417     TypeStore singletons;
418     Allocator* allocator;
419 
420 
421     static Registry create(Allocator* allocator)
422     {
423         Registry ret;
424         ret.handles = EntityHandle_large.createWith(allocator);
425         ret.components = HashMap!(ulong, size_t).create(allocator);
426         ret.contexts = HashMap!(uint, size_t).create(allocator);
427         ret.groups = Array!(GroupData*).createWith(allocator);
428         ret.singletons = TypeStore.create(allocator);
429         ret.allocator = allocator;    
430         return ret;
431     }
432 
433     bool valid(Entity entity)
434     {
435         return handles.alive(entity);
436     }
437 
438     Entity create_entity()
439     {
440         return handles.create();
441     }
442 
443     void destroy_entity(Entity entity)
444     {
445         assert(valid(entity));
446         removeAll(entity);
447         handles.remove(entity);
448     }
449 
450     void removeAll(Entity entity)
451     {
452         assert(valid(entity));
453 
454         foreach(kv; components)
455         {
456             auto store = cast(Storage!(bool)*) kv.value;
457             store.removeIfContains(entity);
458         }
459     }
460 
461     void add(T)(Entity entity, T value)
462     {
463         assert(valid(entity));
464         auto s = assure!T();
465         s.add(entity, value);
466     }
467 
468     void remove(T)(Entity entity)
469     {
470         assert(valid(entity));
471         auto s = assure!T();
472         s.remove(entity);
473     }
474 
475     T* get(T)(Entity entity)
476     {
477         assert(valid(entity));
478         auto s = assure!T();
479         assert(s);
480 
481         return s.get(entity);
482     }
483 
484     bool has(T)(Entity entity)
485     {
486         assert(valid(entity));
487         auto s = assure!T();
488         assert(s);
489         return s.contains(entity);
490 
491     }
492 
493     Storage!(T)* assure(T)()
494     {
495         auto type_id = type_id!T;
496 
497         if(components.has(type_id))
498         {
499             auto ptr = components.get(type_id);
500             return cast(Storage!(T)*) ptr;
501         }
502 
503         auto comp_set = Storage!(T).createPtr(allocator);
504         auto comp_set_ptr = cast(size_t)(comp_set);
505         components.set(type_id, comp_set_ptr);
506         return comp_set;
507     }
508 
509     auto view(Includes)()
510     {
511         return view!(Includes, Excludes!());
512     }
513 
514     auto view(Includes, Excludes)()
515     {
516         static if (Includes.args.length == 1 && Excludes.args.length == 0)
517         {
518             auto storage = assure!(Includes.args[0]);
519             return BasicView!(Includes.args[0]).create( storage );
520         }
521         else
522         {
523 
524             ulong[Includes.args.length] includes_arr;
525             static foreach (i, T; Includes.args)
526             {
527                 assure!(T)();
528                 includes_arr[i] = type_id!(T);
529             }
530             ulong[Excludes.args.length] excludes_arr;
531             static foreach (i, T; Excludes.args)
532             {
533                 assure!(T)();
534                 excludes_arr[i] = type_id!(T);
535             }
536             return MultiView!(Includes.args.length, Excludes.args.length).create(&this, includes_arr, excludes_arr);
537         }
538     }
539 }
540 struct Includes(Args...) { alias args = Args; }
541 struct Excludes(Args...) { alias args = Args; }
542 
543 struct BasicView(T)
544 {
545     Storage!(T)* storage;
546 
547     static BasicView create(Storage!(T)* s)
548     {
549         return BasicView(s);
550     }
551 
552     T* get(T)(Entity entity)
553     {
554         return storage.get(entity);
555     }
556 
557     int opApply(scope int delegate(Entity) dg)
558     {
559         // TODO: should be reverse iteration
560 
561         int result = 0;
562     
563         foreach (Entity item; storage.set.dense)
564         {
565             result = dg(item);
566             if (result)
567                 break;
568         }
569         return result;
570     }
571 }
572 
573 struct MultiView(size_t n_includes, size_t n_excludes)
574 {
575     Registry* registry;
576     ulong[n_includes] type_ids;
577     ulong[n_excludes] exclude_type_ids;
578 
579     static MultiView create(Registry* reg, ulong[n_includes] includes, ulong[n_excludes] excludes)
580     {
581         return MultiView(reg, includes, excludes);
582     }
583 
584     T* get(T)(Entity entity)
585     {
586         return registry.assure!(T).get(entity);
587     }
588     
589     bool valid(T)()
590     {
591         auto t = type_id!T;
592         bool v = false;
593         foreach (tid; type_ids)
594         {
595             if (t == tid)
596             {
597                 v = true;
598             }
599         }
600 
601         foreach (tid; exclude_type_ids)
602         {
603             if(t == tid)
604             {
605                 v = false;
606                 break;
607             }
608         }
609         return v;
610     }
611 
612     void sort()
613     {
614         size_t[n_includes] sub_items;
615         for(int i = 0; i < type_ids.length; i++)
616         {
617             auto ptr = registry.components.get(type_ids[i]);
618             auto storage = cast(Storage!(ubyte)*) ptr;
619             sub_items[i] = storage.len();
620         }
621 
622         sortSub(sub_items[0 .. $], type_ids[ 0 .. $], (size_t a, size_t b) {
623             return a < b;
624         });
625     }
626 
627     int opApply(scope int delegate(Entity) dg)
628     {
629         sort();
630 
631         auto ptr = registry.components.get(type_ids[0]);
632         auto entities = (cast(Storage!(ubyte)*) ptr).set.dense;
633 
634         size_t size = entities.length;
635         int result = 0;
636 
637         for (size_t i = size; i-- > 0;)
638         {
639             auto entity = entities.get(i);
640             auto valid = true;
641 
642             foreach(tid; type_ids)
643             {
644                 auto sptr = registry.components.get(tid);
645                 auto has = (cast(Storage!(ubyte)*) sptr).contains(entity);
646                 if(!has) 
647                 {
648                     valid = false;
649                     goto keep;
650                 }
651             }
652 
653             foreach(tid; exclude_type_ids)
654             {
655                 auto sptr = registry.components.get(tid);
656                 auto has = (cast(Storage!(ubyte)*) sptr).contains(entity);
657                 if(has) 
658                 {
659                     valid = false;
660                     goto keep;
661                 }
662             }
663 
664             keep:
665 
666             if(valid)
667                 result = dg(entity);
668         }
669 
670         //foreach (item; array)
671         //{
672         //    result = dg(item);
673         //    if (result)
674         //        break;
675         //}
676     
677         return result;
678     }
679 }
680 
681 template type_id(alias type)
682 {
683     enum ulong type_id = hashStringFnv(__traits(identifier, type));
684 
685     ulong hashStringFnv(string str)
686     {
687         enum ulong FNV_64_PRIME = 0x100000001b3UL;
688         ulong value;
689         foreach (c; str)
690         {
691             value *= FNV_64_PRIME;
692             value ^= c;
693         }
694         return value;
695     }
696 }
697 
698 void sortSub(T1, T2)(T1[] items, T2[] sub_items, scope bool delegate(T1, T2) lessThan)
699 {
700     size_t i = 1;
701     while(i < items.length)
702     {
703         auto x = items[i];
704         auto y = sub_items[i];
705         size_t j = i;
706         while(j > 0 && lessThan(x, items[j - 1]))
707         {
708             items[j] = items[j - 1];
709             sub_items[j] = sub_items[j - 1];
710 
711             j -= 1;
712         }
713         items[j] = x;
714         sub_items[j] = y;
715 
716         i += 1;
717     }
718 }
719 
720 
721 @("ecs")
722 unittest
723 {
724     import std.stdio: writeln;
725 
726     struct Pos
727     {
728         int x, y;
729     }
730 
731     struct Empty{}
732     struct Pos01{float x, y;}
733     struct Pos02{float x, y;}
734     struct Pos03{float x, y;}
735     struct Pos04{float x, y;}
736     struct Pos05{float x, y;}
737 
738     auto registry = Registry.create(MALLOCATOR.ptr);
739     for(int i = 0; i < 10_000; i++)
740     {
741         Entity e = registry.create_entity();
742         //writeln("ENTITY: ",e);
743         assert(registry.valid(e));
744 
745         registry.add(e,Pos01(5,6));
746         registry.add(e,Pos02(5,6));
747         registry.add(e,Pos03(5,6));
748         registry.add(e,Pos04(5,6));
749         registry.add(e,Pos05(5,6));
750 
751         if(i == 20) registry.add(e, Empty());
752 
753         assert(registry.has!(Pos01)(e));
754         assert(registry.has!(Pos02)(e));
755         assert(registry.has!(Pos03)(e));
756         assert(registry.has!(Pos04)(e));
757         assert(registry.has!(Pos05)(e));
758     }
759 
760     auto viewSolo = registry.view!(Includes!(Pos01));
761     auto viewMulti = registry.view!(Includes!(Pos01, Pos05));
762     auto viewExclude = registry.view!(Includes!(Pos01), Excludes!(Empty));
763 
764     foreach(e; viewSolo)
765     {
766         assert(registry.valid(e));
767         assert(viewSolo.get!(Pos01)(e));
768     }
769 
770     foreach(e; viewMulti)
771     {
772         assert(registry.valid(e));
773         assert(viewMulti.get!(Pos01)(e));
774         assert(viewMulti.get!(Pos05)(e));
775     }
776     foreach(e; viewExclude)
777     {
778         assert(registry.valid(e));
779         assert(viewExclude.get!(Pos01)(e));
780         assert(!viewExclude.valid!(Empty));
781     }
782 }