1 /* 2 __ 3 / _| 4 __ _ _ _ _ __ ___ _ __ __ _ | |_ ___ ___ ___ 5 / _` | | | | '__/ _ \| '__/ _` | | _/ _ \/ __/ __| 6 | (_| | |_| | | | (_) | | | (_| | | || (_) \__ \__ \ 7 \__,_|\__,_|_| \___/|_| \__,_| |_| \___/|___/___/ 8 9 Copyright (C) 2019 Aurora Free Open Source Software. 10 Copyright (C) 2019 João Lourenço <joao@aurorafoss.org> 11 12 This file is part of the Aurora Free Open Source Software. This 13 organization promote free and open source software that you can 14 redistribute and/or modify under the terms of the GNU Lesser General 15 Public License Version 3 as published by the Free Software Foundation or 16 (at your option) any later version approved by the Aurora Free Open Source 17 Software Organization. The license is available in the package root path 18 as 'LICENSE' file. Please review the following information to ensure the 19 GNU Lesser General Public License version 3 requirements will be met: 20 https://www.gnu.org/licenses/lgpl.html . 21 22 Alternatively, this file may be used under the terms of the GNU General 23 Public License version 3 or later as published by the Free Software 24 Foundation. Please review the following information to ensure the GNU 25 General Public License requirements will be met: 26 https://www.gnu.org/licenses/gpl-3.0.html. 27 28 NOTE: All products, services or anything associated to trademarks and 29 service marks used or referenced on this file are the property of their 30 respective companies/owners or its subsidiaries. Other names and brands 31 may be claimed as the property of others. 32 33 For more info about intellectual property visit: aurorafoss.org or 34 directly send an email to: contact (at) aurorafoss.org . 35 */ 36 37 module aurorafw.entity.entitymanager; 38 39 import aurorafw.entity.entity; 40 import aurorafw.entity.icomponent; 41 import aurorafw.entity.componentmanager; 42 import aurorafw.entity.world; 43 44 version(unittest) import aurorafw.unit.assertion; 45 46 import std.range; 47 import std.exception; 48 49 50 class EntityManagerHandlingException : Exception 51 { 52 mixin basicExceptionCtors; 53 } 54 55 56 final class EntityManager 57 { 58 /** 59 * Create an entity 60 * 61 * Creates a new entity with an unique id 62 * 63 * Examples: 64 * -------------------- 65 * world.entity.create(); 66 * Entity e = world.entity.create(); 67 * -------------------- 68 */ 69 @safe pure 70 public Entity create() 71 { 72 Entity e = new Entity(this, !this.delEntities.empty ? popDeletedId : nextId++); 73 this.mEntities[e.id] = e; 74 75 return e; 76 } 77 78 79 /** 80 * Pop deleted id 81 * 82 * Reuses an id which belonged to another entity before 83 * If there's not an id available in this, it returns null 84 * 85 * This function is only used inernaly for the creation of entities 86 */ 87 @safe pure 88 private size_t popDeletedId() 89 { 90 size_t eid = this.delEntities.front; 91 this.delEntities = this.delEntities.length > 1 ? this.delEntities[1 .. $] : null; 92 93 return eid; 94 } 95 96 97 /** 98 * Exists 99 * 100 * Returns whether an entity exists in the scope 101 * 102 * Examples: 103 * -------------------- 104 * world.manager.create(); 105 * world.entity.exists(0); // true 106 * -------------------- 107 */ 108 @safe pure 109 public bool exists(in size_t eid) const 110 { 111 return (eid in this.mEntities) !is null; 112 } 113 114 115 /** 116 * Get an entity 117 * 118 * Returns the entity which contains the id passed 119 * If there are none, it returns null 120 * 121 * Examples: 122 * -------------------- 123 * Entity e = world.entity.get(16); 124 * -------------------- 125 */ 126 @safe pure 127 public Entity get(in size_t eid) 128 { 129 Entity* p; 130 return ((p = eid in this.mEntities) !is null) ? *p : null; 131 } 132 133 134 /** 135 * Get the first entity in the scope with a component 136 * 137 * Returns the first entity with the component passed 138 * If there's none, it returns null 139 * 140 * Examples: 141 * -------------------- 142 * Entity e = world.entity.getFirstWith!Foo; 143 * -------------------- 144 */ 145 @safe pure 146 public Entity getFirstWith(C : IComponent)() 147 { 148 foreach(Entity e; this.mEntities) 149 if (e.contains!C) 150 return e; 151 152 return null; 153 } 154 155 156 /** 157 * Get the first entity in the scope which contains a group of components 158 * 159 * Returns the first entity with the components passed 160 * 161 * Examples: 162 * -------------------- 163 * Entity e = world.entity.getFirstWith!(Component1, Component2, Component3, ...); 164 * -------------------- 165 */ 166 @safe pure 167 public Entity getFirstWith(T...)() 168 { 169 foreach(Entity e; this.mEntities) 170 if (e.containsAll!T) 171 return e; 172 173 return null; 174 } 175 176 177 /** 178 * Get every entity in the scope which contains a component 179 * 180 * Returns an array of type Entity with every entity which contains the component passed 181 * 182 * Examples: 183 * -------------------- 184 * Entity[] arr = world.entity.getAllWith!Foo; 185 * -------------------- 186 */ 187 @safe pure 188 public Entity[] getAllWith(T : IComponent)() 189 { 190 Entity[] ret; 191 foreach(Entity e; this.mEntities) 192 if (e.contains!T) 193 ret ~= e; 194 195 return ret; 196 } 197 198 199 /** 200 * Get every entity in the scope which contains a group of components 201 * 202 * Returns an array of type Entity with every entity which contains the components passed 203 * 204 * Examples: 205 * -------------------- 206 * Entity[] arr = world.entity.getAllWith!(Component1, Component2, Component3, ...); 207 * -------------------- 208 */ 209 @safe pure 210 public Entity[] getAllWith(T...)() 211 { 212 Entity[] ret; 213 foreach(Entity e; this.mEntities) 214 if (e.containsAll!T) 215 ret ~= e; 216 217 return ret; 218 } 219 220 221 /** 222 * Get all the deleted ids 223 * 224 * Returns an array with all the deleted ids currently in the scope 225 * 226 * Examples: 227 * -------------------- 228 * auto[] arr = world.entity.getDeletedIds(); 229 * -------------------- 230 */ 231 @safe pure 232 public const size_t[] getDeletedIds() 233 { 234 return this.delEntities.dup; 235 } 236 237 238 /** 239 * Detach 240 * 241 * Removes an entity from the world's scope if it exists 242 * 243 * Examples: 244 * -------------------- 245 * world.entity.detach(e); 246 * -------------------- 247 */ 248 @safe pure 249 public void detach(Entity e) 250 { 251 Entity* p; 252 p = e.id in this.mEntities; 253 254 if (p !is null && e is this.mEntities[e.id]) 255 { 256 this.mEntities.remove(e.id); 257 this.delEntities ~= e.id; 258 } 259 else 260 throw new EntityManagerHandlingException( 261 "Cannot detach entity. Entity doesn't exist in the world's scope." 262 ); 263 } 264 265 266 /* 267 * Clear 268 * 269 * Removes every entity from the world's scope 270 */ 271 @safe pure 272 public void clear() 273 { 274 foreach(key, value; this.mEntities) 275 { 276 this.delEntities ~= key; 277 this.mEntities.remove(key); 278 } 279 } 280 281 282 private size_t nextId = 0; 283 private Entity[size_t] mEntities; 284 private size_t[] delEntities; 285 } 286 287 288 /// 289 @safe pure 290 @("Entity Manager: Entity id counter") 291 unittest 292 { 293 EntityManager manager = new EntityManager(); 294 295 Entity e1 = manager.create(); 296 Entity e2 = manager.create(); 297 298 assertTrue(manager.exists(0)); 299 300 assertEquals(0, e1.id); // Entity id begins in 0 301 assertEquals(1, e2.id); 302 303 e1.detach(); // Entity id is removed from the world's scope 304 305 assertEquals(0, manager.getDeletedIds.front); 306 307 Entity e3 = manager.create(); // Entity gets an id which was removed previously 308 309 assertEquals(0, e3.id); 310 assertEquals(0, manager.getDeletedIds.length); // Entity id is no longer deleted 311 312 assertThrown!EntityManagerHandlingException(manager.detach(e1)); // This entity is no longer in the world's scope 313 } 314 315 316 /// 317 @safe pure 318 @("Entity Manager: Entities getAllWith and getFirstWith") 319 unittest 320 { 321 final class Foo : IComponent {} 322 final class Bar : IComponent {} 323 final class Foobar : IComponent {} 324 325 EntityManager manager = new EntityManager(); 326 327 Entity e1 = manager.create(); 328 Entity e2 = manager.create(); 329 Entity e3 = manager.create(); 330 331 e1.add(new Foo()); 332 e1.add(new Bar()); 333 334 e2.add(new Foo()); 335 e2.add(new Bar()); 336 337 e3.add(new Foo()); 338 339 auto arr = manager.getAllWith!(Foo, Bar); 340 auto e = manager.getFirstWith!Foo; 341 342 import std.algorithm.comparison : equal; 343 assertTrue(equal!((a,b) => a is b)([e1, e2], arr)); 344 assertTrue(e is e1); 345 346 assertTrue(manager.getFirstWith!Foobar is null); // There are no entities with this Component 347 } 348 349 350 /// 351 @safe pure 352 @("Entity Manager: Entity getter") 353 unittest 354 { 355 EntityManager manager = new EntityManager(); 356 357 Entity e = manager.create(); 358 359 assertTrue(e is manager.get(e.id)); 360 } 361 362 363 /// 364 @safe pure 365 @("Entity Manager: Entities clear") 366 unittest 367 { 368 EntityManager manager = new EntityManager(); 369 Entity e = manager.create(); 370 manager.clear(); 371 372 assertEquals(0, manager.mEntities.length); 373 }