1 /* 2 __ 3 / _| 4 __ _ _ _ _ __ ___ _ __ __ _ | |_ ___ ___ ___ 5 / _` | | | | '__/ _ \| '__/ _` | | _/ _ \/ __/ __| 6 | (_| | |_| | | | (_) | | | (_| | | || (_) \__ \__ \ 7 \__,_|\__,_|_| \___/|_| \__,_| |_| \___/|___/___/ 8 9 Copyright (C) 2019-2020 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 class EntityManagerHandlingException : Exception 50 { 51 mixin basicExceptionCtors; 52 } 53 54 final class EntityManager 55 { 56 /** 57 * Create an entity 58 * 59 * Creates a new entity with an unique id 60 * 61 * Examples: 62 * -------------------- 63 * world.entity.create(); 64 * Entity e = world.entity.create(); 65 * -------------------- 66 */ 67 @safe pure 68 public Entity create() 69 { 70 Entity e = new Entity(this, !this.delEntities.empty ? popDeletedId : nextId++); 71 this.mEntities[e.id] = e; 72 73 return e; 74 } 75 76 /** 77 * Pop deleted id 78 * 79 * Reuses an id which belonged to another entity before 80 * If there's not an id available in this, it returns null 81 * 82 * This function is only used inernaly for the creation of entities 83 */ 84 @safe pure 85 private size_t popDeletedId() 86 { 87 size_t eid = this.delEntities.front; 88 this.delEntities = this.delEntities.length > 1 ? this.delEntities[1 .. $] : null; 89 90 return eid; 91 } 92 93 /** 94 * Exists 95 * 96 * Returns whether an entity exists in the scope 97 * 98 * Examples: 99 * -------------------- 100 * world.manager.create(); 101 * world.entity.exists(0); // true 102 * -------------------- 103 */ 104 @safe pure 105 public bool exists(in size_t eid) const 106 { 107 return (eid in this.mEntities) !is null; 108 } 109 110 /** 111 * Get an entity 112 * 113 * Returns the entity which contains the id passed 114 * If there are none, it returns null 115 * 116 * Examples: 117 * -------------------- 118 * Entity e = world.entity.get(16); 119 * -------------------- 120 */ 121 @safe pure 122 public Entity get(in size_t eid) 123 { 124 Entity* p; 125 return ((p = eid in this.mEntities) !is null) ? *p : null; 126 } 127 128 /** 129 * Get the first entity in the scope with a component 130 * 131 * Returns the first entity with the component passed 132 * If there's none, it returns null 133 * 134 * Examples: 135 * -------------------- 136 * Entity e = world.entity.getFirstWith!Foo; 137 * -------------------- 138 */ 139 @safe pure 140 public Entity getFirstWith(C : IComponent)() 141 { 142 foreach (Entity e; this.mEntities) 143 if (e.contains!C) 144 return e; 145 146 return null; 147 } 148 149 /** 150 * Get the first entity in the scope which contains a group of components 151 * 152 * Returns the first entity with the components passed 153 * 154 * Examples: 155 * -------------------- 156 * Entity e = world.entity.getFirstWith!(Component1, Component2, Component3, ...); 157 * -------------------- 158 */ 159 @safe pure 160 public Entity getFirstWith(T...)() 161 { 162 foreach (Entity e; this.mEntities) 163 if (e.containsAll!T) 164 return e; 165 166 return null; 167 } 168 169 /** 170 * Get every entity in the scope which contains a component 171 * 172 * Returns an array of type Entity with every entity which contains the component passed 173 * 174 * Examples: 175 * -------------------- 176 * Entity[] arr = world.entity.getAllWith!Foo; 177 * -------------------- 178 */ 179 @safe pure 180 public Entity[] getAllWith(T : IComponent)() 181 { 182 Entity[] ret; 183 foreach (Entity e; this.mEntities) 184 if (e.contains!T) 185 ret ~= e; 186 187 return ret; 188 } 189 190 /** 191 * Get every entity in the scope which contains a group of components 192 * 193 * Returns an array of type Entity with every entity which contains the components passed 194 * 195 * Examples: 196 * -------------------- 197 * Entity[] arr = world.entity.getAllWith!(Component1, Component2, Component3, ...); 198 * -------------------- 199 */ 200 @safe pure 201 public Entity[] getAllWith(T...)() 202 { 203 Entity[] ret; 204 foreach (Entity e; this.mEntities) 205 if (e.containsAll!T) 206 ret ~= e; 207 208 return ret; 209 } 210 211 /** 212 * Get all the deleted ids 213 * 214 * Returns an array with all the deleted ids currently in the scope 215 * 216 * Examples: 217 * -------------------- 218 * auto[] arr = world.entity.getDeletedIds(); 219 * -------------------- 220 */ 221 @safe pure 222 public size_t[] getDeletedIds() const 223 { 224 return this.delEntities.dup; 225 } 226 227 /** 228 * Detach 229 * 230 * Removes an entity from the world's scope if it exists 231 * 232 * Examples: 233 * -------------------- 234 * world.entity.detach(e); 235 * -------------------- 236 */ 237 @safe pure 238 public void detach(Entity e) 239 { 240 Entity* p; 241 p = e.id in this.mEntities; 242 243 if (p !is null && e is this.mEntities[e.id]) 244 { 245 this.mEntities.remove(e.id); 246 this.delEntities ~= e.id; 247 } 248 else 249 throw new EntityManagerHandlingException( 250 "Cannot detach entity. Entity doesn't exist in the world's scope." 251 ); 252 } 253 254 /* 255 * Clear 256 * 257 * Removes every entity from the world's scope 258 */ 259 @safe pure 260 public void clear() 261 { 262 foreach (key, value; this.mEntities) 263 { 264 this.delEntities ~= key; 265 this.mEntities.remove(key); 266 } 267 } 268 269 private size_t nextId = 0; 270 private Entity[size_t] mEntities; 271 private size_t[] delEntities; 272 } 273 274 /// 275 @safe pure 276 @("Entity Manager: Entity id counter") 277 unittest 278 { 279 EntityManager manager = new EntityManager(); 280 281 Entity e1 = manager.create(); 282 Entity e2 = manager.create(); 283 284 assertTrue(manager.exists(0)); 285 286 assertEquals(0, e1.id); // Entity id begins in 0 287 assertEquals(1, e2.id); 288 289 e1.detach(); // Entity id is removed from the world's scope 290 291 assertEquals(0, manager.getDeletedIds.front); 292 293 Entity e3 = manager.create(); // Entity gets an id which was removed previously 294 295 assertEquals(0, e3.id); 296 assertEquals(0, manager.getDeletedIds.length); // Entity id is no longer deleted 297 298 assertThrown!EntityManagerHandlingException(manager.detach(e1)); // This entity is no longer in the world's scope 299 } 300 301 /// 302 @safe pure 303 @("Entity Manager: Entities getAllWith and getFirstWith") 304 unittest 305 { 306 final class Foo : IComponent 307 { 308 } 309 310 final class Bar : IComponent 311 { 312 } 313 314 final class Foobar : IComponent 315 { 316 } 317 318 EntityManager manager = new EntityManager(); 319 320 Entity e1 = manager.create(); 321 Entity e2 = manager.create(); 322 Entity e3 = manager.create(); 323 324 e1.add(new Foo()); 325 e1.add(new Bar()); 326 327 e2.add(new Foo()); 328 e2.add(new Bar()); 329 330 e3.add(new Foo()); 331 332 auto arr = manager.getAllWith!(Foo, Bar); 333 auto e = manager.getFirstWith!Foo; 334 335 import std.algorithm.comparison : equal; 336 337 assertTrue(equal!((a, b) => a is b)([e1, e2], arr)); 338 assertTrue(e is e1); 339 340 assertTrue(manager.getFirstWith!Foobar is null); // There are no entities with this Component 341 } 342 343 /// 344 @safe pure 345 @("Entity Manager: Entity getter") 346 unittest 347 { 348 EntityManager manager = new EntityManager(); 349 350 Entity e = manager.create(); 351 352 assertTrue(e is manager.get(e.id)); 353 } 354 355 /// 356 @safe pure 357 @("Entity Manager: Entities clear") 358 unittest 359 { 360 EntityManager manager = new EntityManager(); 361 Entity e = manager.create(); 362 manager.clear(); 363 364 assertEquals(0, manager.mEntities.length); 365 }