1 /** 2 Copyright: Copyright (c) 2016-2018 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module datadriven.query; 7 8 import std.conv : to; 9 import std.meta : Filter, staticMap; 10 import std.traits : TemplateArgsOf, Unqual; 11 12 import datadriven.api; 13 14 auto componentQuery(ComponentStorages...)(ComponentStorages storages) 15 { 16 return ComponentQuery!(ComponentStorages)(storages); 17 } 18 19 template StorageComponentTypePtr(T) 20 { 21 alias StorageComponentTypePtr = T.ComponentType*; 22 } 23 24 struct ComponentQuery(ComponentStorages...) 25 { 26 mixin(genComponentStorages!ComponentStorages); 27 mixin(genRowDefinition!ComponentStorages); 28 alias Components = staticMap!(StorageComponentTypePtr, Filter!(isAnyComponentStorage, ComponentStorages)); 29 30 // Sorts component storages by length. 31 // Then iterates by smallest storage to reduce number of lookups. 32 int opApply(scope int delegate(Row) dg) 33 { 34 static struct StorageLength 35 { 36 size_t index; 37 size_t length; 38 int opCmp(StorageLength other) { return cast(int)(cast(long)length - cast(long)other.length); } 39 } 40 41 StorageLength[ComponentStorages.length] lengths; 42 43 foreach(size_t i, cs; ComponentStorages) 44 { 45 lengths[i].index = i; 46 lengths[i].length = mixin(genComponentStorageName!(cs, i) ~ ".length"); 47 } 48 49 import std.algorithm : sort; 50 sort(lengths[]); 51 52 // Iteration variables 53 int result = 0; 54 Row row; 55 56 mixin(genComponentIterationCode!ComponentStorages); 57 58 return result; 59 } 60 61 // ditto 62 int opApply(scope int delegate(EntityId id, Components) dg) 63 { 64 int iterate(Row row) 65 { 66 if (auto res = dg(row.tupleof)) return res; 67 return 0; 68 } 69 70 return opApply(&iterate); 71 } 72 } 73 74 75 /////////////////////////////////////////////////////////////////////////////// 76 // QUERY CODE GEN 77 /////////////////////////////////////////////////////////////////////////////// 78 79 // // example 80 /////////////////////////////////////////////////////////////////////////////// 81 // genComponentIterationCode!(EntitySet, EntitySet, HashmapComponentStorage!Velocity) 82 /////////////////////////////////////////////////////////////////////////////// 83 // // yields 84 /* 85 switch(lengths[0].index) { 86 case 0: 87 foreach(key; _entity_set_0) { 88 row.id = key; 89 bool component_1 = _entity_set_1.get(key); 90 if (!component_1) continue; 91 auto component_2 = _velocity_2_storage.get(key); 92 if (component_2 is null) continue; 93 row.velocity_2 = component_2; 94 result = dg(row); 95 if (result) 96 break; 97 } 98 break; 99 case 1: 100 foreach(key; _entity_set_1) { 101 row.id = key; 102 bool component_0 = _entity_set_0.get(key); 103 if (!component_0) continue; 104 auto component_2 = _velocity_2_storage.get(key); 105 if (component_2 is null) continue; 106 row.velocity_2 = component_2; 107 result = dg(row); 108 if (result) 109 break; 110 } 111 break; 112 case 2: 113 foreach(key, value; _velocity_2_storage) { 114 row.id = key; 115 row.velocity_2 = &value; 116 bool component_0 = _entity_set_0.get(key); 117 if (!component_0) continue; 118 bool component_1 = _entity_set_1.get(key); 119 if (!component_1) continue; 120 result = dg(row); 121 if (result) 122 break; 123 } 124 break; 125 default: assert(0); 126 } 127 */ 128 // // as code inside ComponentQuery.opApply() 129 130 131 132 string genComponentIterationCode(ComponentStorages...)() 133 { 134 string result; 135 136 result ~= "switch(lengths[0].index) {\n"; 137 138 // gen code for each case when table has fewest items 139 foreach(i, CS_1; ComponentStorages) 140 { 141 string istr = i.to!string; 142 result ~= "\tcase "~istr~":\n"; 143 //result ~= "writeln("~istr~");\n"; 144 145 static if(isAnyComponentStorage!CS_1) 146 { 147 result ~= "\t\tforeach(key, ref value; " ~ genComponentStorageName!(CS_1, i) ~ ") {\n"; 148 // gen component selection for shortest table 149 result ~= "\t\t\trow.id = key;\n"; 150 result ~= "\t\t\trow." ~ genRowComponentName!(CS_1, i) ~" = &value;\n\n"; 151 } 152 else 153 { 154 result ~= "\t\tforeach(key; " ~ genComponentStorageName!(CS_1, i) ~ ") {\n"; 155 // gen component selection for shortest table 156 result ~= "\t\t\trow.id = key;\n\n"; 157 } 158 159 // gen component selection for other tables 160 foreach(j, CS_2; ComponentStorages) 161 if (i != j) 162 { 163 // gen component selection for other tables via random access lookup 164 static if(isAnyComponentStorage!CS_2) 165 { 166 string jstr = j.to!string; 167 result ~= "\t\t\tauto component_"~ jstr ~" = " ~ genComponentStorageName!(CS_2, j) ~ ".get(key);\n"; 168 result ~= "\t\t\tif (component_"~ jstr ~" is null) continue;\n"; 169 result ~= "\t\t\trow." ~ genRowComponentName!(CS_2, j) ~ " = component_"~ jstr ~";\n\n"; 170 } 171 else 172 { 173 string jstr = j.to!string; 174 result ~= "\t\t\tbool component_"~ jstr ~" = " ~ genComponentStorageName!(CS_2, j) ~ ".get(key);\n"; 175 result ~= "\t\t\tif (!component_"~ jstr ~") continue;\n\n"; 176 } 177 } 178 179 // call foreach body passing current row 180 result ~= "\t\t\tresult = dg(row);\n"; 181 result ~= "\t\t\tif (result)\n"; 182 result ~= "\t\t\t\tbreak;\n"; 183 184 result ~= "\t\t}\n"; // end foreach 185 result ~= "\t\tbreak;\n"; 186 } 187 188 result ~= "\tdefault: assert(0);\n"; 189 result ~= "}\n"; // end switch 190 191 return result; 192 } 193 194 // // example 195 /////////////////////////////////////////////////////////////////////////////// 196 // genComponentStorages!(HashmapComponentStorage!Transform, EntitySet, HashmapComponentStorage!Velocity); 197 /////////////////////////////////////////////////////////////////////////////// 198 // // yields 199 // private HashmapComponentStorage!Transform _transform_0_storage; 200 // private EntitySet _entity_set_1; 201 // private HashmapComponentStorage!Velocity _velocity_2_storage; 202 // // as fields inside ComponentQuery 203 204 string genComponentStorages(ComponentStorages...)() 205 { 206 string result; 207 208 foreach(i, cs; ComponentStorages) 209 { 210 result ~= "private ComponentStorages[" ~ i.to!string ~ "] " ~ 211 genComponentStorageName!(cs, i) ~ ";\n"; 212 } 213 214 return result; 215 } 216 217 string genComponentStorageName(ComponentStorage, uint i)() 218 { 219 static if(isAnyComponentStorage!ComponentStorage) 220 return "_" ~ genStorageComponentName!ComponentStorage ~ "_" ~ i.to!string ~ "_storage"; 221 else 222 return "_entity_set_" ~ i.to!string; 223 } 224 225 string genStorageComponentName(ComponentStorage)() 226 { 227 alias C = TemplateArgsOf!ComponentStorage[0]; 228 return genComponentName!C; 229 } 230 231 string genComponentName(Component)() 232 { 233 string ident = Unqual!Component.stringof; 234 import std.uni : toLower; 235 return toLower(ident[0]).to!string ~ ident[1..$]; 236 } 237 238 // // example 239 /////////////////////////////////////////////////////////////////////////////// 240 // genComponentStorages!(HashmapComponentStorage!Transform, EntitySet, HashmapComponentStorage!Velocity); 241 /////////////////////////////////////////////////////////////////////////////// 242 // // yields 243 // static struct Row 244 // { 245 // EntityId id; 246 // Transform* transform_0; 247 // Velocity* velocity_2; 248 // } 249 // // as type definition inside ComponentQuery 250 // // numbers start from zero. entity sets affect numbers. 251 // // Entity sets need no entries since they only affect which entities will be returned by query. 252 253 string genRowDefinition(ComponentStorages...)() 254 { 255 string result; 256 257 result = 258 "static struct Row\n"~ 259 "{\n"~ 260 "\tEntityId id;\n"; 261 262 foreach(i, CS; ComponentStorages) 263 { 264 static if (isAnyComponentStorage!CS) 265 { 266 result ~= "\tTemplateArgsOf!(ComponentStorages["~ i.to!string ~"])[0]* "~ 267 genRowComponentName!(CS, i) ~ ";\n"; 268 } 269 } 270 result ~= "}\n"; 271 272 return result; 273 } 274 275 string genRowComponentName(ComponentStorage, uint i)() 276 { 277 return genStorageComponentName!ComponentStorage ~ "_" ~ i.to!string; 278 }