1 module datadriven.api;
2 
3 import core.time;
4 import std.conv : to;
5 import std.typetuple;
6 import std.random;
7 import std.traits;
8 
9 alias EntityId = ulong;
10 
11 // tests if CS is a Components storage of components C
12 template isComponentStorage(CS, C)
13 {
14 	enum bool isComponentStorage = is(typeof(
15     (inout int = 0)
16     {
17         CS cs = CS.init;
18         C c = C.init;
19         EntityId eid = EntityId.init;
20 
21         cs.add(eid, c); // Can add component
22         cs.remove(eid); // Can remove component
23         cs.removeAll(); // Can remove all
24         C* cptr = cs.get(eid); // Can get component pointer
25 
26         foreach(pair; cs.byKeyValue)
27         {
28         	eid = pair.key;
29         	c = pair.value = c;
30         }
31     }));
32 }
33 
34 unittest
35 {
36 	struct A {}
37 	struct B
38 	{
39 		void add(EntityId, int);
40 		void remove(EntityId);
41 		int* get(EntityId);
42 		void removeAll();
43 		auto byKeyValue() @property {
44 			return (int[EntityId]).init.byKeyValue;
45 		}
46 	}
47     static assert(!isComponentStorage!(A, int));
48     static assert( isComponentStorage!(B, int));
49 }
50 
51 auto componentQuery(ComponentStorages...)(ComponentStorages storages)
52 {
53 	return ComponentQuery!(ComponentStorages)(storages);
54 }
55 
56 // Can be looped by Row.
57 // struct Row {EntityId eid; Comp1* comp1; ... CompN* compN;}
58 // Returned rows are for entities that have all the components.
59 struct ComponentQuery(ComponentStorages...)
60 {
61 	//pragma(msg, genComponentStorages!(ComponentStorages)());
62 	mixin(genComponentStorages!(ComponentStorages));
63 
64 	static struct Row
65 	{
66 		mixin(genRowDefinition!ComponentStorages);
67 	}
68 
69 	int opApply(scope int delegate(Row) dg)
70     {
71         static struct StorageLength
72         {
73         	size_t index;
74         	size_t length;
75         	int opCmp(StorageLength other) { return cast(int)(cast(long)length - cast(long)other.length); }
76         }
77 
78         StorageLength[ComponentStorages.length] lengths;
79 
80         foreach(size_t i, cs; ComponentStorages)
81         {
82         	lengths[i].index = i;
83         	lengths[i].length = mixin(genComponentStorageName!(cs) ~ ".length");
84         }
85 
86 		import std.algorithm : sort;
87         sort(lengths[]);
88 
89         // Iteration variables
90         int result = 0;
91     	Row row;
92 
93         mixin(genComponentIterationCode!(ComponentStorages));
94 
95         return result;
96     }
97 }
98 
99 
100 ///////////////////////////////////////////////////////////////////////////////
101 // QUERY CODE GEN
102 
103 string genComponentIterationCode(ComponentStorages...)()
104 {
105 	string result;
106 
107 	result ~= "switch(lengths[0].index) {\n";
108 
109 	// gen code for each case when table has fewest items
110 	foreach(i, cs1; ComponentStorages)
111 	{
112 		string istr = i.to!string;
113 		result ~=  "case "~istr~":\n";
114 		//result ~=  "writeln("~istr~");\n";
115 
116 		result ~=  "\t\tforeach(pair; " ~ genComponentStorageName!(ComponentStorages[i]) ~ ".byKeyValue) {\n";
117 		// gen component selection for shortest table
118 	    result ~=  "\t\t\trow.eid = pair.key;\n";
119 	    result ~=  "\t\t\trow." ~ genComponentName!(ComponentStorages[i]) ~" = &(pair.value());\n\n";
120 
121 		// gen component selection for other tables
122 	    foreach(j, cs2; ComponentStorages)
123 	    if (i != j)
124 		{
125 			// gen component selection for other tables via random access lookup
126 			string jstr = j.to!string;
127 		    result ~= "\t\t\tauto component"~ jstr ~" = " ~ genComponentStorageName!(ComponentStorages[j]) ~ ".get(pair.key);\n";
128 	        result ~= "\t\t\tif (component"~ jstr ~" is null) continue;\n";
129 	        result ~= "\t\t\trow." ~ genComponentName!(ComponentStorages[j]) ~ " = component"~ jstr ~";\n\n";
130         }
131 
132         // call foreach body passing current row
133 		result ~=  "\t\t\tresult = dg(row);\n";
134 	    result ~=  "\t\t\tif (result)\n";
135 	    result ~=  "\t\t\t\tbreak;\n";
136 
137 		result ~=  "\t\t}\n"; // end foreach
138 		result ~=  "\t\tbreak;\n";
139 	}
140 
141     result ~= "\tdefault: assert(0);\n";
142 	result ~= "}\n"; // end switch
143 
144 	return result;
145 }
146 
147 string genComponentStorages(ComponentStorages...)()
148 {
149 	string result;
150 
151 	foreach(i, cs; ComponentStorages)
152 	{
153 		result ~= "private ComponentStorages[" ~ i.to!string ~ "] " ~
154 			genComponentStorageName!cs ~ ";\n";
155 	}
156 
157 	return result;
158 }
159 
160 string genComponentStorageName(ComponentStorage)()
161 {
162 	return "_" ~ genComponentName!ComponentStorage ~ "Storage";
163 }
164 
165 alias componentType(ComponentStorage) = TemplateArgsOf!ComponentStorage[0];
166 
167 string genComponentName(ComponentStorage)()
168 {
169 	alias C = TemplateArgsOf!ComponentStorage[0];
170 	string ident = Unqual!C.stringof;
171 	import std.uni : toLower;
172 	return toLower(ident[0]).to!string ~ ident[1..$];
173 }
174 
175 string genRowDefinition(ComponentStorages...)()
176 {
177 	string result;
178 
179 	result ~= "EntityId eid;\n";
180 
181 	foreach(i, ComponentStorage; ComponentStorages)
182 	{
183 		result ~= "TemplateArgsOf!(ComponentStorages["~ i.to!string ~"])[0] * "~ genComponentName!ComponentStorage ~";\n";
184 	}
185 
186 	return result;
187 }