Fix WorldStorage accessing null entityMap if a broken world throws an exception on load
happened to me when trying to repair a broken world file
This commit is contained in:
parent
dc37c9bdb8
commit
b2afd65144
@ -371,15 +371,17 @@ void WorldStorage::unloadAll(bool force) {
|
|||||||
// or being entirely outside of the world geometry. This limits the number
|
// or being entirely outside of the world geometry. This limits the number
|
||||||
// of tries to completely uninit and store all entities before giving up
|
// of tries to completely uninit and store all entities before giving up
|
||||||
// and just letting some entities not be stored.
|
// and just letting some entities not be stored.
|
||||||
unsigned forceUnloadTries = storageConfig.getUInt("forceUnloadTries");
|
if (m_entityMap) {
|
||||||
for (unsigned i = 0; i < forceUnloadTries; ++i) {
|
unsigned forceUnloadTries = storageConfig.getUInt("forceUnloadTries");
|
||||||
for (auto sector : sectors)
|
for (unsigned i = 0; i < forceUnloadTries; ++i) {
|
||||||
unloadSectorToLevel(sector, SectorLoadLevel::Tiles, force);
|
for (auto& sector : sectors)
|
||||||
|
unloadSectorToLevel(sector, SectorLoadLevel::Tiles, force);
|
||||||
|
|
||||||
if (!force || m_entityMap->size() == 0)
|
if (!force || m_entityMap->size() == 0)
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (auto sector : sectors)
|
for (auto& sector : sectors)
|
||||||
unloadSectorToLevel(sector, SectorLoadLevel::None, force);
|
unloadSectorToLevel(sector, SectorLoadLevel::None, force);
|
||||||
|
|
||||||
} catch (std::exception const& e) {
|
} catch (std::exception const& e) {
|
||||||
@ -721,71 +723,71 @@ bool WorldStorage::unloadSectorToLevel(Sector const& sector, SectorLoadLevel tar
|
|||||||
if (!m_tileArray->sectorValid(sector) || targetLoadLevel == SectorLoadLevel::Loaded)
|
if (!m_tileArray->sectorValid(sector) || targetLoadLevel == SectorLoadLevel::Loaded)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
auto entityFactory = Root::singleton().entityFactory();
|
|
||||||
|
|
||||||
auto& metadata = m_sectorMetadata[sector];
|
auto& metadata = m_sectorMetadata[sector];
|
||||||
|
|
||||||
List<EntityPtr> entitiesToStore;
|
|
||||||
List<EntityPtr> entitiesToRemove;
|
|
||||||
bool entitiesOverlap = false;
|
bool entitiesOverlap = false;
|
||||||
|
if (m_entityMap) {
|
||||||
|
auto entityFactory = Root::singleton().entityFactory();
|
||||||
|
List<EntityPtr> entitiesToStore;
|
||||||
|
List<EntityPtr> entitiesToRemove;
|
||||||
|
|
||||||
for (auto& entity : m_entityMap->entityQuery(RectF(m_tileArray->sectorRegion(sector)))) {
|
for (auto& entity : m_entityMap->entityQuery(RectF(m_tileArray->sectorRegion(sector)))) {
|
||||||
// Only store / remove entities who belong to this sector. If an entity
|
// Only store / remove entities who belong to this sector. If an entity
|
||||||
// overlaps with this sector but does not belong to it, we may not want to
|
// overlaps with this sector but does not belong to it, we may not want to
|
||||||
// completely unload it.
|
// completely unload it.
|
||||||
auto position = entity->position();
|
auto position = entity->position();
|
||||||
if (!belongsInSector(sector, position)) {
|
if (!belongsInSector(sector, position)) {
|
||||||
if (auto entitySector = sectorForPosition(Vec2I(position))) {
|
if (auto entitySector = sectorForPosition(Vec2I(position))) {
|
||||||
if (auto p = m_sectorMetadata.ptr(*entitySector))
|
if (auto p = m_sectorMetadata.ptr(*entitySector))
|
||||||
entitiesOverlap |= p->timeToLive > 0.0f;
|
entitiesOverlap |= p->timeToLive > 0.0f;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
|
bool keepAlive = m_generatorFacade->entityKeepAlive(this, entity);
|
||||||
|
if (keepAlive && !force)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_generatorFacade->entityPersistent(this, entity))
|
||||||
|
entitiesToStore.append(std::move(entity));
|
||||||
|
else
|
||||||
|
entitiesToRemove.append(std::move(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool keepAlive = m_generatorFacade->entityKeepAlive(this, entity);
|
for (auto const& entity : entitiesToRemove) {
|
||||||
if (keepAlive && !force)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (m_generatorFacade->entityPersistent(this, entity))
|
|
||||||
entitiesToStore.append(std::move(entity));
|
|
||||||
else
|
|
||||||
entitiesToRemove.append(std::move(entity));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const& entity : entitiesToRemove) {
|
|
||||||
m_entityMap->removeEntity(entity->entityId());
|
|
||||||
m_generatorFacade->destructEntity(this, entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metadata.loadLevel == SectorLoadLevel::Entities || !entitiesToStore.empty()) {
|
|
||||||
EntitySectorStore sectorStore;
|
|
||||||
|
|
||||||
// If our current load level indicates that we might have entities that are
|
|
||||||
// not loaded, we need to load and merge with them, otherwise we should be
|
|
||||||
// overwriting them.
|
|
||||||
if (metadata.loadLevel < SectorLoadLevel::Entities) {
|
|
||||||
if (auto res = m_db.find(entitySectorKey(sector)))
|
|
||||||
sectorStore = readEntitySector(*res);
|
|
||||||
}
|
|
||||||
|
|
||||||
UniqueIndexStore storedUniques;
|
|
||||||
for (auto const& entity : entitiesToStore) {
|
|
||||||
m_entityMap->removeEntity(entity->entityId());
|
m_entityMap->removeEntity(entity->entityId());
|
||||||
m_generatorFacade->destructEntity(this, entity);
|
m_generatorFacade->destructEntity(this, entity);
|
||||||
auto position = entity->position();
|
|
||||||
if (auto uniqueId = entity->uniqueId())
|
|
||||||
storedUniques.add(*uniqueId, {sector, position});
|
|
||||||
sectorStore.append(entityFactory->storeVersionedEntity(entity));
|
|
||||||
}
|
}
|
||||||
m_db.insert(entitySectorKey(sector), writeEntitySector(sectorStore));
|
|
||||||
if (metadata.loadLevel < SectorLoadLevel::Entities)
|
|
||||||
mergeSectorUniques(sector, storedUniques);
|
|
||||||
else
|
|
||||||
updateSectorUniques(sector, storedUniques);
|
|
||||||
|
|
||||||
if (metadata.loadLevel == SectorLoadLevel::Entities) {
|
if (metadata.loadLevel == SectorLoadLevel::Entities || !entitiesToStore.empty()) {
|
||||||
metadata.loadLevel = SectorLoadLevel::Tiles;
|
EntitySectorStore sectorStore;
|
||||||
m_generatorFacade->sectorLoadLevelChanged(this, sector, SectorLoadLevel::Tiles);
|
|
||||||
|
// If our current load level indicates that we might have entities that are
|
||||||
|
// not loaded, we need to load and merge with them, otherwise we should be
|
||||||
|
// overwriting them.
|
||||||
|
if (metadata.loadLevel < SectorLoadLevel::Entities) {
|
||||||
|
if (auto res = m_db.find(entitySectorKey(sector)))
|
||||||
|
sectorStore = readEntitySector(*res);
|
||||||
|
}
|
||||||
|
|
||||||
|
UniqueIndexStore storedUniques;
|
||||||
|
for (auto const& entity : entitiesToStore) {
|
||||||
|
m_entityMap->removeEntity(entity->entityId());
|
||||||
|
m_generatorFacade->destructEntity(this, entity);
|
||||||
|
auto position = entity->position();
|
||||||
|
if (auto uniqueId = entity->uniqueId())
|
||||||
|
storedUniques.add(*uniqueId, {sector, position});
|
||||||
|
sectorStore.append(entityFactory->storeVersionedEntity(entity));
|
||||||
|
}
|
||||||
|
m_db.insert(entitySectorKey(sector), writeEntitySector(sectorStore));
|
||||||
|
if (metadata.loadLevel < SectorLoadLevel::Entities)
|
||||||
|
mergeSectorUniques(sector, storedUniques);
|
||||||
|
else
|
||||||
|
updateSectorUniques(sector, storedUniques);
|
||||||
|
|
||||||
|
if (metadata.loadLevel == SectorLoadLevel::Entities) {
|
||||||
|
metadata.loadLevel = SectorLoadLevel::Tiles;
|
||||||
|
m_generatorFacade->sectorLoadLevelChanged(this, sector, SectorLoadLevel::Tiles);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user