| 274 | } |
| 275 | |
| 276 | void WorldStorage::tick(float dt, String const* worldId) { |
| 277 | try { |
| 278 | // Tick down generation queue entries, and erase any that are expired. |
| 279 | eraseWhere(m_generationQueue, [dt](auto& p) { |
| 280 | p.second -= dt; |
| 281 | return p.second <= 0.0f; |
| 282 | }); |
| 283 | |
| 284 | // Tick down sector TTL values |
| 285 | for (auto& p : m_sectorMetadata) |
| 286 | p.second.timeToLive -= dt; |
| 287 | |
| 288 | // Loop over every loaded sector, figure out whether the sector needs to be |
| 289 | // unloaded, kept alive by a keep-alive entity, or has any entities that need |
| 290 | // to be stored because they moved into an entity-unloaded sector (zombies). |
| 291 | auto entityFactory = Root::singleton().entityFactory(); |
| 292 | unsigned unloaded = 0, skipped = 0; |
| 293 | for (auto const& p : m_sectorMetadata.pairs()) { |
| 294 | auto const& sector = p.first; |
| 295 | auto const& metadata = p.second; |
| 296 | |
| 297 | bool needsUnload = metadata.timeToLive <= 0.0f; |
| 298 | |
| 299 | // If it is not time to unload the sector, then we don't need to scan for |
| 300 | // keep-alive entities. If the sector is fully loaded, it can not have any |
| 301 | // zombie entities. If both of these are true, there is no work to do. |
| 302 | if (!needsUnload && metadata.loadLevel == SectorLoadLevel::Entities) |
| 303 | continue; |
| 304 | |
| 305 | bool keepAlive = false; |
| 306 | List<EntityPtr> zombieEntities; |
| 307 | m_entityMap->forEachEntity(RectF(m_tileArray->sectorRegion(sector)), [&](EntityPtr const& entity) { |
| 308 | if (belongsInSector(sector, entity->position())) { |
| 309 | if (!keepAlive && m_generatorFacade->entityKeepAlive(this, entity)) |
| 310 | keepAlive = true; |
| 311 | else if (metadata.loadLevel < SectorLoadLevel::Entities) |
| 312 | zombieEntities.append(entity); |
| 313 | } |
| 314 | }); |
| 315 | |
| 316 | if (keepAlive) { |
| 317 | setSectorTimeToLive(sector, randomizedSectorTTL()); |
| 318 | } else if (needsUnload) { |
| 319 | (unloadSectorToLevel(sector, SectorLoadLevel::None) ? unloaded : skipped)++; |
| 320 | } else if (!zombieEntities.empty()) { |
| 321 | List<EntityPtr> zombiesToStore; |
| 322 | List<EntityPtr> zombiesToRemove; |
| 323 | for (auto const& entity : zombieEntities) { |
| 324 | if (m_generatorFacade->entityPersistent(this, entity)) |
| 325 | zombiesToStore.append(entity); |
| 326 | else |
| 327 | zombiesToRemove.append(entity); |
| 328 | } |
| 329 | |
| 330 | for (auto const& entity : zombiesToRemove) { |
| 331 | m_entityMap->removeEntity(entity->entityId()); |
| 332 | m_generatorFacade->destructEntity(this, entity); |
| 333 | } |
nothing calls this directly
no test coverage detected