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