1 module mutils.entity; 2 3 import std.format; 4 import std.stdio; 5 import std.traits; 6 import std.meta; 7 8 import mutils.container.buckets_chain; 9 10 /** 11 * EntityId No Reference 12 * Struct representing EntityId but without compile time information of EntityManager 13 * Used to bypass forward reference problems 14 **/ 15 struct EntityIdNR{ 16 @disable this(this); 17 uint id; 18 uint type; 19 } 20 21 bool hasComponent(Entity, Components...)(){ 22 bool has=true; 23 foreach(Component;Components){ 24 enum componentNum=staticIndexOf!(Component,Fields!Entity); 25 has = has & (componentNum!=-1); 26 } 27 return has; 28 } 29 30 31 Component* getComponent(Component, Entity)(ref Entity ent) 32 if(!isPointer!Entity) 33 { 34 enum componentNum=staticIndexOf!(Component, Fields!Entity); 35 static assert(componentNum!=-1, "Entity don't have this component."); 36 return &ent.tupleof[componentNum]; 37 } 38 39 Component* getComponent(Component, Entity)(Entity* ent){ 40 enum componentNum=staticIndexOf!(Component, Fields!Entity); 41 static assert(componentNum!=-1, "Entity don't have this component."); 42 return &ent.tupleof[componentNum]; 43 } 44 45 struct EntityManager(Entities...){ 46 alias FromEntities=Entities; 47 alias UniqueComponents=NoDuplicates!(staticMap!(Fields, Entities)); 48 template EntitiesWithComponents(Components...){ 49 template EntityHasComponents(EEE){ 50 alias EntityHasComponents=hasComponent!(EEE, Components); 51 } 52 alias EntitiesWithComponents= Filter!(EntityHasComponents, FromEntities); 53 } 54 55 //Enum elements are well defined, 0->Entities[0], 1->Entities[1], 56 //Enum EntityEnumM {...} // form mixin 57 mixin(createEnumCode()); 58 alias EntityEnum=EntityEnumM;//Alias for autocompletion 59 uint lastId=1; 60 61 static struct EntityId{ 62 @disable this(this); 63 uint id; 64 EntityEnum type; 65 66 auto get(EntityType)(){ 67 foreach(i,Ent;Entities){ 68 static if(is(EntityType==Ent)){ 69 assert(type==i); 70 return cast(Ent*)(cast(void*)&this+8); 71 } 72 } 73 assert(0); 74 } 75 76 Return apply(alias fun,Return=void)() { 77 final switch(type){ 78 foreach(i,Ent;Entities){ 79 case cast(EntityEnum)i: 80 Ent* el=get!Ent; 81 if(is(Return==void)){ 82 fun(el); 83 break; 84 }else{ 85 return fun(el); 86 } 87 } 88 } 89 assert(0); 90 } 91 92 Component* getComponent(Component)(){ 93 foreach(i,Entity;Entities){ 94 enum componentNum=staticIndexOf!(Component,Fields!Entity); 95 static if(componentNum!=-1){ 96 if(type==i){ 97 Entity* el=get!Entity; 98 return &el.tupleof[componentNum]; 99 } 100 } 101 } 102 assert(0); 103 } 104 105 auto hasComponent(Components...)(){ 106 foreach(i,Entity;Entities){ 107 if(type==i){ 108 return mutils.entity.hasComponent!(Entity, Components); 109 } 110 } 111 assert(0); 112 } 113 114 } 115 116 static struct EntityData(Ent){ 117 EntityId entityId; 118 Ent entity; 119 static assert(entity.offsetof==8); 120 121 alias entity this; 122 } 123 124 template getEntityContainer(T){ 125 alias getEntityContainer = BucketsChain!(EntityData!(T)); 126 } 127 128 alias EntityContainers=staticMap!(getEntityContainer,Entities); 129 EntityContainers entityContainers; 130 131 // Check compile time Entites requirements 132 void checkEntities(){ 133 foreach(Entity;Entities){ 134 alias Components=Fields!Entity; 135 // No duplicate components 136 static assert(Components.length==NoDuplicates!(Components).length, "Entities should have unique components."); 137 } 138 } 139 140 @disable this(this); 141 142 void initialize(){ 143 foreach(ref con;entityContainers){ 144 con.initialize; 145 } 146 147 foreach(Comp;UniqueComponents){ 148 static if(hasStaticMember!(Comp, "staticInitialize")){ 149 Comp.staticInitialize(); 150 } 151 } 152 } 153 154 void destroy(){ 155 foreach(Comp;UniqueComponents){ 156 static if(hasStaticMember!(Comp, "staticDestroy")){ 157 Comp.staticDestroy(); 158 } 159 } 160 } 161 162 size_t length(){ 163 size_t len; 164 foreach(ref con;entityContainers){ 165 len+=con.length; 166 } 167 return len; 168 } 169 170 void clear(){ 171 foreach(ref con;entityContainers){ 172 con.clear(); 173 } 174 } 175 176 ref auto getContainer(EntityType)(){ 177 foreach(i,ent;Entities){ 178 static if(is(EntityType==ent)){ 179 return entityContainers[i]; 180 } 181 } 182 assert(0); 183 } 184 185 void update(){ 186 foreach(i, ref con;entityContainers){ 187 foreach(ref con.ElementType el;con){ 188 el.update(); 189 } 190 191 } 192 } 193 194 // Adds enitiy without calling initialize on it, the user has to do it himself 195 EntityType* addNoInitialize(EntityType, Components...)(Components components){ 196 EntityData!(EntityType)* entD=getContainer!(EntityType).add(); 197 entD.entityId.id=lastId++; 198 entD.entityId.type=getEnum!EntityType; 199 200 foreach(ref comp;components){ 201 auto entCmp=getComponent!(typeof(comp))(entD.entity); 202 *entCmp=comp; 203 } 204 205 return &entD.entity; 206 } 207 208 EntityType* add(EntityType, Components...)(Components components){ 209 EntityType* ent=addNoInitialize!(EntityType)(components); 210 ent.initialize(); 211 return ent; 212 } 213 214 void remove(EntityType)(EntityType* entity){ 215 entity.destroy(); 216 getContainer!(EntityType).remove(cast(EntityData!(EntityType)*)(cast(void*)entity-8)); 217 } 218 219 void remove(EntityId* entityId){ 220 foreach(i,Entity;Entities){ 221 if(entityId.type==i){ 222 Entity* ent=entityId.get!Entity; 223 ent.destroy(); 224 getContainer!(Entity).remove(cast(EntityData!(Entity)*)(entityId)); 225 return; 226 } 227 } 228 assert(0); 229 230 } 231 232 233 auto allWith(Component)(){ 234 static struct ForeachStruct(T){ 235 T* mn; 236 int opApply(Dg)(scope Dg dg) 237 { 238 int result; 239 foreach(Entity;Entities){ 240 enum componentNum=staticIndexOf!(Component,Fields!Entity); 241 static if(componentNum!=-1){ 242 foreach(ref EntityData!(Entity) el;mn.getContainer!(Entity)()){ 243 result=dg(el.entityId); 244 if (result) 245 break; 246 } 247 } 248 } 249 return result; 250 } 251 } 252 ForeachStruct!(typeof(this)) tmp; 253 tmp.mn=&this; 254 return tmp; 255 } 256 257 // Based on pointer of component checks its base type 258 EntityId* getEntityFromComponent(Component)(ref Component c){ 259 alias EntsWithComp=EntitiesWithComponents!(Component); 260 static assert(EntsWithComp.length!=0, "There are no entities with this component."); 261 262 foreach(Entity; EntsWithComp){ 263 auto container=&getContainer!(Entity)(); 264 foreach(ref bucket; container.buckets[]){ 265 if(!bucket.isIn(cast(container.ElementType*)&c)){ 266 continue; 267 } 268 enum componentNum=staticIndexOf!(Component,Fields!Entity); 269 Entity el; 270 enum ptrDt=el.tupleof[componentNum].offsetof; 271 Entity* ent=cast(Entity*)(cast(void*)&c-ptrDt); 272 return entityToEntityId(ent); 273 } 274 } 275 assert(0); 276 } 277 278 279 auto getRange(Entity)(size_t start, size_t end){ 280 auto container=&getContainer!Entity(); 281 assert(end<=container.length); 282 return Range!(Entity)(container, start, end); 283 } 284 285 286 287 struct Range(Entity){ 288 getEntityContainer!Entity* container; 289 size_t start; 290 size_t end; 291 292 size_t length(){ 293 return end-start; 294 } 295 296 int opApply(Dg)(scope Dg dg){ 297 int result; 298 // Can be improved: skip whole containers 299 foreach(int i, ref EntityData!(Entity) el;*container){ 300 if(i<start){ 301 continue; 302 } 303 if(i>=end){ 304 break; 305 } 306 result=dg(el.entity); 307 if (result) 308 break; 309 } 310 return result; 311 } 312 313 } 314 315 static EntityId* entityToEntityId(EntityType)(EntityType* el){ 316 static assert(!isPointer!(EntityType), "Wrong type passed. Maybe pointer to pointer was passed?"); 317 static assert(staticIndexOf!(EntityType, FromEntities)!=-1, "There is no entity like: "~EntityType.stringof); 318 EntityId* id=cast(EntityId*)(cast(void*)el-8); 319 assert(id.type<Entities.length); 320 return id; 321 } 322 323 static string getEntityName(EntityEnum type){ 324 foreach(i,Entity;Entities){ 325 if(type==i) 326 return Entity.stringof; 327 } 328 return "!unknown"; 329 } 330 ///////////////////////// 331 /////// Enum code ////// 332 ///////////////////////// 333 static EntityEnum getEnum(T)(){ 334 foreach(i,Type;Entities){ 335 static if(is(Type==T)){ 336 return cast(EntityEnum)i; 337 } 338 } 339 } 340 341 // Order if enum is important, indexing of objects is made out of it 342 static string createEnumCode(){ 343 string code="enum EntityEnumM:uint{"; 344 foreach(i,Entity;Entities){ 345 code~=format("_%d=%d,",i,i); 346 } 347 code~="}"; 348 return code; 349 } 350 351 } 352 353 354 355 unittest{ 356 static int entitiesNum=0; 357 358 static struct EntityTurrent{ 359 int a; 360 361 void update(){} 362 363 void initialize(){ 364 entitiesNum++; 365 } 366 367 void destroy(){ 368 entitiesNum--; 369 } 370 } 371 static struct EntityTurrent2{ 372 int a; 373 374 void update(){} 375 376 void initialize(){ 377 entitiesNum++; 378 } 379 380 void destroy(){ 381 entitiesNum--; 382 } 383 } 384 static struct EntitySomething{ 385 int a; 386 387 void update(){ 388 389 } 390 391 void initialize(){ 392 entitiesNum++; 393 } 394 395 void destroy(){ 396 entitiesNum--; 397 } 398 } 399 400 401 alias TetstEntityManager=EntityManager!( 402 EntityTurrent, 403 EntityTurrent2, 404 EntitySomething 405 ); 406 407 TetstEntityManager entitiesManager; 408 entitiesManager.initialize; 409 assert(entitiesManager.getContainer!(EntityTurrent).length==0); 410 assert(entitiesManager.getContainer!(EntityTurrent2).length==0); 411 412 EntityTurrent* ret1=entitiesManager.add!(EntityTurrent)(3); 413 EntityTurrent2* ret2=entitiesManager.add!(EntityTurrent2)(); 414 415 assert(*ret1.getComponent!int==3); 416 assert(*ret2.getComponent!int==0); 417 418 assert(entitiesManager.getContainer!(EntityTurrent).length==1); 419 assert(entitiesManager.getContainer!(EntityTurrent2).length==1); 420 assert(entitiesManager.getEntityFromComponent(ret1.a).type==entitiesManager.getEnum!EntityTurrent); 421 assert(entitiesNum==2); 422 423 entitiesManager.remove(ret1); 424 entitiesManager.remove(ret2); 425 426 assert(entitiesManager.getContainer!(EntityTurrent).length==0); 427 assert(entitiesManager.getContainer!(EntityTurrent2).length==0); 428 assert(entitiesNum==0); 429 } 430