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 }