瀏覽代碼

Parallel RMG works fine for maps without water.

Tomasz Zieliński 2 年之前
父節點
當前提交
73d9f5bd0a

+ 6 - 1
cmake_modules/VCMI_lib.cmake

@@ -94,6 +94,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/mapping/MapFormatH3M.cpp
 		${MAIN_LIB_DIR}/mapping/MapReaderH3M.cpp
 		${MAIN_LIB_DIR}/mapping/MapFormatJson.cpp
+		${MAIN_LIB_DIR}/mapping/ObstacleProxy.cpp
 
 		${MAIN_LIB_DIR}/registerTypes/RegisterTypes.cpp
 		${MAIN_LIB_DIR}/registerTypes/TypesClientPacks1.cpp
@@ -135,9 +136,11 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/rmg/WaterProxy.cpp
 		${MAIN_LIB_DIR}/rmg/WaterRoutes.cpp
 		${MAIN_LIB_DIR}/rmg/RockPlacer.cpp
+		${MAIN_LIB_DIR}/rmg/RockFiller.cpp
 		${MAIN_LIB_DIR}/rmg/ObstaclePlacer.cpp
 		${MAIN_LIB_DIR}/rmg/RiverPlacer.cpp
 		${MAIN_LIB_DIR}/rmg/TerrainPainter.cpp
+		${MAIN_LIB_DIR}/rmg/threadpool/MapProxy.cpp
 
 		${MAIN_LIB_DIR}/serializer/BinaryDeserializer.cpp
 		${MAIN_LIB_DIR}/serializer/BinarySerializer.cpp
@@ -387,6 +390,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/mapping/MapFormatH3M.h
 		${MAIN_LIB_DIR}/mapping/MapReaderH3M.h
 		${MAIN_LIB_DIR}/mapping/MapFormatJson.h
+		${MAIN_LIB_DIR}/mapping/ObstacleProxy.h
 
 		${MAIN_LIB_DIR}/registerTypes/RegisterTypes.h
 
@@ -421,13 +425,14 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/rmg/WaterProxy.h
 		${MAIN_LIB_DIR}/rmg/WaterRoutes.h
 		${MAIN_LIB_DIR}/rmg/RockPlacer.h
+		${MAIN_LIB_DIR}/rmg/RockFiller.h
 		${MAIN_LIB_DIR}/rmg/ObstaclePlacer.h
 		${MAIN_LIB_DIR}/rmg/RiverPlacer.h
 		${MAIN_LIB_DIR}/rmg/TerrainPainter.h
 		${MAIN_LIB_DIR}/rmg/float3.h
 		${MAIN_LIB_DIR}/rmg/threadpool/BlockingQueue.h
 		${MAIN_LIB_DIR}/rmg/threadpool/ThreadPool.h
-		${MAIN_LIB_DIR}/rmg/threadpool/JobProvider.h
+		${MAIN_LIB_DIR}/rmg/threadpool/MapProxy.h
 
 		${MAIN_LIB_DIR}/serializer/BinaryDeserializer.h
 		${MAIN_LIB_DIR}/serializer/BinarySerializer.h

+ 18 - 17
lib/rmg/CMapGenerator.cpp

@@ -101,7 +101,7 @@ const CMapGenOptions& CMapGenerator::getMapGenOptions() const
 void CMapGenerator::initPrisonsRemaining()
 {
 	allowedPrisons = 0;
-	for (auto isAllowed : map->map().allowedHeroes)
+	for (auto isAllowed : map->getMap(this).allowedHeroes)
 	{
 		if (isAllowed)
 			allowedPrisons++;
@@ -133,7 +133,7 @@ std::unique_ptr<CMap> CMapGenerator::generate()
 		initQuestArtsRemaining();
 		genZones();
 		Load::Progress::step();
-		map->map().calculateGuardingGreaturePositions(); //clear map so that all tiles are unguarded
+		map->getMap(this).calculateGuardingGreaturePositions(); //clear map so that all tiles are unguarded
 		map->addModificators();
 		Load::Progress::step(3);
 		fillZones();
@@ -164,7 +164,7 @@ std::string CMapGenerator::getMapDescription() const
     std::stringstream ss;
     ss << boost::str(boost::format(std::string("Map created by the Random Map Generator.\nTemplate was %s, Random seed was %d, size %dx%d") +
         ", levels %d, players %d, computers %d, water %s, monster %s, VCMI map") % mapTemplate->getName() %
-		randomSeed % map->map().width % map->map().height % static_cast<int>(map->map().levels()) % static_cast<int>(mapGenOptions.getPlayerCount()) %
+		randomSeed % map->width() % map->height() % static_cast<int>(map->levels()) % static_cast<int>(mapGenOptions.getPlayerCount()) %
 		static_cast<int>(mapGenOptions.getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions.getWaterContent()] %
 		monsterStrengthStr[monsterStrengthIndex]);
 
@@ -263,10 +263,10 @@ void CMapGenerator::addPlayerInfo()
 			teamNumbers[j].erase(itTeam);
 		}
 		teamsTotal.insert(player.team.getNum());
-		map->map().players[pSettings.getColor().getNum()] = player;
+		map->getMap(this).players[pSettings.getColor().getNum()] = player;
 	}
 
-	map->map().howManyTeams = teamsTotal.size();
+	map->getMap(this).howManyTeams = teamsTotal.size();
 }
 
 void CMapGenerator::genZones()
@@ -374,7 +374,7 @@ void CMapGenerator::fillZones()
 	}
 	auto grailZone = *RandomGeneratorUtil::nextItem(treasureZones, rand);
 
-	map->map().grailPos = *RandomGeneratorUtil::nextItem(grailZone->freePaths().getTiles(), rand);
+	map->getMap(this).grailPos = *RandomGeneratorUtil::nextItem(grailZone->freePaths().getTiles(), rand);
 
 	logGlobal->info("Zones filled successfully");
 
@@ -383,13 +383,14 @@ void CMapGenerator::fillZones()
 
 void CMapGenerator::addHeaderInfo()
 {
-	map->map().version = EMapFormat::VCMI;
-	map->map().width = mapGenOptions.getWidth();
-	map->map().height = mapGenOptions.getHeight();
-	map->map().twoLevel = mapGenOptions.getHasTwoLevels();
-	map->map().name = VLC->generaltexth->allTexts[740];
-	map->map().description = getMapDescription();
-	map->map().difficulty = 1;
+	auto& m = map->getMap(this);
+	m.version = EMapFormat::VCMI;
+	m.width = mapGenOptions.getWidth();
+	m.height = mapGenOptions.getHeight();
+	m.twoLevel = mapGenOptions.getHasTwoLevels();
+	m.name = VLC->generaltexth->allTexts[740];
+	m.description = getMapDescription();
+	m.difficulty = 1;
 	addPlayerInfo();
 }
 
@@ -434,9 +435,9 @@ const std::vector<HeroTypeID> CMapGenerator::getAllPossibleHeroes() const
 {
 	//Skip heroes that were banned, including the ones placed in prisons
 	std::vector<HeroTypeID> ret;
-	for (int j = 0; j < map->map().allowedHeroes.size(); j++)
+	for (int j = 0; j < map->getMap(this).allowedHeroes.size(); j++)
 	{
-		if (map->map().allowedHeroes[j])
+		if (map->getMap(this).allowedHeroes[j])
 			ret.push_back(HeroTypeID(j));
 	}
 	return ret;
@@ -445,13 +446,13 @@ const std::vector<HeroTypeID> CMapGenerator::getAllPossibleHeroes() const
 void CMapGenerator::banQuestArt(const ArtifactID & id)
 {
 	//TODO: Protect with mutex
-	map->map().allowedArtifact[id] = false;
+	map->getMap(this).allowedArtifact[id] = false;
 }
 
 void CMapGenerator::banHero(const HeroTypeID & id)
 {
 	//TODO: Protect with mutex
-	map->map().allowedHeroes[id] = false;
+	map->getMap(this).allowedHeroes[id] = false;
 }
 
 Zone * CMapGenerator::getZoneWater() const

+ 0 - 1
lib/rmg/CMapGenerator.h

@@ -15,7 +15,6 @@
 #include "CMapGenOptions.h"
 #include "../int3.h"
 #include "CRmgTemplate.h"
-#include "threadpool/JobProvider.h"
 #include "../LoadProgress.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 4 - 5
lib/rmg/CZonePlacer.cpp

@@ -38,7 +38,7 @@ CZonePlacer::CZonePlacer(RmgMap & map)
 
 int3 CZonePlacer::cords(const float3 & f) const
 {
-	return int3(static_cast<si32>(std::max(0.f, (f.x * map.map().width) - 1)), static_cast<si32>(std::max(0.f, (f.y * map.map().height - 1))), f.z);
+	return int3(static_cast<si32>(std::max(0.f, (f.x * map.width()) - 1)), static_cast<si32>(std::max(0.f, (f.y * map.height() - 1))), f.z);
 }
 
 float CZonePlacer::getDistance (float distance) const
@@ -224,8 +224,7 @@ void CZonePlacer::placeOnGrid(CRandomGenerator* rand)
 
 								auto zoneType = zone->getType();
 								auto existingZoneType = existingZone->getType();
-								if ((zoneType == ETemplateZoneType::PLAYER_START || zoneType == ETemplateZoneType::CPU_START) && 
-									(existingZoneType == ETemplateZoneType::PLAYER_START || existingZoneType == ETemplateZoneType::CPU_START))
+								if (zone->getOwner() && existingZone->getOwner()) //Players participate in game
 								{
 									int firstPlayer = zone->getOwner().value();
 									int secondPlayer = existingZone->getOwner().value();
@@ -785,7 +784,7 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
 		zone->setPos(int3(total.x / size, total.y / size, total.z / size));
 	};
 
-	int levels = map.map().levels();
+	int levels = map.levels();
 
 	/*
 	1. Create Voronoi diagram
@@ -863,7 +862,7 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
 
 			//make sure that terrain inside zone is not a rock
 			//FIXME: reorder actions?
-			paintZoneTerrain(*zone.second, *rand, map, ETerrainId::SUBTERRANEAN);
+			paintZoneTerrain(*zone.second, *rand, map.getMapProxy(), ETerrainId::SUBTERRANEAN);
 		}
 	}
 	logGlobal->info("Finished zone colouring");

+ 5 - 3
lib/rmg/ConnectionsPlacer.cpp

@@ -25,6 +25,7 @@
 #include "WaterAdopter.h"
 #include "WaterProxy.h"
 #include "TownPlacer.h"
+#include <boost/interprocess/sync/scoped_lock.hpp>
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -101,7 +102,7 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
 			guardPos = zone.areaPossible().nearest(borderPos);
 			assert(borderPos != guardPos);
 
-			float dist = map.getTile(guardPos).getNearestObjectDistance();
+			float dist = map.getTileInfo(guardPos).getNearestObjectDistance();
 			if (dist >= 3) //Don't place guards at adjacent tiles
 			{
 
@@ -228,11 +229,12 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
 			bool guarded2 = managerOther.addGuard(rmgGate2, connection.getGuardStrength(), true);
 			int minDist = 3;
 			
+			std::scoped_lock doubleLock(zone.areaMutex, otherZone->areaMutex);
 			rmg::Path path2(otherZone->area());
 			rmg::Path path1 = manager.placeAndConnectObject(commonArea, rmgGate1, [this, minDist, &path2, &rmgGate1, &zShift, guarded2, &managerOther, &rmgGate2	](const int3 & tile)
 			{
-				auto ti = map.getTile(tile);
-				auto otherTi = map.getTile(tile - zShift);
+				auto ti = map.getTileInfo(tile);
+				auto otherTi = map.getTileInfo(tile - zShift);
 				float dist = ti.getNearestObjectDistance();
 				float otherDist = otherTi.getNearestObjectDistance();
 				if(dist < minDist || otherDist < minDist)

+ 7 - 7
lib/rmg/Functions.cpp

@@ -51,6 +51,7 @@ void createBorder(RmgMap & gen, Zone & zone)
 		return gen.isOnMap(tile) && gen.getZones()[gen.getZoneID(tile)]->getType() != ETemplateZoneType::WATER;
 	});
 
+	Zone::Lock lock(zone.areaMutex); //Protect from erasing same tiles again
 	for(const auto & tile : blockBorder.getTilesVector())
 	{
 		if(gen.isPossible(tile))
@@ -70,11 +71,10 @@ void createBorder(RmgMap & gen, Zone & zone)
 	}
 }
 
-void paintZoneTerrain(const Zone & zone, CRandomGenerator & generator, RmgMap & map, TerrainId terrain)
+void paintZoneTerrain(const Zone & zone, CRandomGenerator & generator, std::shared_ptr<MapProxy> mapProxy, TerrainId terrain)
 {
 	auto v = zone.getArea().getTilesVector();
-	map.getEditManager()->getTerrainSelection().setSelection(v);
-	map.getEditManager()->drawTerrain(terrain, &generator);
+	mapProxy->drawTerrain(generator, v, terrain);
 }
 
 int chooseRandomAppearance(CRandomGenerator & generator, si32 ObjID, TerrainId terrain)
@@ -153,15 +153,15 @@ void initTerrainType(Zone & zone, CMapGenerator & gen)
 
 void createObstaclesCommon2(RmgMap & map, CRandomGenerator & generator)
 {
-	if(map.map().twoLevel)
+	if(map.levels())
 	{
 		//finally mark rock tiles as occupied, spawn no obstacles there
-		for(int x = 0; x < map.map().width; x++)
+		for(int x = 0; x < map.width(); x++)
 		{
-			for(int y = 0; y < map.map().height; y++)
+			for(int y = 0; y < map.height(); y++)
 			{
 				int3 tile(x, y, 1);
-				if(!map.map().getTile(tile).terType->isPassable())
+				if(!map.getTile(tile).terType->isPassable())
 				{
 					map.setOccupied(tile, ETileType::USED);
 				}

+ 1 - 1
lib/rmg/Functions.h

@@ -43,7 +43,7 @@ rmg::Tileset collectDistantTiles(const Zone & zone, int distance);
 
 void createBorder(RmgMap & gen, Zone & zone);
 
-void paintZoneTerrain(const Zone & zone, CRandomGenerator & generator, RmgMap & map, TerrainId terrainType);
+void paintZoneTerrain(const Zone & zone, CRandomGenerator & generator, std::shared_ptr<MapProxy> mapProxy, TerrainId terrainType);
 
 void initTerrainType(Zone & zone, CMapGenerator & gen);
 

+ 8 - 9
lib/rmg/Modificator.cpp

@@ -19,6 +19,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 Modificator::Modificator(Zone & zone, RmgMap & map, CMapGenerator & generator) : zone(zone), map(map), generator(generator)
 {
+	mapProxy = map.getMapProxy();
 }
 
 void Modificator::setName(const std::string & n)
@@ -45,18 +46,16 @@ bool Modificator::isReady()
 		{
 			if ((*it)->isFinished()) //OK
 			{
+				//This preceeder won't be checked in the future
 				it = preceeders.erase(it);
 			}
-			else if (!(*it)->isReady())
-			{
-				return false;
-			}
 			else
 			{
-				++it;
+				return false;
 			}
 		}
 
+		//If a job is finished, it should be already erased from a queue
 		return !finished;
 	}
 }
@@ -102,6 +101,7 @@ void Modificator::dependency(Modificator * modificator)
 {
 	if(modificator && modificator != this)
 	{
+		//TODO: use vstd::contains
 		if(std::find(preceeders.begin(), preceeders.end(), modificator) == preceeders.end())
 			preceeders.push_back(modificator);
 	}
@@ -119,10 +119,9 @@ void Modificator::postfunction(Modificator * modificator)
 void Modificator::dump()
 {
 	std::ofstream out(boost::to_string(boost::format("seed_%d_modzone_%d_%s.txt") % generator.getRandomSeed() % zone.getId() % getName()));
-	auto & mapInstance = map.map();
-	int levels = mapInstance.levels();
-	int width =  mapInstance.width;
-	int height = mapInstance.height;
+	int levels = map.levels();
+	int width =  map.width();
+	int height = map.height();
 	for(int z = 0; z < levels; z++)
 	{
 		for(int j=0; j<height; j++)

+ 27 - 4
lib/rmg/Modificator.h

@@ -12,14 +12,13 @@
 
 #include "../GameConstants.h"
 #include "../int3.h"
-#include "threadpool/JobProvider.h"
 #include "Zone.h"
+#include "threadpool/MapProxy.h"
 
 class RmgMap;
 class CMapGenerator;
 class Zone;
-
-
+class MapProxy;
 
 #define MODIFICATOR(x) x(Zone & z, RmgMap & m, CMapGenerator & g): Modificator(z, m, g) {setName(#x);}
 #define DEPENDENCY(x) 		dependency(zone.getModificator<x>());
@@ -57,14 +56,38 @@ public:
 
 protected:
 	RmgMap & map;
+	std::shared_ptr<MapProxy> mapProxy;
 	CMapGenerator & generator;
 	Zone & zone;
 
 	bool finished = false;
 	
-	mutable boost::shared_mutex externalAccessMutex; //Used to communicate between Modificators
+	mutable boost::recursive_mutex externalAccessMutex; //Used to communicate between Modificators
+	using RecursiveLock = boost::unique_lock<boost::recursive_mutex>;
 	using Lock = boost::unique_lock<boost::shared_mutex>;
 
+	template <typename TModificator>
+	std::vector<RecursiveLock> tryLockAll()
+	{
+		std::vector<RecursiveLock> locks;
+		for (auto & zone : map.getZones())
+		{
+			if (auto * m = zone.second->getModificator<TModificator>())
+			{
+				RecursiveLock lock(m->externalAccessMutex, boost::try_to_lock_t{});
+				if (lock.owns_lock())
+				{
+					locks.emplace_back(std::move(lock));
+				}
+				else //return empty
+				{
+					return std::vector<RecursiveLock>();
+				}
+			}
+		}
+		return locks;
+	}
+
 private:
 	virtual void process() = 0;
 

+ 16 - 4
lib/rmg/ObjectDistributor.cpp

@@ -27,18 +27,28 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 void ObjectDistributor::process()
 {
-	//Firts call will add objects to ALL zones, once they were added skip it
-	if (zone.getModificator<TreasurePlacer>()->getPossibleObjectsSize() == 0)
+	//Do that only once
+	auto lockVec = tryLockAll<ObjectDistributor>();
+	if (!lockVec.empty())
 	{
+		for(auto & z : map.getZones())
+		{
+			if(auto * m = z.second->getModificator<ObjectDistributor>())
+			{
+				if(m->isFinished())
+					return;
+			}
+		}
 		distributeLimitedObjects();
 		distributeSeerHuts();
+		finished = true;
 	}
 }
 
 void ObjectDistributor::init()
 {
-	DEPENDENCY(TownPlacer);
-	DEPENDENCY(TerrainPainter);
+	//All of the terrain types need to be determined
+	DEPENDENCY_ALL(TerrainPainter);
 	POSTFUNCTION(TreasurePlacer);
 }
 
@@ -84,6 +94,8 @@ void ObjectDistributor::distributeLimitedObjects()
 						//We already know there are some templates
 						auto templates = handler->getTemplates(zone->getTerrainType());
 
+						//FIXME: Templates empty?! Maybe zone changed terrain type over time?
+
 						//Assume the template with fewest terrains is the most suitable
 						auto temp = *boost::min_element(templates, [](std::shared_ptr<const ObjectTemplate> lhs, std::shared_ptr<const ObjectTemplate> rhs) -> bool
 						{

+ 29 - 12
lib/rmg/ObjectManager.cpp

@@ -16,6 +16,9 @@
 #include "RoadPlacer.h"
 #include "RiverPlacer.h"
 #include "WaterAdopter.h"
+#include "ConnectionsPlacer.h"
+#include "TownPlacer.h"
+#include "MinePlacer.h"
 #include "TreasurePlacer.h"
 #include "QuestArtifactPlacer.h"
 #include "../CCreatureHandler.h"
@@ -37,13 +40,16 @@ void ObjectManager::process()
 void ObjectManager::init()
 {
 	DEPENDENCY(WaterAdopter);
+	DEPENDENCY_ALL(ConnectionsPlacer); //Monoliths can be placed by other zone, too
+	DEPENDENCY(TownPlacer); //Only secondary towns
+	DEPENDENCY(MinePlacer);
 	POSTFUNCTION(RoadPlacer);
 	createDistancesPriorityQueue();
 }
 
 void ObjectManager::createDistancesPriorityQueue()
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	tilesByDistance.clear();
 	for(const auto & tile : zone.areaPossible().getTilesVector())
 	{
@@ -53,25 +59,25 @@ void ObjectManager::createDistancesPriorityQueue()
 
 void ObjectManager::addRequiredObject(CGObjectInstance * obj, si32 strength)
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	requiredObjects.emplace_back(obj, strength);
 }
 
 void ObjectManager::addCloseObject(CGObjectInstance * obj, si32 strength)
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	closeObjects.emplace_back(obj, strength);
 }
 
 void ObjectManager::addNearbyObject(CGObjectInstance * obj, CGObjectInstance * nearbyTarget)
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	nearbyObjects.emplace_back(obj, nearbyTarget);
 }
 
 void ObjectManager::updateDistances(const rmg::Object & obj)
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	tilesByDistance.clear();
 	for (auto tile : zone.areaPossible().getTiles()) //don't need to mark distance for not possible tiles
 	{
@@ -83,15 +89,15 @@ void ObjectManager::updateDistances(const rmg::Object & obj)
 
 const rmg::Area & ObjectManager::getVisitableArea() const
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	return objectsVisitableArea;
 }
 
 std::vector<CGObjectInstance*> ObjectManager::getMines() const
 {
-	Lock lock(externalAccessMutex);
 	std::vector<CGObjectInstance*> mines;
 
+	RecursiveLock lock(externalAccessMutex);
 	for(auto * object : objects)
 	{
 		if (object->ID == Obj::MINE)
@@ -161,6 +167,7 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
 		}
 	}
 	
+	//FIXME: Race condition for tiles? For Area?
 	if(result.valid())
 		obj.setPosition(result);
 	return result;
@@ -170,14 +177,14 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
 {
 	return findPlaceForObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile)
 	{
-		auto ti = map.getTile(tile);
+		auto ti = map.getTileInfo(tile);
 		float dist = ti.getNearestObjectDistance();
 		if(dist < min_dist)
 			return -1.f;
 
 		for(const auto & t : obj.getArea().getTilesVector())
 		{
-			if(map.getTile(t).getNearestObjectDistance() < min_dist)
+			if(map.getTileInfo(t).getNearestObjectDistance() < min_dist)
 				return -1.f;
 		}
 		
@@ -189,14 +196,14 @@ rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg
 {
 	return placeAndConnectObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile)
 	{
-		auto ti = map.getTile(tile);
+		auto ti = map.getTileInfo(tile);
 		float dist = ti.getNearestObjectDistance();
 		if(dist < min_dist)
 			return -1.f;
 
 		for(const auto & t : obj.getArea().getTilesVector())
 		{
-			if(map.getTile(t).getNearestObjectDistance() < min_dist)
+			if(map.getTileInfo(t).getNearestObjectDistance() < min_dist)
 				return -1.f;
 		}
 		
@@ -251,13 +258,17 @@ rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg
 bool ObjectManager::createRequiredObjects()
 {
 	logGlobal->trace("Creating required objects");
-		
+	
+	//RecursiveLock lock(externalAccessMutex); //Why could requiredObjects be modified during the loop?
 	for(const auto & object : requiredObjects)
 	{
 		auto * obj = object.first;
+		//FIXME: Invalid dObject inside object?
 		rmg::Object rmgObject(*obj);
 		rmgObject.setTemplate(zone.getTerrainType());
 		bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY));
+
+		Zone::Lock lock(zone.areaMutex);
 		auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
 		
 		if(!path.valid())
@@ -291,7 +302,11 @@ bool ObjectManager::createRequiredObjects()
 	for(const auto & object : closeObjects)
 	{
 		auto * obj = object.first;
+
+		//TODO: Wrap into same area proxy?
+		Zone::Lock lock(zone.areaMutex);
 		auto possibleArea = zone.areaPossible();
+
 		rmg::Object rmgObject(*obj);
 		rmgObject.setTemplate(zone.getTerrainType());
 		bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY));
@@ -352,6 +367,8 @@ bool ObjectManager::createRequiredObjects()
 void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance)
 {	
 	object.finalize(map);
+
+	Zone::Lock lock(zone.areaMutex);
 	zone.areaPossible().subtract(object.getArea());
 	bool keepVisitable = zone.freePaths().contains(object.getVisitablePosition());
 	zone.freePaths().subtract(object.getArea()); //just to avoid areas overlapping

+ 15 - 182
lib/rmg/ObstaclePlacer.cpp

@@ -9,7 +9,6 @@
  */
 
 #include "StdInc.h"
-#include "../mapObjects/CObjectClassesHandler.h"
 #include "ObstaclePlacer.h"
 #include "ObjectManager.h"
 #include "TreasurePlacer.h"
@@ -24,186 +23,16 @@
 #include "Functions.h"
 #include "../mapping/CMapEditManager.h"
 #include "../mapping/CMap.h"
+#include "../mapping/ObstacleProxy.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-void ObstacleProxy::collectPossibleObstacles(TerrainId terrain)
-{
-	//get all possible obstacles for this terrain
-	for(auto primaryID : VLC->objtypeh->knownObjects())
-	{
-		for(auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID))
-		{
-			auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID);
-			if(handler->isStaticObject())
-			{
-				for(const auto & temp : handler->getTemplates())
-				{
-					if(temp->canBePlacedAt(terrain) && temp->getBlockMapOffset().valid())
-						obstaclesBySize[temp->getBlockedOffsets().size()].push_back(temp);
-				}
-			}
-		}
-	}
-	for(const auto & o : obstaclesBySize)
-	{
-		possibleObstacles.emplace_back(o);
-	}
-	boost::sort(possibleObstacles, [](const ObstaclePair &p1, const ObstaclePair &p2) -> bool
-	{
-		return p1.first > p2.first; //bigger obstacles first
-	});
-}
-
-int ObstacleProxy::getWeightedObjects(const int3 & tile, const CMap * map, CRandomGenerator & rand, std::list<rmg::Object> & allObjects, std::vector<std::pair<rmg::Object*, int3>> & weightedObjects)
-{
-	int maxWeight = std::numeric_limits<int>::min();
-	for(auto & possibleObstacle : possibleObstacles)
-	{
-		if(!possibleObstacle.first)
-			continue;
-
-		auto shuffledObstacles = possibleObstacle.second;
-		RandomGeneratorUtil::randomShuffle(shuffledObstacles, rand);
-
-		for(const auto & temp : shuffledObstacles)
-		{
-			auto handler = VLC->objtypeh->getHandlerFor(temp->id, temp->subid);
-			auto * obj = handler->create(temp);
-			allObjects.emplace_back(*obj);
-			rmg::Object * rmgObject = &allObjects.back();
-			for(const auto & offset : obj->getBlockedOffsets())
-			{
-				rmgObject->setPosition(tile - offset);
-				if(!map->isInTheMap(rmgObject->getPosition()))
-					continue;
-
-				if(!rmgObject->getArea().getSubarea([map](const int3 & t)
-				{
-					return !map->isInTheMap(t);
-				}).empty())
-					continue;
-
-				if(isProhibited(rmgObject->getArea()))
-					continue;
-
-				int coverageBlocked = 0;
-				int coveragePossible = 0;
-				//do not use area intersection in optimization purposes
-				for(const auto & t : rmgObject->getArea().getTilesVector())
-				{
-					auto coverage = verifyCoverage(t);
-					if(coverage.first)
-						++coverageBlocked;
-					if(coverage.second)
-						++coveragePossible;
-				}
-
-				int coverageOverlap = possibleObstacle.first - coverageBlocked - coveragePossible;
-				int weight = possibleObstacle.first + coverageBlocked - coverageOverlap * possibleObstacle.first;
-				assert(coverageOverlap >= 0);
-
-				if(weight > maxWeight)
-				{
-					weightedObjects.clear();
-					maxWeight = weight;
-					weightedObjects.emplace_back(rmgObject, rmgObject->getPosition());
-					if(weight > 0)
-						break;
-				}
-				else if(weight == maxWeight)
-					weightedObjects.emplace_back(rmgObject, rmgObject->getPosition());
-
-			}
-		}
-
-		if(maxWeight > 0)
-			break;
-	}
-
-	return maxWeight;
-}
-
-void ObstacleProxy::placeObstacles(CMap * map, CRandomGenerator & rand)
-{
-	//reverse order, since obstacles begin in bottom-right corner, while the map coordinates begin in top-left
-	auto blockedTiles = blockedArea.getTilesVector();
-	int tilePos = 0;
-	std::set<CGObjectInstance*> objs;
-
-	while(!blockedArea.empty() && tilePos < blockedArea.getTilesVector().size())
-	{
-		auto tile = blockedArea.getTilesVector()[tilePos];
-
-		std::list<rmg::Object> allObjects;
-		std::vector<std::pair<rmg::Object*, int3>> weightedObjects;
-		int maxWeight = getWeightedObjects(tile, map, rand, allObjects, weightedObjects);
-
-		if(weightedObjects.empty())
-		{
-			tilePos += 1;
-			continue;
-		}
-
-		auto objIter = RandomGeneratorUtil::nextItem(weightedObjects, rand);
-		objIter->first->setPosition(objIter->second);
-		placeObject(*objIter->first, objs);
-
-		blockedArea.subtract(objIter->first->getArea());
-		tilePos = 0;
-
-		postProcess(*objIter->first);
-
-		if(maxWeight < 0)
-			logGlobal->warn("Placed obstacle with negative weight at %s", objIter->second.toString());
-
-		for(auto & o : allObjects)
-		{
-			if(&o != objIter->first)
-				o.clear();
-		}
-	}
-
-	finalInsertion(map->getEditManager(), objs);
-}
-
-void ObstacleProxy::finalInsertion(CMapEditManager * manager, std::set<CGObjectInstance*> & instances)
-{
-	manager->insertObjects(instances); //insert as one operation - for undo purposes
-}
-
-std::pair<bool, bool> ObstacleProxy::verifyCoverage(const int3 & t) const
-{
-	return {blockedArea.contains(t), false};
-}
-
-void ObstacleProxy::placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances)
-{
-	for (auto * instance : object.instances())
-	{
-		instances.insert(&instance->object());
-	}
-}
-
-void ObstacleProxy::postProcess(const rmg::Object & object)
-{
-}
-
-bool ObstacleProxy::isProhibited(const rmg::Area & objArea) const
-{
-	return false;
-}
-
-
-
 void ObstaclePlacer::process()
 {
 	manager = zone.getModificator<ObjectManager>();
 	if(!manager)
 		return;
-	
-	riverManager = zone.getModificator<RiverPlacer>();
-	
+
 	collectPossibleObstacles(zone.getTerrainType());
 	
 	blockedArea = zone.area().getSubarea([this](const int3 & t)
@@ -212,10 +41,12 @@ void ObstaclePlacer::process()
 	});
 	blockedArea.subtract(zone.areaUsed());
 	zone.areaPossible().subtract(blockedArea);
-	
+
+	//TODO: Set prohibited area in ObstacleProxy :?
 	prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea();
-		
-	placeObstacles(&map.map(), generator.rand);
+
+	auto objs = createObstacles(generator.rand);
+	mapProxy->insertObjects(objs);
 }
 
 void ObstaclePlacer::init()
@@ -228,9 +59,9 @@ void ObstaclePlacer::init()
 	DEPENDENCY_ALL(RockPlacer);
 }
 
-std::pair<bool, bool> ObstaclePlacer::verifyCoverage(const int3 & t) const
+bool ObstaclePlacer::isInTheMap(const int3& tile)
 {
-	return {map.shouldBeBlocked(t), zone.areaPossible().contains(t)};
+	return map.isOnMap(tile);
 }
 
 void ObstaclePlacer::placeObject(rmg::Object & object, std::set<CGObjectInstance*> &)
@@ -238,9 +69,15 @@ void ObstaclePlacer::placeObject(rmg::Object & object, std::set<CGObjectInstance
 	manager->placeObject(object, false, false);
 }
 
+std::pair<bool, bool> ObstaclePlacer::verifyCoverage(const int3 & t) const
+{
+	return {map.shouldBeBlocked(t), zone.areaPossible().contains(t)};
+}
+
 void ObstaclePlacer::postProcess(const rmg::Object & object)
 {
 	//river processing
+	riverManager = zone.getModificator<RiverPlacer>();
 	if(riverManager)
 	{
 		const auto objTypeName = object.instances().front()->object().typeName;
@@ -262,8 +99,4 @@ bool ObstaclePlacer::isProhibited(const rmg::Area & objArea) const
 	return false;
 }
 
-void ObstaclePlacer::finalInsertion(CMapEditManager *, std::set<CGObjectInstance*> &)
-{
-}
-
 VCMI_LIB_NAMESPACE_END

+ 5 - 35
lib/rmg/ObstaclePlacer.h

@@ -9,7 +9,9 @@
  */
 
 #pragma once
-#include "Zone.h"
+
+#include "Modificator.h"
+#include "../mapping/ObstacleProxy.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -17,38 +19,6 @@ class CMap;
 class CMapEditManager;
 class RiverPlacer;
 class ObjectManager;
-
-class DLL_LINKAGE ObstacleProxy
-{
-public:
-	ObstacleProxy() = default;
-	virtual ~ObstacleProxy() = default;
-
-	rmg::Area blockedArea;
-
-	void collectPossibleObstacles(TerrainId terrain);
-
-	void placeObstacles(CMap * map, CRandomGenerator & rand);
-
-	virtual std::pair<bool, bool> verifyCoverage(const int3 & t) const;
-
-	virtual void placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances);
-
-	virtual void postProcess(const rmg::Object & object);
-
-	virtual bool isProhibited(const rmg::Area & objArea) const;
-	
-	virtual void finalInsertion(CMapEditManager * manager, std::set<CGObjectInstance*> & instances);
-
-protected:
-	int getWeightedObjects(const int3 & tile, const CMap * map, CRandomGenerator & rand, std::list<rmg::Object> & allObjects, std::vector<std::pair<rmg::Object*, int3>> & weightedObjects);
-
-	using ObstacleVector = std::vector<std::shared_ptr<const ObjectTemplate>>;
-	std::map<int, ObstacleVector> obstaclesBySize;
-	using ObstaclePair = std::pair<int, ObstacleVector>;
-	std::vector<ObstaclePair> possibleObstacles;
-};
-
 class ObstaclePlacer: public Modificator, public ObstacleProxy
 {
 public:
@@ -56,6 +26,8 @@ public:
 	
 	void process() override;
 	void init() override;
+
+	bool isInTheMap(const int3& tile) override;
 	
 	std::pair<bool, bool> verifyCoverage(const int3 & t) const override;
 	
@@ -65,8 +37,6 @@ public:
 	
 	bool isProhibited(const rmg::Area & objArea) const override;
 	
-	void finalInsertion(CMapEditManager * manager, std::set<CGObjectInstance*> & instances) override;
-	
 private:
 	rmg::Area prohibitedArea;
 	RiverPlacer * riverManager;

+ 8 - 8
lib/rmg/QuestArtifactPlacer.cpp

@@ -33,26 +33,26 @@ void QuestArtifactPlacer::init()
 
 void QuestArtifactPlacer::addQuestArtZone(std::shared_ptr<Zone> otherZone)
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	questArtZones.push_back(otherZone);
 }
 
 void QuestArtifactPlacer::addQuestArtifact(const ArtifactID& id)
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	logGlobal->info("Need to place quest artifact artifact %s", VLC->artifacts()->getById(id)->getNameTranslated());
 	questArtifactsToPlace.emplace_back(id);
 }
 
 void QuestArtifactPlacer::rememberPotentialArtifactToReplace(CGObjectInstance* obj)
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	artifactsToReplace.push_back(obj);
 }
 
 std::vector<CGObjectInstance*> QuestArtifactPlacer::getPossibleArtifactsToReplace() const
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	return artifactsToReplace;
 }
 
@@ -113,19 +113,19 @@ void QuestArtifactPlacer::placeQuestArtifacts(CRandomGenerator * rand)
 
 void QuestArtifactPlacer::dropReplacedArtifact(CGObjectInstance* obj)
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	boost::remove(artifactsToReplace, obj);
 }
 
 size_t QuestArtifactPlacer::getMaxQuestArtifactCount() const
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	return questArtifacts.size();
 }
 
 ArtifactID QuestArtifactPlacer::drawRandomArtifact()
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	if (!questArtifacts.empty())
 	{
 		ArtifactID ret = questArtifacts.back();
@@ -141,6 +141,6 @@ ArtifactID QuestArtifactPlacer::drawRandomArtifact()
 
 void QuestArtifactPlacer::addRandomArtifact(ArtifactID artid)
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	questArtifacts.push_back(artid);
 }

+ 4 - 4
lib/rmg/RiverPlacer.cpp

@@ -87,8 +87,8 @@ void RiverPlacer::init()
 
 void RiverPlacer::drawRivers()
 {
-	map.getEditManager()->getTerrainSelection().setSelection(rivers.getTilesVector());
-	map.getEditManager()->drawRiver(VLC->terrainTypeHandler->getById(zone.getTerrainType())->river, &generator.rand);
+	auto tiles = rivers.getTilesVector();
+	mapProxy->drawRivers(generator.rand, tiles, zone.getTerrainType());
 }
 
 char RiverPlacer::dump(const int3 & t)
@@ -147,9 +147,9 @@ void RiverPlacer::prepareHeightmap()
 	}
 	
 	//make grid
-	for(int j = 0; j < map.map().height; j += 2)
+	for(int j = 0; j < map.height(); j += 2)
 	{
-		for(int i = 0; i < map.map().width; i += 2)
+		for(int i = 0; i < map.width(); i += 2)
 		{
 			int3 t{i, j, zone.getPos().z};
 			if(zone.area().contains(t))

+ 38 - 4
lib/rmg/RmgMap.cpp

@@ -28,6 +28,7 @@
 #include "WaterProxy.h"
 #include "WaterRoutes.h"
 #include "RockPlacer.h"
+#include "RockFiller.h"
 #include "ObstaclePlacer.h"
 #include "RiverPlacer.h"
 #include "TerrainPainter.h"
@@ -40,6 +41,7 @@ RmgMap::RmgMap(const CMapGenOptions& mapGenOptions) :
 	mapGenOptions(mapGenOptions), zonesTotal(0)
 {
 	mapInstance = std::make_unique<CMap>();
+	mapProxy = std::make_shared<MapProxy>(*this);
 	getEditManager()->getUndoManager().setUndoRedoLimit(0);
 }
 
@@ -121,7 +123,7 @@ void RmgMap::addModificators()
 		auto zone = z.second;
 		
 		zone->addModificator<ObjectManager>();
-		zone->addModificator<ObjectDistributor>();
+		zone->addModificator<ObjectDistributor>(); //FIXME: Only one is needed for map
 		zone->addModificator<TreasurePlacer>();
 		zone->addModificator<ObstaclePlacer>();
 		zone->addModificator<TerrainPainter>();
@@ -149,12 +151,38 @@ void RmgMap::addModificators()
 		if(zone->isUnderground())
 		{
 			zone->addModificator<RockPlacer>();
+			zone->addModificator<RockFiller>(); //FIXME: Only one is needed for map
 		}
 		
 	}
 }
 
-CMap & RmgMap::map() const
+int RmgMap::levels() const
+{
+	return mapInstance->levels();
+}
+
+int RmgMap::width() const
+{
+	return mapInstance->width;
+}
+
+int RmgMap::height() const
+{
+	return mapInstance->height;
+}
+
+PlayerInfo & RmgMap::getPlayer(int playerId)
+{
+	return mapInstance->players.at(playerId);
+}
+
+std::shared_ptr<MapProxy> RmgMap::getMapProxy() const
+{
+	return mapProxy;
+}
+
+CMap& RmgMap::getMap(const CMapGenerator*) const
 {
 	return *mapInstance;
 }
@@ -241,13 +269,18 @@ void RmgMap::setRoad(const int3& tile, RoadId roadType)
 	tiles[tile.x][tile.y][tile.z].setRoadType(roadType);
 }
 
-TileInfo RmgMap::getTile(const int3& tile) const
+TileInfo RmgMap::getTileInfo(const int3& tile) const
 {
 	assertOnMap(tile);
 	
 	return tiles[tile.x][tile.y][tile.z];
 }
 
+TerrainTile & RmgMap::getTile(const int3& tile) const
+{
+	return mapInstance->getTile(tile);
+}
+
 TRmgTemplateZoneId RmgMap::getZoneID(const int3& tile) const
 {
 	assertOnMap(tile);
@@ -278,6 +311,7 @@ float RmgMap::getNearestObjectDistance(const int3 &tile) const
 
 void RmgMap::registerZone(FactionID faction)
 {
+	//FIXME: Protect with lock guard?
 	zonesPerFaction[faction]++;
 	zonesTotal++;
 }
@@ -323,7 +357,7 @@ void RmgMap::dump(bool zoneId) const
 				else
 				{
 					char t = '?';
-					switch (getTile(int3(i, j, k)).getTileType())
+					switch (getTileInfo(int3(i, j, k)).getTileType())
 					{
 						case ETileType::FREE:
 							t = ' '; break;

+ 15 - 2
lib/rmg/RmgMap.h

@@ -11,6 +11,7 @@
 #pragma once
 #include "../int3.h"
 #include "../GameConstants.h"
+#include "threadpool/MapProxy.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -20,12 +21,15 @@ class TileInfo;
 class CMapGenOptions;
 class Zone;
 class CMapGenerator;
+class MapProxy;
+class playerInfo;
 
 class RmgMap
 {
 public:
 	mutable std::unique_ptr<CMap> mapInstance;
-	CMap & map() const;
+	std::shared_ptr<MapProxy> getMapProxy() const;
+	CMap & getMap(const CMapGenerator *) const; //limited access
 	
 	RmgMap(const CMapGenOptions& mapGenOptions);
 	~RmgMap() = default;
@@ -44,11 +48,17 @@ public:
 	bool isUsed(const int3 &tile) const;
 	bool isRoad(const int3 &tile) const;
 	bool isOnMap(const int3 & tile) const;
+
+	int levels() const;
+	int width() const;
+	int height() const;
+	PlayerInfo & getPlayer(int playerId);
 	
 	void setOccupied(const int3 &tile, ETileType::ETileType state);
 	void setRoad(const int3 &tile, RoadId roadType);
 	
-	TileInfo getTile(const int3 & tile) const;
+	TileInfo getTileInfo(const int3 & tile) const;
+	TerrainTile & getTile(const int3 & tile) const;
 		
 	float getNearestObjectDistance(const int3 &tile) const;
 	void setNearestObjectDistance(int3 &tile, float value);
@@ -76,6 +86,9 @@ private:
 	void assertOnMap(const int3 &tile) const; //throws
 
 private:
+
+	std::shared_ptr<MapProxy> mapProxy;
+
 	Zones zones;
 	std::map<FactionID, ui32> zonesPerFaction;
 	ui32 zonesTotal; //zones that have their main town only

+ 3 - 3
lib/rmg/RmgObject.cpp

@@ -182,7 +182,7 @@ std::list<Object::Instance*> Object::instances()
 {
 	std::list<Object::Instance*> result;
 	for(auto & i : dInstances)
-		result.push_back(&i);
+		result.push_back(&i); //FIXME: Sterta zosta³a uskzodzona :? Mo¿e w innym miejscu?
 	return result;
 }
 
@@ -310,7 +310,7 @@ void Object::Instance::finalize(RmgMap & map)
 	//If no specific template was defined for this object, select any matching
 	if (!dObject.appearance)
 	{
-		const auto * terrainType = map.map().getTile(getPosition(true)).terType;
+		const auto * terrainType = map.getTile(getPosition(true)).terType;
 		auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrainType->getId());
 		if (templates.empty())
 		{
@@ -336,7 +336,7 @@ void Object::Instance::finalize(RmgMap & map)
 		map.setOccupied(tile, ETileType::ETileType::USED);
 	}
 	
-	map.getEditManager()->insertObject(&dObject);
+	map.getMapProxy()->insertObject(&dObject);
 }
 
 void Object::finalize(RmgMap & map)

+ 15 - 8
lib/rmg/RoadPlacer.cpp

@@ -11,10 +11,11 @@
 #include "StdInc.h"
 #include "RoadPlacer.h"
 #include "ObjectManager.h"
+#include "ObstaclePlacer.h"
 #include "Functions.h"
 #include "CMapGenerator.h"
 #include "RmgMap.h"
-#include "../mapping/CMap.h"
+#include "threadpool/MapProxy.h"
 #include "../mapping/CMapEditManager.h"
 #include "../CModHandler.h"
 #include "RmgPath.h"
@@ -76,19 +77,25 @@ void RoadPlacer::drawRoads(bool secondary)
 	   || (!secondary && generator.getConfig().defaultRoadType.empty()))
 		return;
 	
-	Lock lock(externalAccessMutex);
-	zone.areaPossible().subtract(roads);
-	zone.freePaths().unite(roads);
-	map.getEditManager()->getTerrainSelection().setSelection(roads.getTilesVector());
+	//RecursiveLock lock(externalAccessMutex);
+	{
+		//FIXME: double lock - unsafe
+		Zone::Lock lock(zone.areaMutex);
+
+		zone.areaPossible().subtract(roads);
+		zone.freePaths().unite(roads);
+	}
+
+	auto tiles = roads.getTilesVector();
 
 	std::string roadName = (secondary ? generator.getConfig().secondaryRoadType : generator.getConfig().defaultRoadType);
 	RoadId roadType(*VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "road", roadName));
-	map.getEditManager()->drawRoad(roadType, &generator.rand);
+	mapProxy->drawRoads(generator.rand, tiles, roadType);
 }
 
 void RoadPlacer::addRoadNode(const int3& node)
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	roadNodes.insert(node);
 }
 
@@ -113,7 +120,7 @@ void RoadPlacer::connectRoads()
 		return;
 	
 	//take any tile from road nodes as destination zone for all other road nodes
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	if(roads.empty())
 		roads.add(*roadNodes.begin());
 

+ 91 - 0
lib/rmg/RockFiller.cpp

@@ -0,0 +1,91 @@
+/*
+ * RockFiller.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#include "StdInc.h"
+#include "RockFiller.h"
+#include "RockPlacer.h"
+#include "TreasurePlacer.h"
+#include "ObjectManager.h"
+#include "RoadPlacer.h"
+#include "RiverPlacer.h"
+#include "RmgMap.h"
+#include "CMapGenerator.h"
+#include "Functions.h"
+#include "../TerrainHandler.h"
+#include "../CRandomGenerator.h"
+#include "../mapping/CMapEditManager.h"
+#include "TileInfo.h"
+#include "threadpool/MapProxy.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class TileInfo;
+
+void RockFiller::process()
+{
+    //Do that only once
+	auto lockVec = tryLockAll<RockFiller>();
+	if (!lockVec.empty())
+	{
+		for(auto & z : map.getZones())
+		{
+			if(auto * m = z.second->getModificator<RockFiller>())
+			{
+				if(m->isFinished())
+					return;
+			}
+		}
+		logGlobal->info("Processing RockFiller for the whole map");
+		processMap();
+		finished = true; //Block other placers immediately
+	}
+}
+
+void RockFiller::processMap()
+{
+	//Merge all areas
+	for(auto & z : map.getZones())
+	{
+		if(auto * m = z.second->getModificator<RockPlacer>())
+		{
+			auto tiles = m->rockArea.getTilesVector();
+			mapProxy->drawTerrain(generator.rand, tiles, m->rockTerrain);
+		}
+	}
+	
+	for(auto & z : map.getZones())
+	{
+		if(auto * m = z.second->getModificator<RockPlacer>())
+		{
+			//Now make sure all accessible tiles have no additional rock on them
+			auto tiles = m->accessibleArea.getTilesVector();
+			mapProxy->drawTerrain(generator.rand, tiles, z.second->getTerrainType());
+
+			m->postProcess();
+		}
+	}
+}
+
+void RockFiller::init()
+{
+    DEPENDENCY_ALL(RockPlacer);
+	POSTFUNCTION_ALL(RoadPlacer);
+}
+
+char RockFiller::dump(const int3 & t)
+{
+	if(!map.getTile(t).terType->isPassable())
+	{
+		return zone.area().contains(t) ? 'R' : 'E';
+	}
+	return Modificator::dump(t);
+}
+
+VCMI_LIB_NAMESPACE_END

+ 28 - 0
lib/rmg/RockFiller.h

@@ -0,0 +1,28 @@
+/*
+ * RockFiller.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+#include "Zone.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class RockFiller: public Modificator
+{
+public:
+	MODIFICATOR(RockFiller);
+	
+	void process() override;
+	void init() override;
+	char dump(const int3 &) override;
+	
+	void processMap();
+};
+
+VCMI_LIB_NAMESPACE_END

+ 16 - 43
lib/rmg/RockPlacer.cpp

@@ -20,71 +20,45 @@
 #include "../TerrainHandler.h"
 #include "../CRandomGenerator.h"
 #include "../mapping/CMapEditManager.h"
-#include "../mapping/CMap.h"
+#include "TileInfo.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+class TileInfo;
+
 void RockPlacer::process()
+{
+	blockRock();
+}
+void RockPlacer::blockRock()
 {
 	rockTerrain = VLC->terrainTypeHandler->getById(zone.getTerrainType())->rockTerrain;
 	assert(!VLC->terrainTypeHandler->getById(rockTerrain)->isPassable());
-	
+
 	accessibleArea = zone.freePaths() + zone.areaUsed();
 	if(auto * m = zone.getModificator<ObjectManager>())
 		accessibleArea.unite(m->getVisitableArea());
-	
+
 	//negative approach - create rock tiles first, then make sure all accessible tiles have no rock
 	rockArea = zone.area().getSubarea([this](const int3 & t)
 	{
 		return map.shouldBeBlocked(t);
 	});
-	
-	for(auto & z : map.getZones())
-	{
-		if(auto * m = z.second->getModificator<RockPlacer>())
-		{
-			if(m != this && !m->isFinished())
-				return;
-		}
-	}
-	
-	processMap();
-}
-
-void RockPlacer::processMap()
-{
-	//merge all areas
-	for(auto & z : map.getZones())
-	{
-		if(auto * m = z.second->getModificator<RockPlacer>())
-		{
-			map.getEditManager()->getTerrainSelection().setSelection(m->rockArea.getTilesVector());
-			map.getEditManager()->drawTerrain(m->rockTerrain, &generator.rand);
-		}
-	}
-	
-	for(auto & z : map.getZones())
-	{
-		if(auto * m = z.second->getModificator<RockPlacer>())
-		{
-			//now make sure all accessible tiles have no additional rock on them
-			map.getEditManager()->getTerrainSelection().setSelection(m->accessibleArea.getTilesVector());
-			map.getEditManager()->drawTerrain(z.second->getTerrainType(), &generator.rand);
-			m->postProcess();
-		}
-	}
 }
 
 void RockPlacer::postProcess()
 {
-	//finally mark rock tiles as occupied, spawn no obstacles there
+	Zone::Lock lock(zone.areaMutex);
+	//Finally mark rock tiles as occupied, spawn no obstacles there
 	rockArea = zone.area().getSubarea([this](const int3 & t)
 	{
-		return !map.map().getTile(t).terType->isPassable();
+		return !map.getTile(t).terType->isPassable();
 	});
 	
 	zone.areaUsed().unite(rockArea);
 	zone.areaPossible().subtract(rockArea);
+
+	//TODO: Might need mutex here as well
 	if(auto * m = zone.getModificator<RiverPlacer>())
 		m->riverProhibit().unite(rockArea);
 	if(auto * m = zone.getModificator<RoadPlacer>())
@@ -93,13 +67,12 @@ void RockPlacer::postProcess()
 
 void RockPlacer::init()
 {
-	POSTFUNCTION_ALL(RoadPlacer);
-	DEPENDENCY(TreasurePlacer);
+	DEPENDENCY_ALL(TreasurePlacer);
 }
 
 char RockPlacer::dump(const int3 & t)
 {
-	if(!map.map().getTile(t).terType->isPassable())
+	if(!map.getTile(t).terType->isPassable())
 	{
 		return zone.area().contains(t) ? 'R' : 'E';
 	}

+ 2 - 1
lib/rmg/RockPlacer.h

@@ -15,6 +15,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 class RockPlacer: public Modificator
 {
+	friend class RockFiller;
 public:
 	MODIFICATOR(RockPlacer);
 	
@@ -22,7 +23,7 @@ public:
 	void init() override;
 	char dump(const int3 &) override;
 	
-	void processMap();
+	void blockRock();
 	void postProcess();
 	
 protected:

+ 3 - 1
lib/rmg/TerrainPainter.cpp

@@ -23,8 +23,10 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 void TerrainPainter::process()
 {
+	//TODO: Make member methods
+
 	initTerrainType(zone, generator);
-	paintZoneTerrain(zone, generator.rand, map, zone.getTerrainType());
+	paintZoneTerrain(zone, generator.rand, mapProxy, zone.getTerrainType());
 }
 
 void TerrainPainter::init()

+ 11 - 5
lib/rmg/TownPlacer.cpp

@@ -52,7 +52,7 @@ void TownPlacer::placeTowns(ObjectManager & manager)
 		//set zone types to player faction, generate main town
 		logGlobal->info("Preparing playing zone");
 		int player_id = *zone.getOwner() - 1;
-		auto & playerInfo = map.map().players[player_id];
+		auto& playerInfo = map.getPlayer(player_id);
 		PlayerColor player(player_id);
 		if(playerInfo.canAnyonePlay())
 		{
@@ -140,11 +140,16 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
 	//towns are big objects and should be centered around visitable position
 	rmg::Object rmgObject(town);
 	rmgObject.setTemplate(zone.getTerrainType());
-	auto position = manager.findPlaceForObject(zone.areaPossible(), rmgObject, [this](const int3 & t)
+
+	int3 position(-1, -1, -1);
 	{
-		float distance = zone.getPos().dist2dSQ(t);
-		return 100000.f - distance; //some big number
-	}, ObjectManager::OptimizeType::WEIGHT);
+		Zone::Lock lock(zone.areaMutex);
+		position = manager.findPlaceForObject(zone.areaPossible(), rmgObject, [this](const int3& t)
+			{
+				float distance = zone.getPos().dist2dSQ(t);
+				return 100000.f - distance; //some big number
+			}, ObjectManager::OptimizeType::WEIGHT);
+	}
 	rmgObject.setPosition(position + int3(2, 2, 0)); //place visitable tile in the exact center of a zone
 	manager.placeObject(rmgObject, false, true);
 	cleanupBoundaries(rmgObject);
@@ -154,6 +159,7 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
 
 void TownPlacer::cleanupBoundaries(const rmg::Object & rmgObject)
 {
+	Zone::Lock lock(zone.areaMutex);
 	for(const auto & t : rmgObject.getArea().getBorderOutside())
 	{
 		if(map.isOnMap(t))

+ 8 - 6
lib/rmg/TreasurePlacer.cpp

@@ -45,7 +45,7 @@ void TreasurePlacer::init()
 
 void TreasurePlacer::addObjectToRandomPool(const ObjectInfo& oi)
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	possibleObjects.push_back(oi);
 }
 
@@ -526,19 +526,19 @@ void TreasurePlacer::addAllPossibleObjects()
 
 size_t TreasurePlacer::getPossibleObjectsSize() const
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	return possibleObjects.size();
 }
 
 void TreasurePlacer::setMaxPrisons(size_t count)
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	maxPrisons = count;
 }
 
 size_t TreasurePlacer::getMaxPrisons() const
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	return maxPrisons;
 }
 
@@ -772,6 +772,8 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
 			if(guarded)
 				guarded = manager.addGuard(rmgObject, value);
 			
+			Zone::Lock lock(zone.areaMutex); //We are going to subtract this area
+			//TODO: Don't place 
 			auto possibleArea = zone.areaPossible();
 			
 			auto path = rmg::Path::invalid();
@@ -779,13 +781,13 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
 			{
 				path = manager.placeAndConnectObject(possibleArea, rmgObject, [this, &rmgObject, &minDistance, &manager](const int3 & tile)
 				{
-					auto ti = map.getTile(tile);
+					auto ti = map.getTileInfo(tile);
 					if(ti.getNearestObjectDistance() < minDistance)
 						return -1.f;
 
 					for(const auto & t : rmgObject.getArea().getTilesVector())
 					{
-						if(map.getTile(t).getNearestObjectDistance() < minDistance)
+						if(map.getTileInfo(t).getNearestObjectDistance() < minDistance)
 							return -1.f;
 					}
 					

+ 1 - 0
lib/rmg/WaterAdopter.cpp

@@ -225,6 +225,7 @@ void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent)
 	}
 	
 	map.getZones()[waterZoneId]->area().unite(waterArea);
+	Zone::Lock lock(zone.areaMutex);
 	zone.area().subtract(waterArea);
 	zone.areaPossible().subtract(waterArea);
 	distanceMap = zone.area().computeDistanceMap(reverseDistanceMap);

+ 8 - 5
lib/rmg/WaterProxy.cpp

@@ -38,7 +38,7 @@ void WaterProxy::process()
 		map.setOccupied(t, ETileType::POSSIBLE);
 	}
 	
-	paintZoneTerrain(zone, generator.rand, map, zone.getTerrainType());
+	paintZoneTerrain(zone, generator.rand, mapProxy, zone.getTerrainType());
 	
 	//check terrain type
 	for([[maybe_unused]] const auto & t : zone.area().getTilesVector())
@@ -52,9 +52,10 @@ void WaterProxy::process()
 		if(z.second->getId() == zone.getId())
 			continue;
 
+		Zone::Lock lock(z.second->areaMutex);
 		for(const auto & t : z.second->area().getTilesVector())
 		{
-			if(map.map().getTile(t).terType->getId() == zone.getTerrainType())
+			if(map.getTile(t).terType->getId() == zone.getTerrainType())
 			{
 				z.second->areaPossible().erase(t);
 				z.second->area().erase(t);
@@ -90,13 +91,13 @@ void WaterProxy::init()
 
 const std::vector<WaterProxy::Lake> & WaterProxy::getLakes() const
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	return lakes;
 }
 
 void WaterProxy::collectLakes()
 {
-	Lock lock(externalAccessMutex);
+	RecursiveLock lock(externalAccessMutex);
 	int lakeId = 0;
 	for(const auto & lake : connectedAreas(zone.getArea(), true))
 	{
@@ -140,6 +141,8 @@ RouteInfo WaterProxy::waterRoute(Zone & dst)
 					if(map.isPossible(ct))
 						map.setOccupied(ct, ETileType::BLOCKED);
 				}
+
+				Zone::Lock lock(dst.areaMutex);
 				dst.areaPossible().subtract(lake.neighbourZones[dst.getId()]);
 				continue;
 			}
@@ -223,7 +226,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, RouteInfo & info)
 	auto boardingPositions = coast.getSubarea([&waterAvailable, this](const int3 & tile) //tiles where boarding is possible
 		{
 			//We don't want place boat right to any land object, especiallly the zone guard
-			if (map.getTile(tile).getNearestObjectDistance() <= 3)
+			if (map.getTileInfo(tile).getNearestObjectDistance() <= 3)
 				return false;
 
 			rmg::Area a({tile});

+ 3 - 1
lib/rmg/WaterRoutes.cpp

@@ -44,7 +44,9 @@ void WaterRoutes::process()
 		if(z.first != zone.getId())
 			result.push_back(wproxy->waterRoute(*z.second));
 	}
-	
+
+	Zone::Lock lock(zone.areaMutex);
+
 	//prohibit to place objects on sealed off lakes
 	for(const auto & lake : wproxy->getLakes())
 	{

+ 7 - 2
lib/rmg/Zone.cpp

@@ -86,6 +86,7 @@ rmg::Area & Zone::area()
 
 rmg::Area & Zone::areaPossible()
 {
+	//FIXME: make const, only modify via mutex-protected interface
 	return dAreaPossible;
 }
 
@@ -96,6 +97,7 @@ rmg::Area & Zone::areaUsed()
 
 void Zone::clearTiles()
 {
+	//Lock lock(mx);
 	dArea.clear();
 	dAreaPossible.clear();
 	dAreaFree.clear();
@@ -104,6 +106,7 @@ void Zone::clearTiles()
 void Zone::initFreeTiles()
 {
 	rmg::Tileset possibleTiles;
+	//Lock lock(mx);
 	vstd::copy_if(dArea.getTiles(), vstd::set_inserter(possibleTiles), [this](const int3 &tile) -> bool
 	{
 		return map.isPossible(tile);
@@ -201,6 +204,7 @@ void Zone::fractalize()
 	rmg::Area tilesToIgnore; //will be erased in this iteration
 
 	const float minDistance = 10 * 10; //squared
+	float blockDistance = minDistance * 0.25f;
 	
 	if(type != ETemplateZoneType::JUNCTION)
 	{
@@ -235,7 +239,7 @@ void Zone::fractalize()
 			tilesToIgnore.clear();
 		}
 	}
-	
+	Lock lock(areaMutex);
 	//cut straight paths towards the center. A* is too slow for that.
 	auto areas = connectedAreas(clearedTiles, false);
 	for(auto & area : areas)
@@ -264,7 +268,6 @@ void Zone::fractalize()
 	}
 	
 	//now block most distant tiles away from passages
-	float blockDistance = minDistance * 0.25f;
 	auto areaToBlock = dArea.getSubarea([this, blockDistance](const int3 & t)
 	{
 		auto distance = static_cast<float>(dAreaFree.distanceSqr(t));
@@ -272,6 +275,8 @@ void Zone::fractalize()
 	});
 	dAreaPossible.subtract(areaToBlock);
 	dAreaFree.subtract(areaToBlock);
+
+	lock.unlock();
 	for(const auto & t : areaToBlock.getTiles())
 		map.setOccupied(t, ETileType::BLOCKED);
 }

+ 10 - 2
lib/rmg/Zone.h

@@ -13,7 +13,6 @@
 #include "../GameConstants.h"
 #include "float3.h"
 #include "../int3.h"
-#include "threadpool/JobProvider.h"
 #include "CRmgTemplate.h"
 #include "RmgArea.h"
 #include "RmgPath.h"
@@ -87,6 +86,10 @@ public:
 	}
 	
 	void initModificators();
+
+public:
+	boost::recursive_mutex areaMutex;
+	using Lock = boost::unique_lock<boost::recursive_mutex>;
 	
 protected:
 	CMapGenerator & generator;
@@ -106,7 +109,12 @@ protected:
 	//template info
 	si32 townType;
 	TerrainId terrainType;
-	
+
+/*
+private:
+	mutable boost::shared_mutex mx; //Used for area access
+	using Lock = boost::unique_lock<boost::shared_mutex>;
+*/
 };
 
 VCMI_LIB_NAMESPACE_END

+ 0 - 30
lib/rmg/threadpool/JobProvider.h

@@ -1,30 +0,0 @@
-/*
- * JobProvider.h, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
-
-#pragma once
-
-#include "StdInc.h"
-#include "../../GameConstants.h"
-
-VCMI_LIB_NAMESPACE_BEGIN
-
-typedef std::function<void()> TRMGfunction ;
-typedef std::optional<TRMGfunction> TRMGJob;
-
-class DLL_LINKAGE IJobProvider
-{
-public:
-	//TODO: Think about some mutex protection
-
-	virtual TRMGJob getNextJob() = 0;
-	virtual bool hasJobs() = 0;
-};
-
-VCMI_LIB_NAMESPACE_END

+ 57 - 0
lib/rmg/threadpool/MapProxy.cpp

@@ -0,0 +1,57 @@
+/*
+ * MapProxy.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+
+#pragma once
+
+#include "MapProxy.h"
+#include "../../TerrainHandler.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+MapProxy::MapProxy(RmgMap & map):
+    map(map)
+{
+}
+
+void MapProxy::insertObject(CGObjectInstance * obj)
+{
+    Lock lock(mx);
+    map.getEditManager()->insertObject(obj);
+}
+
+void MapProxy::insertObjects(std::set<CGObjectInstance*>& objects)
+{
+    Lock lock(mx);
+    map.getEditManager()->insertObjects(objects);
+}
+
+void MapProxy::drawTerrain(CRandomGenerator & generator, std::vector<int3> & tiles, TerrainId terrain)
+{
+    Lock lock(mx);
+	map.getEditManager()->getTerrainSelection().setSelection(tiles);
+	map.getEditManager()->drawTerrain(terrain, &generator);
+}
+
+void MapProxy::drawRivers(CRandomGenerator & generator, std::vector<int3> & tiles, TerrainId terrain)
+{
+    Lock lock(mx);
+	map.getEditManager()->getTerrainSelection().setSelection(tiles);
+	map.getEditManager()->drawRiver(VLC->terrainTypeHandler->getById(terrain)->river, &generator);
+}
+
+void MapProxy::drawRoads(CRandomGenerator & generator, std::vector<int3> & tiles, RoadId roadType)
+{
+    Lock lock(mx);
+    map.getEditManager()->getTerrainSelection().setSelection(tiles);
+	map.getEditManager()->drawRoad(roadType, &generator);
+}
+
+VCMI_LIB_NAMESPACE_END

+ 41 - 0
lib/rmg/threadpool/MapProxy.h

@@ -0,0 +1,41 @@
+/*
+ * MapProxy.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+#include "StdInc.h"
+#include "../../mapping/CMap.h"
+#include "../RmgMap.h"
+#include "../../mapping/CMapEditManager.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class RmgMap;
+
+class MapProxy
+{
+public:
+    MapProxy(RmgMap & map);
+
+    void insertObject(CGObjectInstance * obj);
+    void insertObjects(std::set<CGObjectInstance*>& objects);
+
+    void drawTerrain(CRandomGenerator & generator, std::vector<int3> & tiles, TerrainId terrain);
+    void drawRivers(CRandomGenerator & generator, std::vector<int3> & tiles, TerrainId terrain);
+    void drawRoads(CRandomGenerator & generator, std::vector<int3> & tiles, RoadId roadType);
+
+private:
+    mutable boost::shared_mutex mx;
+    using Lock = boost::unique_lock<boost::shared_mutex>;
+
+    RmgMap & map;
+};
+
+VCMI_LIB_NAMESPACE_END

+ 3 - 1
lib/rmg/threadpool/ThreadPool.h

@@ -11,12 +11,14 @@
 #pragma once
 
 #include "BlockingQueue.h"
-#include "JobProvider.h"
 #include <boost/thread/future.hpp>
 #include <boost/thread/condition_variable.hpp>
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+typedef std::function<void()> TRMGfunction ;
+typedef std::optional<TRMGfunction> TRMGJob;
+
 //Credit to https://github.com/Liam0205/toy-threadpool/tree/master/yuuki
 
 class DLL_LINKAGE ThreadPool

+ 8 - 4
mapeditor/mapcontroller.cpp

@@ -397,20 +397,24 @@ void MapController::commitObstacleFill(int level)
 		return;
 	
 	//split by zones
-	std::map<TerrainId, ObstacleProxy> terrainSelected;
+
+	std::map<TerrainId, std::unique_ptr<EditorObstaclePlacer>> terrainSelected;
 	for(auto & t : selection)
 	{
 		auto tl = _map->getTile(t);
 		if(tl.blocked || tl.visitable)
 			continue;
 		
-		terrainSelected[tl.terType->getId()].blockedArea.add(t);
+		auto terrain = tl.terType->getId();
+		//FIXME: This map can be populated once at launch, not need to collect objects every time
+		terrainSelected[terrain] = std::make_unique<EditorObstaclePlacer>(_map.get());
+		terrainSelected[terrain]->addBlockedTile(t);
 	}
 	
 	for(auto & sel : terrainSelected)
 	{
-		sel.second.collectPossibleObstacles(sel.first);
-		sel.second.placeObstacles(_map.get(), CRandomGenerator::getDefault());
+		sel.second->collectPossibleObstacles(sel.first);
+		sel.second->placeObstacles(CRandomGenerator::getDefault());
 	}
 
 	_mapHandler->invalidateObjects();