1 module mutils.entity;
2 
3 import std.algorithm : clamp, max, min;
4 import std.format : format;
5 import std.meta;
6 import std.traits;
7 
8 import mutils.container.buckets_chain;
9 import mutils.container.hash_map;
10 import mutils.container.string_intern;
11 import mutils.time : useconds;
12 import mutils.container.hash_map_tow_way;
13 
14 /**
15  * EntityId No Reference
16  * Struct representing EntityId but without compile time information of EntityManager
17  * Used to bypass forward reference problems
18  **/
19 struct EntityIdNR {
20 	@disable this(this);
21 
22 	private StringIntern name;
23 	private StableId id;
24 	StringIntern triggerEventOnDestroy;
25 	bool doSerialization = true;
26 	int type = int.max;
27 
28 	StableId stableIdNoAutoAdd() {
29 		return id;
30 	}
31 }
32 
33 bool hasComponent(Entity, Components...)() {
34 	bool has = true;
35 	foreach (Component; Components) {
36 		enum componentNum = staticIndexOf!(Component, Fields!Entity);
37 		has = has & (componentNum != -1);
38 	}
39 	return has;
40 }
41 
42 Component* getComponent(Component, Entity)(ref Entity ent) if (!isPointer!Entity) {
43 	enum componentNum = staticIndexOf!(Component, Fields!Entity);
44 	static assert(componentNum != -1, "Entity don't have this component.");
45 	return &ent.tupleof[componentNum];
46 }
47 
48 Component* getComponent(Component, Entity)(Entity* ent) {
49 	enum componentNum = staticIndexOf!(Component, Fields!Entity);
50 	static assert(componentNum != -1, "Entity don't have this component.");
51 	return &ent.tupleof[componentNum];
52 }
53 
54 struct StableId {
55 	private @("noserialize") ulong id;
56 }
57 
58 struct EntityManager(ENTS) {
59 	alias Entities = ENTS.Entities;
60 	alias FromEntities = Entities;
61 	alias UniqueComponents = NoDuplicates!(staticMap!(Fields, Entities));
62 	template EntitiesWithComponents(Components...) {
63 		template EntityHasComponents(EEE) {
64 			alias EntityHasComponents = hasComponent!(EEE, Components);
65 		}
66 
67 		alias EntitiesWithComponents = Filter!(EntityHasComponents, FromEntities);
68 	}
69 
70 	//Enum elements are well defined, 0->Entities[0], 1->Entities[1],
71 	//Enum EntityEnumM {...} // form mixin
72 	mixin(createEnumCode());
73 	alias EntityEnum = EntityEnumM; //Alias for autocompletion
74 
75 	enum memoryDtId = 32;
76 
77 	private __gshared static HashMapTwoWay!(StringIntern, EntityId*) nameIdMap;
78 	private __gshared static HashMapTwoWay!(StableId, EntityId*) stableIdEntityIdMap;
79 	private __gshared static ulong lastStableId = 0;
80 
81 	// Keep this struct in sync with  EntityIdNR
82 	static struct EntityId {
83 		@disable this(this);
84 
85 		private StringIntern entityName;
86 		private StableId id;
87 		StringIntern triggerEventOnDestroy;
88 		bool doSerialization = true;
89 		EntityEnum type = EntityEnum.none;
90 
91 		StableId stableId() {
92 			if (id.id != 0) {
93 				return id;
94 			}
95 			lastStableId++;
96 			id = StableId(lastStableId);
97 			stableIdEntityIdMap.add(id, &this);
98 			return id;
99 		}
100 
101 		StableId stableIdNoAutoAdd() {
102 			return id;
103 		}
104 
105 		StringIntern name() {
106 			return entityName;
107 		}
108 
109 		void name(StringIntern newName) {
110 			if (newName == entityName) {
111 				return;
112 			}
113 			if (newName.length != 0) {
114 				EntityId* otherEnt = nameIdMap.get(newName, null);
115 				if (otherEnt !is null) {
116 					otherEnt.entityName = StringIntern();
117 					//nameIdMap.remove(newName);
118 				}
119 			}
120 			entityName = newName;
121 			nameIdMap.add(newName, &this);
122 		}
123 
124 		auto get(EntityType)() {
125 			enum int entityEnumIndex = staticIndexOf!(EntityType, FromEntities);
126 			static assert(entityEnumIndex != -1, "There is no entity like: " ~ EntityType.stringof);
127 
128 			assert(type == entityEnumIndex);
129 			return cast(EntityType*)(cast(void*)&this + memoryDtId);
130 		}
131 
132 		Return apply(alias fun, Return = void)() {
133 			final switch (type) {
134 				foreach (i, Ent; Entities) {
135 			case cast(EntityEnum) i:
136 					Ent* el = get!Ent;
137 					if (is(Return == void)) {
138 						fun(el);
139 						break;
140 					} else {
141 						return fun(el);
142 					}
143 				}
144 			}
145 			assert(0);
146 		}
147 
148 		Component* getComponent(Component)() {
149 			static assert(staticIndexOf!(Component, UniqueComponents) != -1,
150 					"No entity has such component");
151 			switch (type) {
152 				foreach (uint i, Entity; Entities) {
153 					enum componentNum = staticIndexOf!(Component, Fields!Entity);
154 					static if (componentNum != -1) {
155 			case cast(EntityEnumM) i:
156 						Entity* el = cast(Entity*)(cast(void*)&this + memoryDtId); // Inline get!Entity for debug performance
157 						return &el.tupleof[componentNum];
158 					}
159 				}
160 			default:
161 				break;
162 			}
163 
164 			assert(0, "This entity does not have this component.");
165 		}
166 
167 		auto hasComponent(Components...)() {
168 			foreach (C; Components) {
169 				static assert(staticIndexOf!(C, UniqueComponents) != -1,
170 						"No entity has such component");
171 			}
172 			switch (type) {
173 				foreach (uint i, Entity; Entities) {
174 			case cast(EntityEnumM) i:
175 					enum has = mutils.entity.hasComponent!(Entity, Components);
176 					return has;
177 				}
178 			default:
179 				break;
180 			}
181 
182 			assert(0, "There is no entity represented by this EntityId enum."); //TODO log
183 			//return false;
184 		}
185 
186 	}
187 
188 	static struct EntityData(Ent) {
189 		EntityId entityId;
190 		Ent entity;
191 		static assert(entity.offsetof == memoryDtId);
192 
193 		alias entity this;
194 	}
195 
196 	template getEntityContainer(T) {
197 		alias getEntityContainer = BucketsChain!(EntityData!(T), 64, false);
198 	}
199 
200 	alias EntityContainers = staticMap!(getEntityContainer, Entities);
201 	EntityContainers entityContainers;
202 
203 	// Check compile time Entites requirements
204 	void checkEntities() {
205 		foreach (Entity; Entities) {
206 			alias Components = Fields!Entity;
207 			// No duplicate components
208 			static assert(Components.length == NoDuplicates!(Components)
209 					.length, "Entities should have unique components.");
210 		}
211 	}
212 
213 	@disable this(this);
214 
215 	void initialize() {
216 		foreach (Comp; UniqueComponents) {
217 			static if (hasStaticMember!(Comp, "staticInitialize")) {
218 				Comp.staticInitialize();
219 			}
220 		}
221 	}
222 
223 	void destroy() {
224 		foreach (Comp; UniqueComponents) {
225 			static if (hasStaticMember!(Comp, "staticDestroy")) {
226 				Comp.staticDestroy();
227 			}
228 		}
229 	}
230 
231 	size_t length() {
232 		size_t len;
233 		foreach (ref con; entityContainers) {
234 			len += con.length;
235 		}
236 		return len;
237 	}
238 
239 	void clear() {
240 		foreach (ref con; entityContainers) {
241 			con.clear();
242 		}
243 		stableIdEntityIdMap.clear();
244 		nameIdMap.clear();
245 	}
246 
247 	ref auto getContainer(EntityType)() {
248 		enum int entityEnumIndex = staticIndexOf!(EntityType, FromEntities);
249 		static assert(entityEnumIndex != -1, "There is no entity like: " ~ EntityType.stringof);
250 		return entityContainers[entityEnumIndex];
251 	}
252 
253 	void update() {
254 		import mutils.benchmark;
255 
256 		auto timeThis = TimeThis.time();
257 		foreach (i, ref con; entityContainers) {
258 			foreach (ref con.ElementType el; con) {
259 				el.update();
260 			}
261 		}
262 
263 		foreach (i, ref con; entityContainers) {
264 			alias EntityType = typeof(con.ElementType.entity);
265 			alias TFields = Fields!EntityType;
266 			foreach (Type; TFields) {
267 				static if (hasMember!(Type, "updateTimely")) {
268 					updateTimely!(Type)(con);
269 				}
270 			}
271 
272 		}
273 
274 	}
275 
276 	void updateTimely(Component, Container)(ref Container container) {
277 		static assert(hasMember!(Component, "updateTimely"));
278 		alias Entity = typeof(Container.ElementType.entity);
279 
280 		static size_t startTime = 0;
281 		static size_t lastIndex = 0;
282 		static size_t lastUnitsPerFrame = 100;
283 		//static size_t sumUnitsOfWork=0;
284 
285 		if (startTime == 0) {
286 			startTime = useconds();
287 		}
288 
289 		size_t currentWork;
290 		auto range = getRange!(Entity)(lastIndex, container.length);
291 		foreach (ref Entity ent; range) {
292 			Component* comp = ent.getComponent!Component;
293 			currentWork += comp.updateTimely(ent);
294 			lastIndex += 1;
295 			if (currentWork > lastUnitsPerFrame) {
296 				break;
297 			}
298 		}
299 		//sumUnitsOfWork+=currentWork;
300 		if (lastIndex < container.length || startTime > useconds()) {
301 			return;
302 		}
303 		size_t endTime = useconds();
304 		size_t dt = endTime - startTime;
305 
306 		startTime = endTime + Component.updateTimelyPeriod;
307 
308 		float mul = cast(float) Component.updateTimelyPeriod / dt;
309 		mul = (mul - 1) * 0.5 + 1;
310 
311 		lastUnitsPerFrame = cast(size_t)(lastUnitsPerFrame / mul);
312 		lastUnitsPerFrame = max(10, lastUnitsPerFrame);
313 
314 		//sumUnitsOfWork=0;
315 		lastIndex = 0;
316 
317 	}
318 
319 	// Adds enitiy without calling initialize on it, the user has to do it himself
320 	EntityType* addNoInitialize(EntityType, Components...)(Components components) {
321 		EntityData!(EntityType)* entD = getContainer!(EntityType).add();
322 		entD.entityId.type = getEnum!EntityType;
323 
324 		foreach (ref comp; components) {
325 			auto entCmp = getComponent!(typeof(comp))(entD.entity);
326 			*entCmp = comp;
327 		}
328 
329 		return &entD.entity;
330 	}
331 
332 	EntityType* add(EntityType, Components...)(Components components) {
333 		EntityType* ent = addNoInitialize!(EntityType)(components);
334 		ent.initialize();
335 		return ent;
336 	}
337 
338 	void remove(EntityType)(EntityType* entity) {
339 		EntityId* entId = entityToEntityId(entity);
340 		entity.destroy();
341 
342 		if (entId.id.id != 0) {
343 			stableIdEntityIdMap.remove(entId);
344 		} else {
345 			assert(stableIdEntityIdMap.get(entId, StableId()) == StableId());
346 		}
347 
348 		if (entId.entityName != StringIntern()) {
349 			nameIdMap.remove(entId);
350 		} else {
351 			assert(nameIdMap.get(entId, StringIntern()) == StringIntern());
352 
353 		}
354 		getContainer!(EntityType).remove(
355 				cast(EntityData!(EntityType)*)(cast(void*) entity - memoryDtId));
356 
357 	}
358 
359 	void remove(EntityId* entityId) {
360 		foreach (i, Entity; Entities) {
361 			if (entityId.type == i) {
362 				Entity* ent = entityId.get!Entity;
363 				remove(ent);
364 				return;
365 			}
366 		}
367 		assert(0);
368 
369 	}
370 
371 	// Based on pointer of component checks its base type
372 	EntityId* getEntityFromComponent(Component)(ref Component c) {
373 		alias EntsWithComp = EntitiesWithComponents!(Component);
374 		static assert(EntsWithComp.length != 0, "There are no entities with this component.");
375 
376 		foreach (Entity; EntsWithComp) {
377 			auto container = &getContainer!(Entity)();
378 			foreach (ref bucket; container.buckets[]) {
379 				if (!bucket.isIn(cast(container.ElementType*)&c)) {
380 					continue;
381 				}
382 				enum componentNum = staticIndexOf!(Component, Fields!Entity);
383 				Entity el;
384 				enum ptrDt = el.tupleof[componentNum].offsetof;
385 				Entity* ent = cast(Entity*)(cast(void*)&c - ptrDt);
386 				return entityToEntityId(ent);
387 			}
388 		}
389 		assert(0);
390 	}
391 
392 	// When (id == 0 && makeDefault !is null ) new id is assigned and Entity is created by makeDefault function
393 	EntityId* getEntityByStableId(ref StableId id, EntityId* function() makeDefault = null) {
394 		assert(id.id <= lastStableId);
395 		EntityId* ent = stableIdEntityIdMap.get(id, null);
396 
397 		if (ent == null && makeDefault !is null) {
398 			ent = makeDefault();
399 			id = ent.stableId;
400 		}
401 		return ent;
402 	}
403 
404 	EntityId* getEntityByName(StringIntern name) {
405 		EntityId* ent = nameIdMap.get(name, null);
406 		return ent;
407 	}
408 
409 	void removeByStableId(StableId id) {
410 		if (id.id == 0) {
411 			return;
412 		}
413 		EntityId* ent = stableIdEntityIdMap.get(id, null);
414 		if (ent !is null) {
415 			remove(ent);
416 		}
417 	}
418 
419 	auto getRange(Entity)(size_t start, size_t end) {
420 		auto container = &getContainer!Entity();
421 		assert(end <= container.length);
422 		return Range!(Entity)(container, start, end);
423 	}
424 
425 	struct Range(Entity) {
426 		getEntityContainer!Entity* container;
427 		size_t start;
428 		size_t end;
429 
430 		size_t length() {
431 			return end - start;
432 		}
433 
434 		int opApply(Dg)(scope Dg dg) {
435 			int result;
436 			// Can be improved: skip whole containers
437 			int i;
438 			foreach (ref EntityData!(Entity) el; *container) {
439 				scope (exit)
440 					i++;
441 				if (i < start) {
442 					continue;
443 				}
444 				if (i >= end) {
445 					break;
446 				}
447 				result = dg(el.entity);
448 				if (result)
449 					break;
450 			}
451 			return result;
452 		}
453 
454 	}
455 
456 	static EntityId* entityToEntityId(EntityType)(EntityType* el) {
457 		static assert(!isPointer!(EntityType),
458 				"Wrong type passed. Maybe pointer to pointer was passed?");
459 		static assert(staticIndexOf!(EntityType, FromEntities) != -1,
460 				"There is no entity like: " ~ EntityType.stringof);
461 		EntityId* id = cast(EntityId*)(cast(void*) el - memoryDtId);
462 		assert(id.type < Entities.length);
463 		return id;
464 	}
465 
466 	static string getEntityEnumName(EntityEnum type) {
467 		foreach (i, Entity; Entities) {
468 			if (type == i)
469 				return Entity.stringof;
470 		}
471 		return "!unknown";
472 	}
473 	/////////////////////////
474 	/////// Enum code  //////
475 	/////////////////////////
476 	static EntityEnum getEnum(EntityType)() {
477 		enum int entityEnumIndex = staticIndexOf!(EntityType, FromEntities);
478 		static assert(entityEnumIndex != -1, "There is no entity like: " ~ EntityType.stringof);
479 		return cast(EntityEnum) entityEnumIndex;
480 
481 	}
482 
483 	// Order if enum is important, indexing of objects is made out of it
484 	static string createEnumCode() {
485 		string code = "enum EntityEnumM:int{";
486 		foreach (i, Entity; Entities) {
487 			code ~= format("_%d=%d,", i, i);
488 		}
489 		code ~= format("none");
490 		code ~= "}";
491 		return code;
492 	}
493 
494 }
495 
496 unittest {
497 	static int entitiesNum = 0;
498 
499 	static struct EntityTurrent {
500 		int a;
501 
502 		void update() {
503 		}
504 
505 		void initialize() {
506 			entitiesNum++;
507 		}
508 
509 		void destroy() {
510 			entitiesNum--;
511 		}
512 	}
513 
514 	static struct EntityTurrent2 {
515 		int a;
516 
517 		void update() {
518 		}
519 
520 		void initialize() {
521 			entitiesNum++;
522 		}
523 
524 		void destroy() {
525 			entitiesNum--;
526 		}
527 	}
528 
529 	static struct EntitySomething {
530 		int a;
531 
532 		void update() {
533 
534 		}
535 
536 		void initialize() {
537 			entitiesNum++;
538 		}
539 
540 		void destroy() {
541 			entitiesNum--;
542 		}
543 	}
544 
545 	static struct ENTS {
546 		alias Entities = AliasSeq!(EntityTurrent, EntityTurrent2, EntitySomething);
547 	}
548 
549 	alias TetstEntityManager = EntityManager!(ENTS);
550 
551 	TetstEntityManager entitiesManager;
552 	entitiesManager.initialize;
553 	assert(entitiesManager.getContainer!(EntityTurrent).length == 0);
554 	assert(entitiesManager.getContainer!(EntityTurrent2).length == 0);
555 
556 	EntityTurrent* ret1 = entitiesManager.add!(EntityTurrent)(3);
557 	EntityTurrent2* ret2 = entitiesManager.add!(EntityTurrent2)();
558 
559 	assert(*ret1.getComponent!int == 3);
560 	assert(*ret2.getComponent!int == 0);
561 
562 	assert(entitiesManager.getContainer!(EntityTurrent).length == 1);
563 	assert(entitiesManager.getContainer!(EntityTurrent2).length == 1);
564 	assert(entitiesManager.getEntityFromComponent(ret1.a)
565 			.type == entitiesManager.getEnum!EntityTurrent);
566 	assert(entitiesNum == 2);
567 
568 	entitiesManager.remove(ret1);
569 	entitiesManager.remove(ret2);
570 
571 	assert(entitiesManager.getContainer!(EntityTurrent).length == 0);
572 	assert(entitiesManager.getContainer!(EntityTurrent2).length == 0);
573 	assert(entitiesNum == 0);
574 }