1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-14 02:33:51 +02:00

Protect every access to zone tiles with a mutex

This commit is contained in:
Tomasz Zieliński 2024-03-27 06:16:48 +01:00
parent cfc4a26f55
commit d8c93cb222
20 changed files with 385 additions and 225 deletions

View File

@ -418,7 +418,7 @@ void CMapGenerator::fillZones()
} }
auto grailZone = *RandomGeneratorUtil::nextItem(treasureZones, rand); auto grailZone = *RandomGeneratorUtil::nextItem(treasureZones, rand);
map->getMap(this).grailPos = *RandomGeneratorUtil::nextItem(grailZone->freePaths().getTiles(), rand); map->getMap(this).grailPos = *RandomGeneratorUtil::nextItem(grailZone->freePaths()->getTiles(), rand);
logGlobal->info("Zones filled successfully"); logGlobal->info("Zones filled successfully");

View File

@ -858,7 +858,7 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
auto moveZoneToCenterOfMass = [width, height](const std::shared_ptr<Zone> & zone) -> void auto moveZoneToCenterOfMass = [width, height](const std::shared_ptr<Zone> & zone) -> void
{ {
int3 total(0, 0, 0); int3 total(0, 0, 0);
auto tiles = zone->area().getTiles(); auto tiles = zone->area()->getTiles();
for(const auto & tile : tiles) for(const auto & tile : tiles)
{ {
total += tile; total += tile;
@ -892,14 +892,14 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
{ {
distances.emplace_back(zone.second, static_cast<float>(pos.dist2dSQ(zone.second->getPos()))); distances.emplace_back(zone.second, static_cast<float>(pos.dist2dSQ(zone.second->getPos())));
} }
boost::min_element(distances, compareByDistance)->first->area().add(pos); //closest tile belongs to zone boost::min_element(distances, compareByDistance)->first->area()->add(pos); //closest tile belongs to zone
} }
} }
} }
for(const auto & zone : zones) for(const auto & zone : zones)
{ {
if(zone.second->area().empty()) if(zone.second->area()->empty())
throw rmgException("Empty zone is generated, probably RMG template is inappropriate for map size"); throw rmgException("Empty zone is generated, probably RMG template is inappropriate for map size");
moveZoneToCenterOfMass(zone.second); moveZoneToCenterOfMass(zone.second);
@ -948,14 +948,14 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
//Tile closest to vertex belongs to zone //Tile closest to vertex belongs to zone
auto closestZone = boost::min_element(distances, simpleCompareByDistance)->first; auto closestZone = boost::min_element(distances, simpleCompareByDistance)->first;
closestZone->area().add(pos); closestZone->area()->add(pos);
map.setZoneID(pos, closestZone->getId()); map.setZoneID(pos, closestZone->getId());
} }
} }
for(const auto & zone : zonesOnLevel[level]) for(const auto & zone : zonesOnLevel[level])
{ {
if(zone.second->area().empty()) if(zone.second->area()->empty())
{ {
// FIXME: Some vertices are duplicated, but it's not a source of problem // FIXME: Some vertices are duplicated, but it's not a source of problem
logGlobal->error("Zone %d at %s is empty, dumping Penrose tiling", zone.second->getId(), zone.second->getCenter().toString()); logGlobal->error("Zone %d at %s is empty, dumping Penrose tiling", zone.second->getId(), zone.second->getCenter().toString());
@ -981,12 +981,12 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
{ {
auto discardTiles = collectDistantTiles(*zone.second, zone.second->getSize() + 1.f); auto discardTiles = collectDistantTiles(*zone.second, zone.second->getSize() + 1.f);
for(const auto & t : discardTiles) for(const auto & t : discardTiles)
zone.second->area().erase(t); zone.second->area()->erase(t);
} }
//make sure that terrain inside zone is not a rock //make sure that terrain inside zone is not a rock
auto v = zone.second->getArea().getTilesVector(); auto v = zone.second->area()->getTilesVector();
map.getMapProxy()->drawTerrain(*rand, v, ETerrainId::SUBTERRANEAN); map.getMapProxy()->drawTerrain(*rand, v, ETerrainId::SUBTERRANEAN);
} }
} }

View File

@ -26,7 +26,8 @@ VCMI_LIB_NAMESPACE_BEGIN
rmg::Tileset collectDistantTiles(const Zone& zone, int distance) rmg::Tileset collectDistantTiles(const Zone& zone, int distance)
{ {
uint32_t distanceSq = distance * distance; uint32_t distanceSq = distance * distance;
auto subarea = zone.getArea().getSubarea([&zone, distanceSq](const int3 & t)
auto subarea = zone.area()->getSubarea([&zone, distanceSq](const int3 & t)
{ {
return t.dist2dSQ(zone.getPos()) > distanceSq; return t.dist2dSQ(zone.getPos()) > distanceSq;
}); });

View File

@ -75,25 +75,44 @@ void Zone::setPos(const int3 &Pos)
pos = Pos; pos = Pos;
} }
const rmg::Area & Zone::getArea() const ThreadSafeProxy<rmg::Area> Zone::area()
{ {
return dArea; return ThreadSafeProxy<rmg::Area>(dArea, areaMutex);
} }
rmg::Area & Zone::area() ThreadSafeProxy<const rmg::Area> Zone::area() const
{ {
return dArea; return ThreadSafeProxy<const rmg::Area>(dArea, areaMutex);
} }
rmg::Area & Zone::areaPossible() ThreadSafeProxy<rmg::Area> Zone::areaPossible()
{ {
//FIXME: make const, only modify via mutex-protected interface return ThreadSafeProxy<rmg::Area>(dAreaPossible, areaMutex);
return dAreaPossible;
} }
rmg::Area & Zone::areaUsed() ThreadSafeProxy<const rmg::Area> Zone::areaPossible() const
{ {
return dAreaUsed; return ThreadSafeProxy<const rmg::Area>(dAreaPossible, areaMutex);
}
ThreadSafeProxy<rmg::Area> Zone::freePaths()
{
return ThreadSafeProxy<rmg::Area>(dAreaFree, areaMutex);
}
ThreadSafeProxy<const rmg::Area> Zone::freePaths() const
{
return ThreadSafeProxy<const rmg::Area>(dAreaFree, areaMutex);
}
ThreadSafeProxy<rmg::Area> Zone::areaUsed()
{
return ThreadSafeProxy<rmg::Area>(dAreaUsed, areaMutex);
}
ThreadSafeProxy<const rmg::Area> Zone::areaUsed() const
{
return ThreadSafeProxy<const rmg::Area>(dAreaUsed, areaMutex);
} }
void Zone::clearTiles() void Zone::clearTiles()
@ -122,11 +141,6 @@ void Zone::initFreeTiles()
} }
} }
rmg::Area & Zone::freePaths()
{
return dAreaFree;
}
FactionID Zone::getTownType() const FactionID Zone::getTownType() const
{ {
return townType; return townType;
@ -225,18 +239,16 @@ TModificators Zone::getModificators()
void Zone::connectPath(const rmg::Path & path) void Zone::connectPath(const rmg::Path & path)
///connect current tile to any other free tile within zone ///connect current tile to any other free tile within zone
{ {
dAreaPossible.subtract(path.getPathArea()); //Lock lock(areaMutex);
dAreaFree.unite(path.getPathArea());
areaPossible()->subtract(path.getPathArea());
freePaths()->unite(path.getPathArea());
for(const auto & t : path.getPathArea().getTilesVector()) for(const auto & t : path.getPathArea().getTilesVector())
map.setOccupied(t, ETileType::FREE); map.setOccupied(t, ETileType::FREE);
} }
void Zone::fractalize() void Zone::fractalize()
{ {
rmg::Area clearedTiles(dAreaFree);
rmg::Area possibleTiles(dAreaPossible);
rmg::Area tilesToIgnore; //will be erased in this iteration
//Squared //Squared
float minDistance = 9 * 9; float minDistance = 9 * 9;
float freeDistance = pos.z ? (10 * 10) : (9 * 9); float freeDistance = pos.z ? (10 * 10) : (9 * 9);
@ -283,6 +295,13 @@ void Zone::fractalize()
vstd::amax(freeDistance, 4 * 4); vstd::amax(freeDistance, 4 * 4);
logGlobal->trace("Zone %d: treasureValue %d blockDistance: %2.f, freeDistance: %2.f", getId(), treasureValue, blockDistance, freeDistance); logGlobal->trace("Zone %d: treasureValue %d blockDistance: %2.f, freeDistance: %2.f", getId(), treasureValue, blockDistance, freeDistance);
Lock lock(areaMutex);
// FIXME: Do not access Area directly
rmg::Area clearedTiles(dAreaFree);
rmg::Area possibleTiles(dAreaPossible);
rmg::Area tilesToIgnore; //will be erased in this iteration
if(type != ETemplateZoneType::JUNCTION) if(type != ETemplateZoneType::JUNCTION)
{ {
//junction is not fractalized, has only one straight path //junction is not fractalized, has only one straight path
@ -336,7 +355,7 @@ void Zone::fractalize()
} }
} }
Lock lock(areaMutex); //Lock lock(areaMutex);
//Connect with free areas //Connect with free areas
auto areas = connectedAreas(clearedTiles, true); auto areas = connectedAreas(clearedTiles, true);
for(auto & area : areas) for(auto & area : areas)

View File

@ -34,6 +34,36 @@ extern const std::function<bool(const int3 &)> AREA_NO_FILTER;
typedef std::list<std::shared_ptr<Modificator>> TModificators; typedef std::list<std::shared_ptr<Modificator>> TModificators;
template<typename T>
class ThreadSafeProxy
{
public:
ThreadSafeProxy(T& resource, boost::recursive_mutex& mutex)
: resourceRef(resource), lock(mutex) {}
T* operator->() { return &resourceRef; }
const T* operator->() const { return &resourceRef; }
T& operator*() { return resourceRef; }
const T& operator*() const { return resourceRef; }
T& get() {return resourceRef;}
const T& get() const {return resourceRef;}
T operator+(const T & other)
{
return resourceRef + other;
}
T operator+(ThreadSafeProxy<T> & other)
{
return get() + other.get();
}
private:
T& resourceRef;
std::lock_guard<boost::recursive_mutex> lock;
};
class Zone : public rmg::ZoneOptions class Zone : public rmg::ZoneOptions
{ {
public: public:
@ -48,11 +78,14 @@ public:
int3 getPos() const; int3 getPos() const;
void setPos(const int3 &pos); void setPos(const int3 &pos);
const rmg::Area & getArea() const; ThreadSafeProxy<rmg::Area> area();
rmg::Area & area(); ThreadSafeProxy<const rmg::Area> area() const;
rmg::Area & areaPossible(); ThreadSafeProxy<rmg::Area> areaPossible();
rmg::Area & freePaths(); ThreadSafeProxy<const rmg::Area> areaPossible() const;
rmg::Area & areaUsed(); ThreadSafeProxy<rmg::Area> freePaths();
ThreadSafeProxy<const rmg::Area> freePaths() const;
ThreadSafeProxy<rmg::Area> areaUsed();
ThreadSafeProxy<const rmg::Area> areaUsed() const;
void initFreeTiles(); void initFreeTiles();
void clearTiles(); void clearTiles();
@ -89,7 +122,7 @@ public:
CRandomGenerator & getRand(); CRandomGenerator & getRand();
public: public:
boost::recursive_mutex areaMutex; mutable boost::recursive_mutex areaMutex;
using Lock = boost::unique_lock<boost::recursive_mutex>; using Lock = boost::unique_lock<boost::recursive_mutex>;
protected: protected:

View File

@ -28,6 +28,24 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
std::pair<Zone::Lock, Zone::Lock> ConnectionsPlacer::lockZones(std::shared_ptr<Zone> otherZone)
{
if (zone.getId() == otherZone->getId())
return {};
while (true)
{
// FIXME: What if every zone owns its own lock?
auto lock1 = Zone::Lock(zone.areaMutex, boost::try_to_lock);
auto lock2 = Zone::Lock(otherZone->areaMutex, boost::try_to_lock);
if (lock1.owns_lock() && lock2.owns_lock())
{
return { std::move(lock1), std::move(lock2) };
}
}
}
void ConnectionsPlacer::process() void ConnectionsPlacer::process()
{ {
collectNeighbourZones(); collectNeighbourZones();
@ -36,16 +54,14 @@ void ConnectionsPlacer::process()
{ {
for (auto& c : dConnections) for (auto& c : dConnections)
{ {
if (c.getZoneA() != zone.getId() && c.getZoneB() != zone.getId())
continue;
auto otherZone = map.getZones().at(c.getZoneB()); auto otherZone = map.getZones().at(c.getZoneB());
auto* cp = otherZone->getModificator<ConnectionsPlacer>(); auto* cp = otherZone->getModificator<ConnectionsPlacer>();
while (cp) while (cp)
{ {
RecursiveLock lock1(externalAccessMutex, boost::try_to_lock_t{}); // FIXME: It does lock ConnectionPlacer, but not areaMutex
RecursiveLock lock2(cp->externalAccessMutex, boost::try_to_lock_t{}); RecursiveLock lock1(externalAccessMutex, boost::try_to_lock);
RecursiveLock lock2(cp->externalAccessMutex, boost::try_to_lock);
if (lock1.owns_lock() && lock2.owns_lock()) if (lock1.owns_lock() && lock2.owns_lock())
{ {
if (!vstd::contains(dCompleted, c)) if (!vstd::contains(dCompleted, c))
@ -78,9 +94,16 @@ void ConnectionsPlacer::init()
POSTFUNCTION(RoadPlacer); POSTFUNCTION(RoadPlacer);
POSTFUNCTION(ObjectManager); POSTFUNCTION(ObjectManager);
auto id = zone.getId();
for(auto c : map.getMapGenOptions().getMapTemplate()->getConnectedZoneIds()) for(auto c : map.getMapGenOptions().getMapTemplate()->getConnectedZoneIds())
{
// Only consider connected zones
if (c.getZoneA() == id || c.getZoneB() == id)
{
addConnection(c); addConnection(c);
} }
}
}
void ConnectionsPlacer::addConnection(const rmg::ZoneConnection& connection) void ConnectionsPlacer::addConnection(const rmg::ZoneConnection& connection)
{ {
@ -106,6 +129,9 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
bool directProhibited = vstd::contains(ourTerrain->prohibitTransitions, otherZone->getTerrainType()) bool directProhibited = vstd::contains(ourTerrain->prohibitTransitions, otherZone->getTerrainType())
|| vstd::contains(otherTerrain->prohibitTransitions, zone.getTerrainType()); || vstd::contains(otherTerrain->prohibitTransitions, zone.getTerrainType());
auto lock = lockZones(otherZone);
auto directConnectionIterator = dNeighbourZones.find(otherZoneId); auto directConnectionIterator = dNeighbourZones.find(otherZoneId);
if (directConnectionIterator != dNeighbourZones.end()) if (directConnectionIterator != dNeighbourZones.end())
@ -115,19 +141,19 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
for (auto borderPos : directConnectionIterator->second) for (auto borderPos : directConnectionIterator->second)
{ {
//TODO: Refactor common code with direct connection //TODO: Refactor common code with direct connection
int3 potentialPos = zone.areaPossible().nearest(borderPos); int3 potentialPos = zone.areaPossible()->nearest(borderPos);
assert(borderPos != potentialPos); assert(borderPos != potentialPos);
auto safetyGap = rmg::Area({ potentialPos }); auto safetyGap = rmg::Area({ potentialPos });
safetyGap.unite(safetyGap.getBorderOutside()); safetyGap.unite(safetyGap.getBorderOutside());
safetyGap.intersect(zone.areaPossible()); safetyGap.intersect(zone.areaPossible().get());
if (!safetyGap.empty()) if (!safetyGap.empty())
{ {
safetyGap.intersect(otherZone->areaPossible()); safetyGap.intersect(otherZone->areaPossible().get());
if (safetyGap.empty()) if (safetyGap.empty())
{ {
rmg::Area border(zone.getArea().getBorder()); rmg::Area border(zone.area()->getBorder());
border.unite(otherZone->getArea().getBorder()); border.unite(otherZone->area()->getBorder());
auto costFunction = [&border](const int3& s, const int3& d) auto costFunction = [&border](const int3& s, const int3& d)
{ {
@ -139,9 +165,9 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
theirArea.add(potentialPos); theirArea.add(potentialPos);
rmg::Path ourPath(ourArea); rmg::Path ourPath(ourArea);
rmg::Path theirPath(theirArea); rmg::Path theirPath(theirArea);
ourPath.connect(zone.freePaths()); ourPath.connect(zone.freePaths().get());
ourPath = ourPath.search(potentialPos, true, costFunction); ourPath = ourPath.search(potentialPos, true, costFunction);
theirPath.connect(otherZone->freePaths()); theirPath.connect(otherZone->freePaths().get());
theirPath = theirPath.search(potentialPos, true, costFunction); theirPath = theirPath.search(potentialPos, true, costFunction);
if (ourPath.valid() && theirPath.valid()) if (ourPath.valid() && theirPath.valid())
@ -174,7 +200,7 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
int3 roadNode; int3 roadNode;
for (auto borderPos : directConnectionIterator->second) for (auto borderPos : directConnectionIterator->second)
{ {
int3 potentialPos = zone.areaPossible().nearest(borderPos); int3 potentialPos = zone.areaPossible()->nearest(borderPos);
assert(borderPos != potentialPos); assert(borderPos != potentialPos);
//Check if guard pos doesn't touch any 3rd zone. This would create unwanted passage to 3rd zone //Check if guard pos doesn't touch any 3rd zone. This would create unwanted passage to 3rd zone
@ -200,10 +226,10 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
auto safetyGap = rmg::Area({ potentialPos }); auto safetyGap = rmg::Area({ potentialPos });
safetyGap.unite(safetyGap.getBorderOutside()); safetyGap.unite(safetyGap.getBorderOutside());
safetyGap.intersect(zone.areaPossible()); safetyGap.intersect(zone.areaPossible().get());
if (!safetyGap.empty()) if (!safetyGap.empty())
{ {
safetyGap.intersect(otherZone->areaPossible()); safetyGap.intersect(otherZone->areaPossible().get());
if (safetyGap.empty()) if (safetyGap.empty())
{ {
float distanceToCenter = zone.getPos().dist2d(potentialPos) * otherZone->getPos().dist2d(potentialPos); float distanceToCenter = zone.getPos().dist2d(potentialPos) * otherZone->getPos().dist2d(potentialPos);
@ -228,8 +254,8 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
auto & manager = *zone.getModificator<ObjectManager>(); auto & manager = *zone.getModificator<ObjectManager>();
auto * monsterType = manager.chooseGuard(connection.getGuardStrength(), true); auto * monsterType = manager.chooseGuard(connection.getGuardStrength(), true);
rmg::Area border(zone.getArea().getBorder()); rmg::Area border(zone.area()->getBorder());
border.unite(otherZone->getArea().getBorder()); border.unite(otherZone->area()->getBorder());
auto costFunction = [&border](const int3 & s, const int3 & d) auto costFunction = [&border](const int3 & s, const int3 & d)
{ {
@ -241,9 +267,9 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
theirArea.add(guardPos); theirArea.add(guardPos);
rmg::Path ourPath(ourArea); rmg::Path ourPath(ourArea);
rmg::Path theirPath(theirArea); rmg::Path theirPath(theirArea);
ourPath.connect(zone.freePaths()); ourPath.connect(zone.freePaths().get());
ourPath = ourPath.search(guardPos, true, costFunction); ourPath = ourPath.search(guardPos, true, costFunction);
theirPath.connect(otherZone->freePaths()); theirPath.connect(otherZone->freePaths().get());
theirPath = theirPath.search(guardPos, true, costFunction); theirPath = theirPath.search(guardPos, true, costFunction);
if(ourPath.valid() && theirPath.valid()) if(ourPath.valid() && theirPath.valid())
@ -262,10 +288,11 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
else else
{ {
//Update distances from empty passage, too //Update distances from empty passage, too
zone.areaPossible().erase(guardPos); zone.areaPossible()->erase(guardPos);
zone.freePaths().add(guardPos); zone.freePaths()->add(guardPos);
map.setOccupied(guardPos, ETileType::FREE); map.setOccupied(guardPos, ETileType::FREE);
manager.updateDistances(guardPos); manager.updateDistances(guardPos);
// FIXME: Accessing other manager might lead to deadlock
otherZone->getModificator<ObjectManager>()->updateDistances(guardPos); otherZone->getModificator<ObjectManager>()->updateDistances(guardPos);
} }
@ -318,8 +345,10 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
{ {
int3 zShift(0, 0, zone.getPos().z - otherZone->getPos().z); int3 zShift(0, 0, zone.getPos().z - otherZone->getPos().z);
auto lock = lockZones(otherZone);
std::scoped_lock doubleLock(zone.areaMutex, otherZone->areaMutex); std::scoped_lock doubleLock(zone.areaMutex, otherZone->areaMutex);
auto commonArea = zone.areaPossible() * (otherZone->areaPossible() + zShift); auto commonArea = zone.areaPossible().get() * (otherZone->areaPossible().get() + zShift);
if(!commonArea.empty()) if(!commonArea.empty())
{ {
assert(zone.getModificator<ObjectManager>()); assert(zone.getModificator<ObjectManager>());
@ -339,7 +368,7 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
bool guarded2 = managerOther.addGuard(rmgGate2, connection.getGuardStrength(), true); bool guarded2 = managerOther.addGuard(rmgGate2, connection.getGuardStrength(), true);
int minDist = 3; int minDist = 3;
rmg::Path path2(otherZone->area()); rmg::Path path2(otherZone->area().get());
rmg::Path path1 = manager.placeAndConnectObject(commonArea, rmgGate1, [this, minDist, &path2, &rmgGate1, &zShift, guarded2, &managerOther, &rmgGate2 ](const int3 & tile) rmg::Path path1 = manager.placeAndConnectObject(commonArea, rmgGate1, [this, minDist, &path2, &rmgGate1, &zShift, guarded2, &managerOther, &rmgGate2 ](const int3 & tile)
{ {
auto ti = map.getTileInfo(tile); auto ti = map.getTileInfo(tile);
@ -403,7 +432,7 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
void ConnectionsPlacer::collectNeighbourZones() void ConnectionsPlacer::collectNeighbourZones()
{ {
auto border = zone.area().getBorderOutside(); auto border = zone.area()->getBorderOutside();
for(const auto & i : border) for(const auto & i : border)
{ {
if(!map.isOnMap(i)) if(!map.isOnMap(i))
@ -423,8 +452,8 @@ bool ConnectionsPlacer::shouldGenerateRoad(const rmg::ZoneConnection& connection
void ConnectionsPlacer::createBorder() void ConnectionsPlacer::createBorder()
{ {
rmg::Area borderArea(zone.getArea().getBorder()); rmg::Area borderArea(zone.area()->getBorder());
rmg::Area borderOutsideArea(zone.getArea().getBorderOutside()); rmg::Area borderOutsideArea(zone.area()->getBorderOutside());
auto blockBorder = borderArea.getSubarea([this, &borderOutsideArea](const int3 & t) auto blockBorder = borderArea.getSubarea([this, &borderOutsideArea](const int3 & t)
{ {
auto tile = borderOutsideArea.nearest(t); auto tile = borderOutsideArea.nearest(t);
@ -448,21 +477,22 @@ void ConnectionsPlacer::createBorder()
} }
}; };
Zone::Lock lock(zone.areaMutex); //Protect from erasing same tiles again //Zone::Lock lock(zone.areaMutex); //Protect from erasing same tiles again
auto areaPossible = zone.areaPossible();
for(const auto & tile : blockBorder.getTilesVector()) for(const auto & tile : blockBorder.getTilesVector())
{ {
if(map.isPossible(tile)) if(map.isPossible(tile))
{ {
map.setOccupied(tile, ETileType::BLOCKED); map.setOccupied(tile, ETileType::BLOCKED);
zone.areaPossible().erase(tile); areaPossible->erase(tile);
} }
map.foreachDirectNeighbour(tile, [this](int3 &nearbyPos) map.foreachDirectNeighbour(tile, [this, &areaPossible](int3 &nearbyPos)
{ {
if(map.isPossible(nearbyPos) && map.getZoneID(nearbyPos) == zone.getId()) if(map.isPossible(nearbyPos) && map.getZoneID(nearbyPos) == zone.getId())
{ {
map.setOccupied(nearbyPos, ETileType::BLOCKED); map.setOccupied(nearbyPos, ETileType::BLOCKED);
zone.areaPossible().erase(nearbyPos); areaPossible->erase(nearbyPos);
} }
}); });
} }

View File

@ -33,6 +33,7 @@ public:
protected: protected:
void collectNeighbourZones(); void collectNeighbourZones();
std::pair<Zone::Lock, Zone::Lock> lockZones(std::shared_ptr<Zone> otherZone);
protected: protected:
std::vector<rmg::ZoneConnection> dConnections; std::vector<rmg::ZoneConnection> dConnections;

View File

@ -35,7 +35,7 @@ const std::string & Modificator::getName() const
bool Modificator::isReady() bool Modificator::isReady()
{ {
Lock lock(mx, boost::try_to_lock_t{}); Lock lock(mx, boost::try_to_lock);
if (!lock.owns_lock()) if (!lock.owns_lock())
{ {
return false; return false;
@ -63,7 +63,7 @@ bool Modificator::isReady()
bool Modificator::isFinished() bool Modificator::isFinished()
{ {
Lock lock(mx, boost::try_to_lock_t{}); Lock lock(mx, boost::try_to_lock);
if (!lock.owns_lock()) if (!lock.owns_lock())
{ {
return false; return false;
@ -119,6 +119,8 @@ void Modificator::postfunction(Modificator * modificator)
void Modificator::dump() void Modificator::dump()
{ {
// TODO: Refactor to lock zone area only once
std::ofstream out(boost::str(boost::format("seed_%d_modzone_%d_%s.txt") % generator.getRandomSeed() % zone.getId() % getName())); std::ofstream out(boost::str(boost::format("seed_%d_modzone_%d_%s.txt") % generator.getRandomSeed() % zone.getId() % getName()));
int levels = map.levels(); int levels = map.levels();
int width = map.width(); int width = map.width();
@ -140,13 +142,13 @@ void Modificator::dump()
char Modificator::dump(const int3 & t) char Modificator::dump(const int3 & t)
{ {
if(zone.freePaths().contains(t)) if(zone.freePaths()->contains(t))
return '.'; //free path return '.'; //free path
if(zone.areaPossible().contains(t)) if(zone.areaPossible()->contains(t))
return ' '; //possible return ' '; //possible
if(zone.areaUsed().contains(t)) if(zone.areaUsed()->contains(t))
return 'U'; //used return 'U'; //used
if(zone.area().contains(t)) if(zone.area()->contains(t))
{ {
if(map.shouldBeBlocked(t)) if(map.shouldBeBlocked(t))
return '#'; //obstacle return '#'; //obstacle

View File

@ -40,7 +40,30 @@ void ObjectManager::process()
void ObjectManager::init() void ObjectManager::init()
{ {
DEPENDENCY(WaterAdopter); DEPENDENCY(WaterAdopter);
DEPENDENCY_ALL(ConnectionsPlacer); //Monoliths can be placed by other zone, too
//Monoliths can be placed by other zone, too
// Consider only connected zones
auto id = zone.getId();
std::set<TRmgTemplateZoneId> connectedZones;
for(auto c : map.getMapGenOptions().getMapTemplate()->getConnectedZoneIds())
{
// Only consider connected zones
if (c.getZoneA() == id || c.getZoneB() == id)
{
connectedZones.insert(c.getZoneA());
connectedZones.insert(c.getZoneB());
}
}
auto zones = map.getZones();
for (auto zoneId : connectedZones)
{
auto * cp = zones.at(zoneId)->getModificator<ConnectionsPlacer>();
if (cp)
{
dependency(cp);
}
}
DEPENDENCY(TownPlacer); //Only secondary towns DEPENDENCY(TownPlacer); //Only secondary towns
DEPENDENCY(MinePlacer); DEPENDENCY(MinePlacer);
POSTFUNCTION(RoadPlacer); POSTFUNCTION(RoadPlacer);
@ -49,9 +72,11 @@ void ObjectManager::init()
void ObjectManager::createDistancesPriorityQueue() void ObjectManager::createDistancesPriorityQueue()
{ {
const auto tiles = zone.areaPossible()->getTilesVector();
RecursiveLock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
tilesByDistance.clear(); tilesByDistance.clear();
for(const auto & tile : zone.areaPossible().getTilesVector()) for(const auto & tile : tiles)
{ {
tilesByDistance.push(std::make_pair(tile, map.getNearestObjectDistance(tile))); tilesByDistance.push(std::make_pair(tile, map.getNearestObjectDistance(tile)));
} }
@ -93,9 +118,24 @@ void ObjectManager::updateDistances(const int3 & pos)
void ObjectManager::updateDistances(std::function<ui32(const int3 & tile)> distanceFunction) void ObjectManager::updateDistances(std::function<ui32(const int3 & tile)> distanceFunction)
{ {
RecursiveLock lock(externalAccessMutex); // Workaround to avoid dealock when accessed from other zone
RecursiveLock lock(zone.areaMutex, boost::try_to_lock);
if (!lock.owns_lock())
{
// Sorry, unsolvable problem of mutual impact¯\_(ツ)_/¯
return;
}
/*
1. Update map distances - Requires lock on zone area only
2. Update tilesByDistance priority queue - Requires lock area AND externalAccessMutex
*/
const auto tiles = zone.areaPossible()->getTilesVector();
//RecursiveLock lock(externalAccessMutex);
tilesByDistance.clear(); tilesByDistance.clear();
for (const auto & tile : zone.areaPossible().getTilesVector()) //don't need to mark distance for not possible tiles
for (const auto & tile : tiles) //don't need to mark distance for not possible tiles
{ {
ui32 d = distanceFunction(tile); ui32 d = distanceFunction(tile);
map.setNearestObjectDistance(tile, std::min(static_cast<float>(d), map.getNearestObjectDistance(tile))); map.setNearestObjectDistance(tile, std::min(static_cast<float>(d), map.getNearestObjectDistance(tile)));
@ -145,7 +185,10 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
if(optimizer & OptimizeType::DISTANCE) if(optimizer & OptimizeType::DISTANCE)
{ {
// Do not add or remove tiles while we iterate on them
//RecursiveLock lock(externalAccessMutex);
auto open = tilesByDistance; auto open = tilesByDistance;
while(!open.empty()) while(!open.empty())
{ {
auto node = open.top(); auto node = open.top();
@ -235,7 +278,7 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const
{ {
RecursiveLock lock(externalAccessMutex); //RecursiveLock lock(externalAccessMutex);
return placeAndConnectObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile) return placeAndConnectObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile)
{ {
float bestDistance = 10e9; float bestDistance = 10e9;
@ -372,7 +415,7 @@ bool ObjectManager::createMonoliths()
bool guarded = addGuard(rmgObject, objInfo.guardStrength, true); bool guarded = addGuard(rmgObject, objInfo.guardStrength, true);
Zone::Lock lock(zone.areaMutex); Zone::Lock lock(zone.areaMutex);
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE); auto path = placeAndConnectObject(zone.areaPossible().get(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
if(!path.valid()) if(!path.valid())
{ {
@ -395,7 +438,7 @@ bool ObjectManager::createRequiredObjects()
{ {
logGlobal->trace("Creating required objects"); logGlobal->trace("Creating required objects");
//RecursiveLock lock(externalAccessMutex); //Why could requiredObjects be modified during the loop? RecursiveLock lock(externalAccessMutex); //In case someone adds more objects
for(const auto & objInfo : requiredObjects) for(const auto & objInfo : requiredObjects)
{ {
rmg::Object rmgObject(*objInfo.obj); rmg::Object rmgObject(*objInfo.obj);
@ -403,7 +446,7 @@ bool ObjectManager::createRequiredObjects()
bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY)); bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
Zone::Lock lock(zone.areaMutex); Zone::Lock lock(zone.areaMutex);
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE); auto path = placeAndConnectObject(zone.areaPossible().get(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
if(!path.valid()) if(!path.valid())
{ {
@ -421,7 +464,7 @@ bool ObjectManager::createRequiredObjects()
rmg::Object rmgNearObject(*nearby.obj); rmg::Object rmgNearObject(*nearby.obj);
rmg::Area possibleArea(rmgObject.instances().front()->getBlockedArea().getBorderOutside()); rmg::Area possibleArea(rmgObject.instances().front()->getBlockedArea().getBorderOutside());
possibleArea.intersect(zone.areaPossible()); possibleArea.intersect(zone.areaPossible().get());
if(possibleArea.empty()) if(possibleArea.empty())
{ {
rmgNearObject.clear(); rmgNearObject.clear();
@ -436,12 +479,11 @@ bool ObjectManager::createRequiredObjects()
for(const auto & objInfo : closeObjects) for(const auto & objInfo : closeObjects)
{ {
Zone::Lock lock(zone.areaMutex); Zone::Lock lock(zone.areaMutex);
auto possibleArea = zone.areaPossible();
rmg::Object rmgObject(*objInfo.obj); rmg::Object rmgObject(*objInfo.obj);
rmgObject.setTemplate(zone.getTerrainType(), zone.getRand()); rmgObject.setTemplate(zone.getTerrainType(), zone.getRand());
bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY)); bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, auto path = placeAndConnectObject(zone.areaPossible().get(), rmgObject,
[this, &rmgObject](const int3 & tile) [this, &rmgObject](const int3 & tile)
{ {
float dist = rmgObject.getArea().distanceSqr(zone.getPos()); float dist = rmgObject.getArea().distanceSqr(zone.getPos());
@ -470,15 +512,15 @@ bool ObjectManager::createRequiredObjects()
rmg::Object rmgNearObject(*nearby.obj); rmg::Object rmgNearObject(*nearby.obj);
std::set<int3> blockedArea = targetObject->getBlockedPos(); std::set<int3> blockedArea = targetObject->getBlockedPos();
rmg::Area possibleArea(rmg::Area(rmg::Tileset(blockedArea.begin(), blockedArea.end())).getBorderOutside()); rmg::Area areaForObject(rmg::Area(rmg::Tileset(blockedArea.begin(), blockedArea.end())).getBorderOutside());
possibleArea.intersect(zone.areaPossible()); areaForObject.intersect(zone.areaPossible().get());
if(possibleArea.empty()) if(areaForObject.empty())
{ {
rmgNearObject.clear(); rmgNearObject.clear();
continue; continue;
} }
rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(possibleArea.getTiles(), zone.getRand())); rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(areaForObject.getTiles(), zone.getRand()));
placeObject(rmgNearObject, false, false); placeObject(rmgNearObject, false, false);
auto path = zone.searchPath(rmgNearObject.getVisitablePosition(), false); auto path = zone.searchPath(rmgNearObject.getVisitablePosition(), false);
if (path.valid()) if (path.valid())
@ -515,8 +557,6 @@ bool ObjectManager::createRequiredObjects()
void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance, bool createRoad/* = false*/) void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance, bool createRoad/* = false*/)
{ {
//object.finalize(map);
if (object.instances().size() == 1 && object.instances().front()->object().ID == Obj::MONSTER) if (object.instances().size() == 1 && object.instances().front()->object().ID == Obj::MONSTER)
{ {
//Fix for HoTA offset - lonely guards //Fix for HoTA offset - lonely guards
@ -539,14 +579,16 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
} }
object.finalize(map, zone.getRand()); object.finalize(map, zone.getRand());
{
Zone::Lock lock(zone.areaMutex); Zone::Lock lock(zone.areaMutex);
zone.areaPossible().subtract(object.getArea());
bool keepVisitable = zone.freePaths().contains(object.getVisitablePosition()); zone.areaPossible()->subtract(object.getArea());
zone.freePaths().subtract(object.getArea()); //just to avoid areas overlapping bool keepVisitable = zone.freePaths()->contains(object.getVisitablePosition());
zone.freePaths()->subtract(object.getArea()); //just to avoid areas overlapping
if(keepVisitable) if(keepVisitable)
zone.freePaths().add(object.getVisitablePosition()); zone.freePaths()->add(object.getVisitablePosition());
zone.areaUsed().unite(object.getArea()); zone.areaUsed()->unite(object.getArea());
zone.areaUsed().erase(object.getVisitablePosition()); zone.areaUsed()->erase(object.getVisitablePosition());
if(guarded) //We assume the monster won't be guarded if(guarded) //We assume the monster won't be guarded
{ {
@ -554,16 +596,16 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
guardedArea.add(object.instances().back()->getVisitablePosition()); guardedArea.add(object.instances().back()->getVisitablePosition());
auto areaToBlock = object.getAccessibleArea(true); auto areaToBlock = object.getAccessibleArea(true);
areaToBlock.subtract(guardedArea); areaToBlock.subtract(guardedArea);
zone.areaPossible().subtract(areaToBlock); zone.areaPossible()->subtract(areaToBlock);
for(const auto & i : areaToBlock.getTilesVector()) for(const auto & i : areaToBlock.getTilesVector())
if(map.isOnMap(i) && map.isPossible(i)) if(map.isOnMap(i) && map.isPossible(i))
map.setOccupied(i, ETileType::BLOCKED); map.setOccupied(i, ETileType::BLOCKED);
} }
lock.unlock(); }
if (updateDistance) if (updateDistance)
{ {
//Update distances in every adjacent zone in case of wide connection //Update distances in every adjacent zone (including this one) in case of wide connection
std::set<TRmgTemplateZoneId> adjacentZones; std::set<TRmgTemplateZoneId> adjacentZones;
auto objectArea = object.getArea(); auto objectArea = object.getArea();
@ -577,8 +619,15 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
} }
} }
for (auto id : adjacentZones) // FIXME: Possible deadlock by two managers updating each other
// At this point areaMutex is locked
// TODO: Generic function for multiple spinlocks
//std::vector sorted(adjacentZones.begin(), adjacentZones.end());
//std::sort(sorted.begin(), sorted.end());
//for (auto id : adjacentZones)
..for (auto id : sorted)
{ {
//TODO: Test again with sorted order?
auto otherZone = map.getZones().at(id); auto otherZone = map.getZones().at(id);
if ((otherZone->getType() == ETemplateZoneType::WATER) == (zone.getType() == ETemplateZoneType::WATER)) if ((otherZone->getType() == ETemplateZoneType::WATER) == (zone.getType() == ETemplateZoneType::WATER))
{ {

View File

@ -37,22 +37,26 @@ void ObstaclePlacer::process()
collectPossibleObstacles(zone.getTerrainType()); collectPossibleObstacles(zone.getTerrainType());
{ {
Zone::Lock lock(zone.areaMutex); //Zone::Lock lock(zone.areaMutex);
blockedArea = zone.area().getSubarea([this](const int3& t) auto area = zone.area();
auto areaPossible = zone.areaPossible();
auto areaUsed = zone.areaUsed();
blockedArea = area->getSubarea([this](const int3& t)
{ {
return map.shouldBeBlocked(t); return map.shouldBeBlocked(t);
}); });
blockedArea.subtract(zone.areaUsed()); blockedArea.subtract(*areaUsed);
zone.areaPossible().subtract(blockedArea); areaPossible->subtract(blockedArea);
prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea(); prohibitedArea = zone.freePaths() + areaUsed + manager->getVisitableArea();
//Progressively block tiles, but make sure they don't seal any gap between blocks //Progressively block tiles, but make sure they don't seal any gap between blocks
rmg::Area toBlock; rmg::Area toBlock;
do do
{ {
toBlock.clear(); toBlock.clear();
for (const auto& tile : zone.areaPossible().getTilesVector()) for (const auto& tile : areaPossible->getTilesVector())
{ {
rmg::Area neighbors; rmg::Area neighbors;
rmg::Area t; rmg::Area t;
@ -76,7 +80,7 @@ void ObstaclePlacer::process()
toBlock.add(tile); toBlock.add(tile);
} }
} }
zone.areaPossible().subtract(toBlock); areaPossible->subtract(toBlock);
for (const auto& tile : toBlock.getTilesVector()) for (const auto& tile : toBlock.getTilesVector())
{ {
map.setOccupied(tile, ETileType::BLOCKED); map.setOccupied(tile, ETileType::BLOCKED);
@ -84,7 +88,7 @@ void ObstaclePlacer::process()
} while (!toBlock.empty()); } while (!toBlock.empty());
prohibitedArea.unite(zone.areaPossible()); prohibitedArea.unite(areaPossible.get());
} }
auto objs = createObstacles(zone.getRand(), map.mapInstance->cb); auto objs = createObstacles(zone.getRand(), map.mapInstance->cb);
@ -119,7 +123,7 @@ void ObstaclePlacer::placeObject(rmg::Object & object, std::set<CGObjectInstance
std::pair<bool, bool> ObstaclePlacer::verifyCoverage(const int3 & t) const std::pair<bool, bool> ObstaclePlacer::verifyCoverage(const int3 & t) const
{ {
return {map.shouldBeBlocked(t), zone.areaPossible().contains(t)}; return {map.shouldBeBlocked(t), zone.areaPossible()->contains(t)};
} }
void ObstaclePlacer::postProcess(const rmg::Object & object) void ObstaclePlacer::postProcess(const rmg::Object & object)
@ -141,7 +145,7 @@ bool ObstaclePlacer::isProhibited(const rmg::Area & objArea) const
if(prohibitedArea.overlap(objArea)) if(prohibitedArea.overlap(objArea))
return true; return true;
if(!zone.area().contains(objArea)) if(!zone.area()->contains(objArea))
return true; return true;
return false; return false;

View File

@ -106,14 +106,14 @@ char RiverPlacer::dump(const int3 & t)
return '2'; return '2';
if(source.contains(t)) if(source.contains(t))
return '1'; return '1';
if(zone.area().contains(t)) if(zone.area()->contains(t))
return ' '; return ' ';
return '?'; return '?';
} }
void RiverPlacer::addRiverNode(const int3 & node) void RiverPlacer::addRiverNode(const int3 & node)
{ {
assert(zone.area().contains(node)); assert(zone.area()->contains(node));
riverNodes.insert(node); riverNodes.insert(node);
} }
@ -140,14 +140,17 @@ void RiverPlacer::prepareHeightmap()
roads.unite(m->getRoads()); roads.unite(m->getRoads());
} }
for(const auto & t : zone.area().getTilesVector()) auto area = zone.area();
auto areaUsed = zone.areaUsed();
for(const auto & t : area->getTilesVector())
{ {
heightMap[t] = zone.getRand().nextInt(5); heightMap[t] = zone.getRand().nextInt(5);
if(roads.contains(t)) if(roads.contains(t))
heightMap[t] += 30.f; heightMap[t] += 30.f;
if(zone.areaUsed().contains(t)) if(areaUsed->contains(t))
heightMap[t] += 1000.f; heightMap[t] += 1000.f;
} }
@ -157,7 +160,7 @@ void RiverPlacer::prepareHeightmap()
for(int i = 0; i < map.width(); i += 2) for(int i = 0; i < map.width(); i += 2)
{ {
int3 t{i, j, zone.getPos().z}; int3 t{i, j, zone.getPos().z};
if(zone.area().contains(t)) if(area->contains(t))
heightMap[t] += 10.f; heightMap[t] += 10.f;
} }
} }
@ -167,9 +170,10 @@ void RiverPlacer::preprocess()
{ {
rmg::Area outOfMapTiles; rmg::Area outOfMapTiles;
std::map<TRmgTemplateZoneId, rmg::Area> neighbourZonesTiles; std::map<TRmgTemplateZoneId, rmg::Area> neighbourZonesTiles;
rmg::Area borderArea(zone.getArea().getBorder());
rmg::Area borderArea(zone.area()->getBorder());
TRmgTemplateZoneId connectedToWaterZoneId = -1; TRmgTemplateZoneId connectedToWaterZoneId = -1;
for(const auto & t : zone.getArea().getBorderOutside()) for(const auto & t : zone.area()->getBorderOutside())
{ {
if(!map.isOnMap(t)) if(!map.isOnMap(t))
{ {
@ -182,6 +186,7 @@ void RiverPlacer::preprocess()
neighbourZonesTiles[map.getZoneID(t)].add(t); neighbourZonesTiles[map.getZoneID(t)].add(t);
} }
} }
rmg::Area outOfMapInternal(outOfMapTiles.getBorderOutside()); rmg::Area outOfMapInternal(outOfMapTiles.getBorderOutside());
outOfMapInternal.intersect(borderArea); outOfMapInternal.intersect(borderArea);
@ -297,7 +302,7 @@ void RiverPlacer::preprocess()
prepareHeightmap(); prepareHeightmap();
//decorative river //decorative river
if(!sink.empty() && !source.empty() && riverNodes.empty() && !zone.areaPossible().empty()) if(!sink.empty() && !source.empty() && riverNodes.empty() && !zone.areaPossible()->empty())
{ {
addRiverNode(*RandomGeneratorUtil::nextItem(source.getTilesVector(), zone.getRand())); addRiverNode(*RandomGeneratorUtil::nextItem(source.getTilesVector(), zone.getRand()));
} }
@ -347,7 +352,7 @@ void RiverPlacer::connectRiver(const int3 & tile)
return cost; return cost;
}; };
auto availableArea = zone.area() - prohibit; auto availableArea = zone.area().get() - prohibit;
rmg::Path pathToSource(availableArea); rmg::Path pathToSource(availableArea);
pathToSource.connect(source); pathToSource.connect(source);

View File

@ -144,8 +144,8 @@ void RoadPlacer::drawRoads(bool secondary)
return !terrain->isPassable() || !terrain->isLand(); return !terrain->isPassable() || !terrain->isLand();
}); });
zone.areaPossible().subtract(roads); zone.areaPossible()->subtract(roads);
zone.freePaths().unite(roads); zone.freePaths()->unite(roads);
} }
if(!generator.getMapGenOptions().isRoadEnabled()) if(!generator.getMapGenOptions().isRoadEnabled())

View File

@ -68,7 +68,7 @@ char RockFiller::dump(const int3 & t)
{ {
if(!map.getTile(t).terType->isPassable()) if(!map.getTile(t).terType->isPassable())
{ {
return zone.area().contains(t) ? 'R' : 'E'; return zone.area()->contains(t) ? 'R' : 'E';
} }
return Modificator::dump(t); return Modificator::dump(t);
} }

View File

@ -40,25 +40,28 @@ void RockPlacer::blockRock()
accessibleArea.unite(m->getVisitableArea()); accessibleArea.unite(m->getVisitableArea());
//negative approach - create rock tiles first, then make sure all accessible tiles have no rock //negative approach - create rock tiles first, then make sure all accessible tiles have no rock
rockArea = zone.area().getSubarea([this](const int3 & t) rockArea = zone.area()->getSubarea([this](const int3 & t)
{ {
return map.shouldBeBlocked(t); return map.shouldBeBlocked(t);
}); });
} }
void RockPlacer::postProcess() void RockPlacer::postProcess()
{
{ {
Zone::Lock lock(zone.areaMutex); Zone::Lock lock(zone.areaMutex);
//Finally mark rock tiles as occupied, spawn no obstacles there //Finally mark rock tiles as occupied, spawn no obstacles there
rockArea = zone.area().getSubarea([this](const int3 & t) rockArea = zone.area()->getSubarea([this](const int3 & t)
{ {
return !map.getTile(t).terType->isPassable(); return !map.getTile(t).terType->isPassable();
}); });
zone.areaUsed().unite(rockArea); zone.areaUsed()->unite(rockArea);
zone.areaPossible().subtract(rockArea); zone.areaPossible()->subtract(rockArea);
}
//RecursiveLock lock(externalAccessMutex);
//TODO: Might need mutex here as well
if(auto * m = zone.getModificator<RiverPlacer>()) if(auto * m = zone.getModificator<RiverPlacer>())
m->riverProhibit().unite(rockArea); m->riverProhibit().unite(rockArea);
if(auto * m = zone.getModificator<RoadPlacer>()) if(auto * m = zone.getModificator<RoadPlacer>())
@ -84,7 +87,7 @@ char RockPlacer::dump(const int3 & t)
{ {
if(!map.getTile(t).terType->isPassable()) if(!map.getTile(t).terType->isPassable())
{ {
return zone.area().contains(t) ? 'R' : 'E'; return zone.area()->contains(t) ? 'R' : 'E';
} }
return Modificator::dump(t); return Modificator::dump(t);
} }

View File

@ -28,7 +28,7 @@ void TerrainPainter::process()
{ {
initTerrainType(); initTerrainType();
auto v = zone.getArea().getTilesVector(); auto v = zone.area()->getTilesVector();
mapProxy->drawTerrain(zone.getRand(), v, zone.getTerrainType()); mapProxy->drawTerrain(zone.getRand(), v, zone.getTerrainType());
} }

View File

@ -146,7 +146,7 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
int3 position(-1, -1, -1); int3 position(-1, -1, -1);
{ {
Zone::Lock lock(zone.areaMutex); Zone::Lock lock(zone.areaMutex);
position = manager.findPlaceForObject(zone.areaPossible(), rmgObject, [this](const int3& t) position = manager.findPlaceForObject(zone.areaPossible().get(), rmgObject, [this](const int3& t)
{ {
float distance = zone.getPos().dist2dSQ(t); float distance = zone.getPos().dist2dSQ(t);
return 100000.f - distance; //some big number return 100000.f - distance; //some big number
@ -169,8 +169,8 @@ void TownPlacer::cleanupBoundaries(const rmg::Object & rmgObject)
if (map.isOnMap(t)) if (map.isOnMap(t))
{ {
map.setOccupied(t, ETileType::FREE); map.setOccupied(t, ETileType::FREE);
zone.areaPossible().erase(t); zone.areaPossible()->erase(t);
zone.freePaths().add(t); zone.freePaths()->add(t);
} }
} }
} }

View File

@ -852,11 +852,7 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
return oi1.value < oi2.value; return oi1.value < oi2.value;
}); });
size_t size = 0; const size_t size = zone.area()->getTilesVector().size();
{
Zone::Lock lock(zone.areaMutex);
size = zone.getArea().getTilesVector().size();
}
int totalDensity = 0; int totalDensity = 0;
@ -920,9 +916,11 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
auto path = rmg::Path::invalid(); auto path = rmg::Path::invalid();
{
Zone::Lock lock(zone.areaMutex); //We are going to subtract this area Zone::Lock lock(zone.areaMutex); //We are going to subtract this area
auto possibleArea = zone.areaPossible(); // FIXME: Possible area will be regenerated for every object
possibleArea.erase_if([this, &minDistance](const int3& tile) -> bool auto searchArea = zone.areaPossible().get();
searchArea.erase_if([this, &minDistance](const int3& tile) -> bool
{ {
auto ti = map.getTileInfo(tile); auto ti = map.getTileInfo(tile);
return (ti.getNearestObjectDistance() < minDistance); return (ti.getNearestObjectDistance() < minDistance);
@ -930,7 +928,7 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
if (guarded) if (guarded)
{ {
path = manager.placeAndConnectObject(possibleArea, rmgObject, [this, &rmgObject, &minDistance, &manager](const int3& tile) path = manager.placeAndConnectObject(searchArea, rmgObject, [this, &rmgObject, &minDistance, &manager](const int3& tile)
{ {
float bestDistance = 10e9; float bestDistance = 10e9;
for (const auto& t : rmgObject.getArea().getTilesVector()) for (const auto& t : rmgObject.getArea().getTilesVector())
@ -945,7 +943,7 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
const auto & guardedArea = rmgObject.instances().back()->getAccessibleArea(); const auto & guardedArea = rmgObject.instances().back()->getAccessibleArea();
const auto areaToBlock = rmgObject.getAccessibleArea(true) - guardedArea; const auto areaToBlock = rmgObject.getAccessibleArea(true) - guardedArea;
if (zone.freePaths().overlap(areaToBlock) || manager.getVisitableArea().overlap(areaToBlock)) if (zone.freePaths()->overlap(areaToBlock) || manager.getVisitableArea().overlap(areaToBlock))
return -1.f; return -1.f;
return bestDistance; return bestDistance;
@ -953,9 +951,10 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
} }
else else
{ {
path = manager.placeAndConnectObject(possibleArea, rmgObject, minDistance, guarded, false, ObjectManager::OptimizeType::DISTANCE); path = manager.placeAndConnectObject(searchArea, rmgObject, minDistance, guarded, false, ObjectManager::OptimizeType::DISTANCE);
} }
lock.unlock(); }
//lock.unlock();
if (path.valid()) if (path.valid())
{ {

View File

@ -43,13 +43,13 @@ void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent)
if(waterContent == EWaterContent::NONE || zone.isUnderground() || zone.getType() == ETemplateZoneType::WATER) if(waterContent == EWaterContent::NONE || zone.isUnderground() || zone.getType() == ETemplateZoneType::WATER)
return; //do nothing return; //do nothing
distanceMap = zone.area().computeDistanceMap(reverseDistanceMap); distanceMap = zone.area()->computeDistanceMap(reverseDistanceMap);
//add border tiles as water for ISLANDS //add border tiles as water for ISLANDS
if(waterContent == EWaterContent::ISLANDS) if(waterContent == EWaterContent::ISLANDS)
{ {
waterArea.unite(collectDistantTiles(zone, zone.getSize() + 1)); waterArea.unite(collectDistantTiles(zone, zone.getSize() + 1));
waterArea.unite(zone.area().getBorder()); waterArea.unite(zone.area()->getBorder());
} }
//protect some parts from water for NORMAL //protect some parts from water for NORMAL
@ -199,7 +199,7 @@ void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent)
std::vector<int3> groundCoast; std::vector<int3> groundCoast;
map.foreachDirectNeighbour(tile, [this, &groundCoast](const int3 & t) map.foreachDirectNeighbour(tile, [this, &groundCoast](const int3 & t)
{ {
if(!waterArea.contains(t) && zone.area().contains(t)) //for ground tiles of same zone if(!waterArea.contains(t) && zone.area()->contains(t)) //for ground tiles of same zone
{ {
groundCoast.push_back(t); groundCoast.push_back(t);
} }
@ -223,12 +223,12 @@ void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent)
{ {
Zone::Lock waterLock(map.getZones()[waterZoneId]->areaMutex); Zone::Lock waterLock(map.getZones()[waterZoneId]->areaMutex);
map.getZones()[waterZoneId]->area().unite(waterArea); map.getZones()[waterZoneId]->area()->unite(waterArea);
} }
Zone::Lock lock(zone.areaMutex); Zone::Lock lock(zone.areaMutex);
zone.area().subtract(waterArea); zone.area()->subtract(waterArea);
zone.areaPossible().subtract(waterArea); zone.areaPossible()->subtract(waterArea);
distanceMap = zone.area().computeDistanceMap(reverseDistanceMap); distanceMap = zone.area()->computeDistanceMap(reverseDistanceMap);
} }
void WaterAdopter::setWaterZone(TRmgTemplateZoneId water) void WaterAdopter::setWaterZone(TRmgTemplateZoneId water)

View File

@ -34,45 +34,51 @@ VCMI_LIB_NAMESPACE_BEGIN
void WaterProxy::process() void WaterProxy::process()
{ {
for(const auto & t : zone.area().getTilesVector()) auto area = zone.area();
for(const auto & t : area->getTilesVector())
{ {
map.setZoneID(t, zone.getId()); map.setZoneID(t, zone.getId());
map.setOccupied(t, ETileType::POSSIBLE); map.setOccupied(t, ETileType::POSSIBLE);
} }
auto v = zone.getArea().getTilesVector(); auto v = area->getTilesVector();
mapProxy->drawTerrain(zone.getRand(), v, zone.getTerrainType()); mapProxy->drawTerrain(zone.getRand(), v, zone.getTerrainType());
//check terrain type //check terrain type
for([[maybe_unused]] const auto & t : zone.area().getTilesVector()) for([[maybe_unused]] const auto & t : area->getTilesVector())
{ {
assert(map.isOnMap(t)); assert(map.isOnMap(t));
assert(map.getTile(t).terType->getId() == zone.getTerrainType()); assert(map.getTile(t).terType->getId() == zone.getTerrainType());
} }
// FIXME: Possible deadlock for 2 zones
auto areaPossible = zone.areaPossible();
for(const auto & z : map.getZones()) for(const auto & z : map.getZones())
{ {
if(z.second->getId() == zone.getId()) if(z.second->getId() == zone.getId())
continue; continue;
Zone::Lock lock(z.second->areaMutex); auto secondArea = z.second->area();
for(const auto & t : z.second->area().getTilesVector()) auto secondAreaPossible = z.second->areaPossible();
for(const auto & t : secondArea->getTilesVector())
{ {
if(map.getTile(t).terType->getId() == zone.getTerrainType()) if(map.getTile(t).terType->getId() == zone.getTerrainType())
{ {
z.second->areaPossible().erase(t); secondArea->erase(t);
z.second->area().erase(t); secondAreaPossible->erase(t);
zone.area().add(t); area->add(t);
zone.areaPossible().add(t); areaPossible->add(t);
map.setZoneID(t, zone.getId()); map.setZoneID(t, zone.getId());
map.setOccupied(t, ETileType::POSSIBLE); map.setOccupied(t, ETileType::POSSIBLE);
} }
} }
} }
if(!zone.area().contains(zone.getPos())) if(!area->contains(zone.getPos()))
{ {
zone.setPos(zone.area().getTilesVector().front()); zone.setPos(area->getTilesVector().front());
} }
zone.initFreeTiles(); zone.initFreeTiles();
@ -105,7 +111,7 @@ void WaterProxy::collectLakes()
{ {
RecursiveLock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
int lakeId = 0; int lakeId = 0;
for(const auto & lake : connectedAreas(zone.getArea(), true)) for(const auto & lake : connectedAreas(zone.area().get(), true))
{ {
lakes.push_back(Lake{}); lakes.push_back(Lake{});
lakes.back().area = lake; lakes.back().area = lake;
@ -117,8 +123,8 @@ void WaterProxy::collectLakes()
lakeMap[t] = lakeId; lakeMap[t] = lakeId;
//each lake must have at least one free tile //each lake must have at least one free tile
if(!lake.overlap(zone.freePaths())) if(!lake.overlap(zone.freePaths().get()))
zone.freePaths().add(*lakes.back().reverseDistanceMap[lakes.back().reverseDistanceMap.size() - 1].begin()); zone.freePaths()->add(*lakes.back().reverseDistanceMap[lakes.back().reverseDistanceMap.size() - 1].begin());
++lakeId; ++lakeId;
} }
@ -151,7 +157,7 @@ RouteInfo WaterProxy::waterRoute(Zone & dst)
} }
Zone::Lock lock(dst.areaMutex); Zone::Lock lock(dst.areaMutex);
dst.areaPossible().subtract(lake.neighbourZones[dst.getId()]); dst.areaPossible()->subtract(lake.neighbourZones[dst.getId()]);
continue; continue;
} }
@ -349,7 +355,7 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, bool
} }
//try to place shipyard close to boarding position and appropriate water access //try to place shipyard close to boarding position and appropriate water access
auto path = manager->placeAndConnectObject(land.areaPossible(), rmgObject, [&rmgObject, &shipPositions, &boardingPosition](const int3 & tile) auto path = manager->placeAndConnectObject(land.areaPossible().get(), rmgObject, [&rmgObject, &shipPositions, &boardingPosition](const int3 & tile)
{ {
//Must only check the border of shipyard and not the added guard //Must only check the border of shipyard and not the added guard
rmg::Area shipyardOut = rmgObject.instances().front()->getBlockedArea().getBorderOutside(); rmg::Area shipyardOut = rmgObject.instances().front()->getBlockedArea().getBorderOutside();
@ -361,9 +367,9 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, bool
}, guarded, true, ObjectManager::OptimizeType::NONE); }, guarded, true, ObjectManager::OptimizeType::NONE);
//search path to boarding position //search path to boarding position
auto searchArea = land.areaPossible() - rmgObject.getArea(); auto searchArea = land.areaPossible().get() - rmgObject.getArea();
rmg::Path pathToBoarding(searchArea); rmg::Path pathToBoarding(searchArea);
pathToBoarding.connect(land.freePaths()); pathToBoarding.connect(land.freePaths().get());
pathToBoarding.connect(path); pathToBoarding.connect(path);
pathToBoarding = pathToBoarding.search(boardingPosition, false); pathToBoarding = pathToBoarding.search(boardingPosition, false);
@ -391,7 +397,7 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, bool
manager->placeObject(rmgObject, guarded, true, createRoad); manager->placeObject(rmgObject, guarded, true, createRoad);
zone.areaPossible().subtract(shipyardOutToBlock); zone.areaPossible()->subtract(shipyardOutToBlock);
for(const auto & i : shipyardOutToBlock.getTilesVector()) for(const auto & i : shipyardOutToBlock.getTilesVector())
if(map.isOnMap(i) && map.isPossible(i)) if(map.isOnMap(i) && map.isPossible(i))
map.setOccupied(i, ETileType::BLOCKED); map.setOccupied(i, ETileType::BLOCKED);

View File

@ -43,22 +43,27 @@ void WaterRoutes::process()
result.push_back(wproxy->waterRoute(*z.second)); result.push_back(wproxy->waterRoute(*z.second));
} }
Zone::Lock lock(zone.areaMutex); //Zone::Lock lock(zone.areaMutex);
auto area = zone.area();
auto freePaths = zone.freePaths();
auto areaPossible = zone.areaPossible();
auto areaUsed = zone.areaUsed();
//prohibit to place objects on sealed off lakes //prohibit to place objects on sealed off lakes
for(const auto & lake : wproxy->getLakes()) for(const auto & lake : wproxy->getLakes())
{ {
if((lake.area * zone.freePaths()).getTilesVector().size() == 1) if((lake.area * freePaths.get()).getTilesVector().size() == 1)
{ {
zone.freePaths().subtract(lake.area); freePaths->subtract(lake.area);
zone.areaPossible().subtract(lake.area); areaPossible->subtract(lake.area);
} }
} }
//prohibit to place objects on the borders //prohibit to place objects on the borders
for(const auto & t : zone.area().getBorder()) for(const auto & t : area->getBorder())
{ {
if(zone.areaPossible().contains(t)) if(areaPossible->contains(t))
{ {
std::vector<int3> landTiles; std::vector<int3> landTiles;
map.foreachDirectNeighbour(t, [this, &landTiles, &t](const int3 & c) map.foreachDirectNeighbour(t, [this, &landTiles, &t](const int3 & c)
@ -74,8 +79,8 @@ void WaterRoutes::process()
int3 o = landTiles[0] + landTiles[1]; int3 o = landTiles[0] + landTiles[1];
if(o.x * o.x * o.y * o.y == 1) if(o.x * o.x * o.y * o.y == 1)
{ {
zone.areaPossible().erase(t); areaPossible->erase(t);
zone.areaUsed().add(t); areaUsed->add(t);
} }
} }
} }
@ -96,6 +101,9 @@ void WaterRoutes::init()
char WaterRoutes::dump(const int3 & t) char WaterRoutes::dump(const int3 & t)
{ {
auto area = zone.area();
auto freePaths = zone.freePaths();
for(auto & i : result) for(auto & i : result)
{ {
if(t == i.boarding) if(t == i.boarding)
@ -106,15 +114,15 @@ char WaterRoutes::dump(const int3 & t)
return '#'; return '#';
if(i.water.contains(t)) if(i.water.contains(t))
{ {
if(zone.freePaths().contains(t)) if(freePaths->contains(t))
return '+'; return '+';
else else
return '-'; return '-';
} }
} }
if(zone.freePaths().contains(t)) if(freePaths->contains(t))
return '.'; return '.';
if(zone.area().contains(t)) if(area->contains(t))
return '~'; return '~';
return ' '; return ' ';
} }