X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Femerge.cpp;h=fc1da4ee74c04e5fbc02b1e047a201c65ce214f0;hb=d4b8075c3576688d94dad302cf97c4afc5fc41a5;hp=f096bb874a21e84dbd41250556a66d0ca1aee1d4;hpb=c3708b456e90bccf19e7c82c54a93c8cb7c8896c;p=dragonfireclient.git diff --git a/src/emerge.cpp b/src/emerge.cpp index f096bb874..fc1da4ee7 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -20,594 +20,658 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "emerge.h" -#include "server.h" + #include #include -#include "jthread/jevent.h" -#include "map.h" -#include "environment.h" + #include "util/container.h" #include "util/thread.h" -#include "main.h" -#include "constants.h" -#include "voxel.h" +#include "threading/event.h" + #include "config.h" +#include "constants.h" +#include "environment.h" +#include "log.h" +#include "map.h" #include "mapblock.h" +#include "mapgen/mg_biome.h" +#include "mapgen/mg_ore.h" +#include "mapgen/mg_decoration.h" +#include "mapgen/mg_schematic.h" +#include "nodedef.h" +#include "profiler.h" +#include "scripting_server.h" +#include "server.h" #include "serverobject.h" #include "settings.h" -#include "scripting_game.h" -#include "profiler.h" -#include "log.h" -#include "nodedef.h" -#include "biome.h" -#include "mapgen_v6.h" -#include "mapgen_v7.h" -#include "mapgen_indev.h" -#include "mapgen_singlenode.h" -#include "mapgen_math.h" - +#include "voxel.h" -class EmergeThread : public SimpleThread -{ +class EmergeThread : public Thread { public: - Server *m_server; - ServerMap *map; - EmergeManager *emerge; - Mapgen *mapgen; bool enable_mapgen_debug_info; int id; - Event qevent; - std::queue blockqueue; + EmergeThread(Server *server, int ethreadid); + ~EmergeThread() = default; - EmergeThread(Server *server, int ethreadid): - SimpleThread(), - m_server(server), - map(NULL), - emerge(NULL), - mapgen(NULL), - id(ethreadid) - { - } + void *run(); + void signal(); - void *Thread(); + // Requires queue mutex held + bool pushBlock(const v3s16 &pos); - void trigger() - { - setRun(true); - if(IsRunning() == false) - { - Start(); - } - } - - bool popBlockEmerge(v3s16 *pos, u8 *flags); - bool getBlockOrStartGen(v3s16 p, MapBlock **b, - BlockMakeData *data, bool allow_generate); -}; + void cancelPendingItems(); + static void runCompletionCallbacks( + const v3s16 &pos, EmergeAction action, + const EmergeCallbackList &callbacks); -/////////////////////////////// Emerge Manager //////////////////////////////// +private: + Server *m_server; + ServerMap *m_map; + EmergeManager *m_emerge; + Mapgen *m_mapgen; -EmergeManager::EmergeManager(IGameDef *gamedef) { - //register built-in mapgens - registerMapgen("v6", new MapgenFactoryV6()); - registerMapgen("v7", new MapgenFactoryV7()); - registerMapgen("indev", new MapgenFactoryIndev()); - registerMapgen("singlenode", new MapgenFactorySinglenode()); - registerMapgen("math", new MapgenFactoryMath()); + Event m_queue_event; + std::queue m_block_queue; - this->ndef = gamedef->getNodeDefManager(); - this->biomedef = new BiomeDefManager(); - this->params = NULL; + bool popBlockEmerge(v3s16 *pos, BlockEmergeData *bedata); - this->luaoverride_params = NULL; - this->luaoverride_params_modified = 0; - this->luaoverride_flagmask = 0; + EmergeAction getBlockOrStartGen( + const v3s16 &pos, bool allow_gen, MapBlock **block, BlockMakeData *data); + MapBlock *finishGen(v3s16 pos, BlockMakeData *bmdata, + std::map *modified_blocks); - this->gennotify = 0; + friend class EmergeManager; +}; - mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info"); +class MapEditEventAreaIgnorer +{ +public: + MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a): + m_ignorevariable(ignorevariable) + { + if(m_ignorevariable->getVolume() == 0) + *m_ignorevariable = a; + else + m_ignorevariable = NULL; + } - int nthreads; - if (g_settings->get("num_emerge_threads").empty()) { - int nprocs = porting::getNumberOfProcessors(); - // leave a proc for the main thread and one for some other misc threads - nthreads = (nprocs > 2) ? nprocs - 2 : 1; - } else { - nthreads = g_settings->getU16("num_emerge_threads"); + ~MapEditEventAreaIgnorer() + { + if(m_ignorevariable) + { + assert(m_ignorevariable->getVolume() != 0); + *m_ignorevariable = VoxelArea(); + } } + +private: + VoxelArea *m_ignorevariable; +}; + +//// +//// EmergeManager +//// + +EmergeManager::EmergeManager(Server *server) +{ + this->ndef = server->getNodeDefManager(); + this->biomemgr = new BiomeManager(server); + this->oremgr = new OreManager(server); + this->decomgr = new DecorationManager(server); + this->schemmgr = new SchematicManager(server); + + // Note that accesses to this variable are not synchronized. + // This is because the *only* thread ever starting or stopping + // EmergeThreads should be the ServerThread. + + enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info"); + + s16 nthreads = 1; + g_settings->getS16NoEx("num_emerge_threads", nthreads); + // If automatic, leave a proc for the main thread and one for + // some other misc thread + if (nthreads == 0) + nthreads = Thread::getNumberOfProcessors() - 2; if (nthreads < 1) nthreads = 1; + verbosestream << "Using " << nthreads << " emerge threads." << std::endl; + + m_qlimit_total = g_settings->getU16("emergequeue_limit_total"); + if (!g_settings->getU16NoEx("emergequeue_limit_diskonly", m_qlimit_diskonly)) + m_qlimit_diskonly = nthreads * 5 + 1; + if (!g_settings->getU16NoEx("emergequeue_limit_generate", m_qlimit_generate)) + m_qlimit_generate = nthreads + 1; - qlimit_total = g_settings->getU16("emergequeue_limit_total"); - qlimit_diskonly = g_settings->get("emergequeue_limit_diskonly").empty() ? - nthreads * 5 + 1 : - g_settings->getU16("emergequeue_limit_diskonly"); - qlimit_generate = g_settings->get("emergequeue_limit_generate").empty() ? - nthreads + 1 : - g_settings->getU16("emergequeue_limit_generate"); + // don't trust user input for something very important like this + if (m_qlimit_total < 1) + m_qlimit_total = 1; + if (m_qlimit_diskonly < 1) + m_qlimit_diskonly = 1; + if (m_qlimit_generate < 1) + m_qlimit_generate = 1; - for (int i = 0; i != nthreads; i++) - emergethread.push_back(new EmergeThread((Server *)gamedef, i)); + for (s16 i = 0; i < nthreads; i++) + m_threads.push_back(new EmergeThread(server, i)); infostream << "EmergeManager: using " << nthreads << " threads" << std::endl; } -EmergeManager::~EmergeManager() { - for (unsigned int i = 0; i != emergethread.size(); i++) { - emergethread[i]->setRun(false); - emergethread[i]->qevent.signal(); - emergethread[i]->stop(); - delete emergethread[i]; - delete mapgen[i]; - } - emergethread.clear(); - mapgen.clear(); +EmergeManager::~EmergeManager() +{ + for (u32 i = 0; i != m_threads.size(); i++) { + EmergeThread *thread = m_threads[i]; - for (unsigned int i = 0; i < ores.size(); i++) - delete ores[i]; - ores.clear(); + if (m_threads_active) { + thread->stop(); + thread->signal(); + thread->wait(); + } - for (unsigned int i = 0; i < decorations.size(); i++) - delete decorations[i]; - decorations.clear(); + delete thread; - for (std::map::iterator iter = mglist.begin(); - iter != mglist.end(); iter ++) { - delete iter->second; + // Mapgen init might not be finished if there is an error during startup. + if (m_mapgens.size() > i) + delete m_mapgens[i]; } - mglist.clear(); - delete biomedef; + delete biomemgr; + delete oremgr; + delete decomgr; + delete schemmgr; } -void EmergeManager::initMapgens(MapgenParams *mgparams) { - Mapgen *mg; - - if (mapgen.size()) - return; +void EmergeManager::initMapgens(MapgenParams *params) +{ + FATAL_ERROR_IF(!m_mapgens.empty(), "Mapgen already initialised."); - // Resolve names of nodes for things that were registered - // (at this point, the registration period is over) - biomedef->resolveNodeNames(ndef); - - for (size_t i = 0; i != ores.size(); i++) - ores[i]->resolveNodeNames(ndef); - - for (size_t i = 0; i != decorations.size(); i++) - decorations[i]->resolveNodeNames(ndef); - - // Apply mapgen parameter overrides from Lua - if (luaoverride_params) { - if (luaoverride_params_modified & MGPARAMS_SET_MGNAME) { - MapgenParams *mgp = setMapgenType(mgparams, luaoverride_params->mg_name); - if (!mgp) { - errorstream << "EmergeManager: Failed to set new mapgen name" - << std::endl; - } else { - mgparams = mgp; - } - } + mgparams = params; - if (luaoverride_params_modified & MGPARAMS_SET_SEED) - mgparams->seed = luaoverride_params->seed; + for (u32 i = 0; i != m_threads.size(); i++) + m_mapgens.push_back(Mapgen::createMapgen(params->mgtype, params, this)); +} - if (luaoverride_params_modified & MGPARAMS_SET_WATER_LEVEL) - mgparams->water_level = luaoverride_params->water_level; - if (luaoverride_params_modified & MGPARAMS_SET_FLAGS) { - mgparams->flags &= ~luaoverride_flagmask; - mgparams->flags |= luaoverride_params->flags; - } +Mapgen *EmergeManager::getCurrentMapgen() +{ + if (!m_threads_active) + return nullptr; - delete luaoverride_params; - luaoverride_params = NULL; + for (u32 i = 0; i != m_threads.size(); i++) { + if (m_threads[i]->isCurrentThread()) + return m_threads[i]->m_mapgen; } - // Create the mapgens - this->params = mgparams; - for (size_t i = 0; i != emergethread.size(); i++) { - mg = createMapgen(params->mg_name, i, params); - if (!mg) { - infostream << "EmergeManager: Falling back to Mapgen V6" << std::endl; - - params = setMapgenType(params, "v6"); - mg = createMapgen(params->mg_name, i, params); - if (!mg) { - errorstream << "EmergeManager: CRITICAL ERROR: Failed to fall" - "back to Mapgen V6, not generating map" << std::endl; - } - } - mapgen.push_back(mg); - } + return nullptr; } -MapgenParams *EmergeManager::setMapgenType(MapgenParams *mgparams, - std::string newname) { - MapgenParams *newparams = createMapgenParams(newname); - if (!newparams) { - errorstream << "EmergeManager: Mapgen override failed" << std::endl; - return NULL; - } - - newparams->mg_name = newname; - newparams->seed = mgparams->seed; - newparams->water_level = mgparams->water_level; - newparams->chunksize = mgparams->chunksize; - newparams->flags = mgparams->flags; +void EmergeManager::startThreads() +{ + if (m_threads_active) + return; - if (!newparams->readParams(g_settings)) { - errorstream << "EmergeManager: Mapgen override failed" << std::endl; - delete newparams; - return NULL; - } + for (u32 i = 0; i != m_threads.size(); i++) + m_threads[i]->start(); - delete mgparams; - return newparams; + m_threads_active = true; } -Mapgen *EmergeManager::getCurrentMapgen() { - for (unsigned int i = 0; i != emergethread.size(); i++) { - if (emergethread[i]->IsSameThread()) - return emergethread[i]->mapgen; +void EmergeManager::stopThreads() +{ + if (!m_threads_active) + return; + + // Request thread stop in parallel + for (u32 i = 0; i != m_threads.size(); i++) { + m_threads[i]->stop(); + m_threads[i]->signal(); } - return NULL; + // Then do the waiting for each + for (u32 i = 0; i != m_threads.size(); i++) + m_threads[i]->wait(); + + m_threads_active = false; } -void EmergeManager::triggerAllThreads() { - for (unsigned int i = 0; i != emergethread.size(); i++) - emergethread[i]->trigger(); +bool EmergeManager::isRunning() +{ + return m_threads_active; } -bool EmergeManager::enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate) { - std::map::const_iterator iter; - BlockEmergeData *bedata; - u16 count; - u8 flags = 0; - int idx = 0; - +bool EmergeManager::enqueueBlockEmerge( + session_t peer_id, + v3s16 blockpos, + bool allow_generate, + bool ignore_queue_limits) +{ + u16 flags = 0; if (allow_generate) - flags |= BLOCK_EMERGE_ALLOWGEN; + flags |= BLOCK_EMERGE_ALLOW_GEN; + if (ignore_queue_limits) + flags |= BLOCK_EMERGE_FORCE_QUEUE; - { - JMutexAutoLock queuelock(queuemutex); + return enqueueBlockEmergeEx(blockpos, peer_id, flags, NULL, NULL); +} - count = blocks_enqueued.size(); - if (count >= qlimit_total) - return false; - count = peer_queue_count[peer_id]; - u16 qlimit_peer = allow_generate ? qlimit_generate : qlimit_diskonly; - if (count >= qlimit_peer) +bool EmergeManager::enqueueBlockEmergeEx( + v3s16 blockpos, + session_t peer_id, + u16 flags, + EmergeCompletionCallback callback, + void *callback_param) +{ + EmergeThread *thread = NULL; + bool entry_already_exists = false; + + { + MutexAutoLock queuelock(m_queue_mutex); + + if (!pushBlockEmergeData(blockpos, peer_id, flags, + callback, callback_param, &entry_already_exists)) return false; - iter = blocks_enqueued.find(p); - if (iter != blocks_enqueued.end()) { - bedata = iter->second; - bedata->flags |= flags; + if (entry_already_exists) return true; - } - bedata = new BlockEmergeData; - bedata->flags = flags; - bedata->peer_requested = peer_id; - blocks_enqueued.insert(std::make_pair(p, bedata)); + thread = getOptimalThread(); + thread->pushBlock(blockpos); + } - peer_queue_count[peer_id] = count + 1; + thread->signal(); + + return true; +} + + +// +// Mapgen-related helper functions +// + + +// TODO(hmmmm): Move this to ServerMap +v3s16 EmergeManager::getContainingChunk(v3s16 blockpos) +{ + return getContainingChunk(blockpos, mgparams->chunksize); +} + +// TODO(hmmmm): Move this to ServerMap +v3s16 EmergeManager::getContainingChunk(v3s16 blockpos, s16 chunksize) +{ + s16 coff = -chunksize / 2; + v3s16 chunk_offset(coff, coff, coff); + + return getContainerPos(blockpos - chunk_offset, chunksize) + * chunksize + chunk_offset; +} - // insert into the EmergeThread queue with the least items - int lowestitems = emergethread[0]->blockqueue.size(); - for (unsigned int i = 1; i != emergethread.size(); i++) { - int nitems = emergethread[i]->blockqueue.size(); - if (nitems < lowestitems) { - idx = i; - lowestitems = nitems; - } - } - emergethread[idx]->blockqueue.push(p); +int EmergeManager::getSpawnLevelAtPoint(v2s16 p) +{ + if (m_mapgens.empty() || !m_mapgens[0]) { + errorstream << "EmergeManager: getSpawnLevelAtPoint() called" + " before mapgen init" << std::endl; + return 0; } - emergethread[idx]->qevent.signal(); - return true; + return m_mapgens[0]->getSpawnLevelAtPoint(p); } -int EmergeManager::getGroundLevelAtPoint(v2s16 p) { - if (mapgen.size() == 0 || !mapgen[0]) { +int EmergeManager::getGroundLevelAtPoint(v2s16 p) +{ + if (m_mapgens.empty() || !m_mapgens[0]) { errorstream << "EmergeManager: getGroundLevelAtPoint() called" - " before mapgen initialized" << std::endl; + " before mapgen init" << std::endl; return 0; } - return mapgen[0]->getGroundLevelAtPoint(p); + return m_mapgens[0]->getGroundLevelAtPoint(p); } - -bool EmergeManager::isBlockUnderground(v3s16 blockpos) { - /* +// TODO(hmmmm): Move this to ServerMap +bool EmergeManager::isBlockUnderground(v3s16 blockpos) +{ +#if 0 v2s16 p = v2s16((blockpos.X * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2, (blockpos.Y * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2); int ground_level = getGroundLevelAtPoint(p); return blockpos.Y * (MAP_BLOCKSIZE + 1) <= min(water_level, ground_level); - */ +#endif - //yuck, but then again, should i bother being accurate? - //the height of the nodes in a single block is quite variable - return blockpos.Y * (MAP_BLOCKSIZE + 1) <= params->water_level; + // Use a simple heuristic; the above method is wildly inaccurate anyway. + return blockpos.Y * (MAP_BLOCKSIZE + 1) <= mgparams->water_level; } +bool EmergeManager::pushBlockEmergeData( + v3s16 pos, + u16 peer_requested, + u16 flags, + EmergeCompletionCallback callback, + void *callback_param, + bool *entry_already_exists) +{ + u16 &count_peer = m_peer_queue_count[peer_requested]; -u32 EmergeManager::getBlockSeed(v3s16 p) { - return (u32)(params->seed & 0xFFFFFFFF) + - p.Z * 38134234 + - p.Y * 42123 + - p.X * 23; -} + if ((flags & BLOCK_EMERGE_FORCE_QUEUE) == 0) { + if (m_blocks_enqueued.size() >= m_qlimit_total) + return false; + if (peer_requested != PEER_ID_INEXISTENT) { + u16 qlimit_peer = (flags & BLOCK_EMERGE_ALLOW_GEN) ? + m_qlimit_generate : m_qlimit_diskonly; + if (count_peer >= qlimit_peer) + return false; + } + } -Mapgen *EmergeManager::createMapgen(std::string mgname, int mgid, - MapgenParams *mgparams) { - std::map::const_iterator iter; - iter = mglist.find(mgname); - if (iter == mglist.end()) { - errorstream << "EmergeManager; mapgen " << mgname << - " not registered" << std::endl; - return NULL; + std::pair::iterator, bool> findres; + findres = m_blocks_enqueued.insert(std::make_pair(pos, BlockEmergeData())); + + BlockEmergeData &bedata = findres.first->second; + *entry_already_exists = !findres.second; + + if (callback) + bedata.callbacks.emplace_back(callback, callback_param); + + if (*entry_already_exists) { + bedata.flags |= flags; + } else { + bedata.flags = flags; + bedata.peer_requested = peer_requested; + + count_peer++; } - MapgenFactory *mgfactory = iter->second; - return mgfactory->createMapgen(mgid, mgparams, this); + return true; } -MapgenParams *EmergeManager::createMapgenParams(std::string mgname) { - std::map::const_iterator iter; - iter = mglist.find(mgname); - if (iter == mglist.end()) { - errorstream << "EmergeManager: mapgen " << mgname << - " not registered" << std::endl; - return NULL; - } +bool EmergeManager::popBlockEmergeData(v3s16 pos, BlockEmergeData *bedata) +{ + std::map::iterator it; + std::unordered_map::iterator it2; - MapgenFactory *mgfactory = iter->second; - return mgfactory->createMapgenParams(); + it = m_blocks_enqueued.find(pos); + if (it == m_blocks_enqueued.end()) + return false; + + *bedata = it->second; + + it2 = m_peer_queue_count.find(bedata->peer_requested); + if (it2 == m_peer_queue_count.end()) + return false; + + u16 &count_peer = it2->second; + assert(count_peer != 0); + count_peer--; + + m_blocks_enqueued.erase(it); + + return true; } -MapgenParams *EmergeManager::getParamsFromSettings(Settings *settings) { - std::string mg_name = settings->get("mg_name"); - MapgenParams *mgparams = createMapgenParams(mg_name); - if (!mgparams) - return NULL; +EmergeThread *EmergeManager::getOptimalThread() +{ + size_t nthreads = m_threads.size(); - std::string seedstr = settings->get(settings == g_settings ? - "fixed_map_seed" : "seed"); + FATAL_ERROR_IF(nthreads == 0, "No emerge threads!"); - mgparams->mg_name = mg_name; - mgparams->seed = read_seed(seedstr.c_str()); - mgparams->water_level = settings->getS16("water_level"); - mgparams->chunksize = settings->getS16("chunksize"); - mgparams->flags = settings->getFlagStr("mg_flags", flagdesc_mapgen); + size_t index = 0; + size_t nitems_lowest = m_threads[0]->m_block_queue.size(); - if (!mgparams->readParams(settings)) { - delete mgparams; - return NULL; + for (size_t i = 1; i < nthreads; i++) { + size_t nitems = m_threads[i]->m_block_queue.size(); + if (nitems < nitems_lowest) { + index = i; + nitems_lowest = nitems; + } } - return mgparams; + + return m_threads[index]; } -void EmergeManager::setParamsToSettings(Settings *settings) { - settings->set("mg_name", params->mg_name); - settings->setU64("seed", params->seed); - settings->setS16("water_level", params->water_level); - settings->setS16("chunksize", params->chunksize); - settings->setFlagStr("mg_flags", params->flags, flagdesc_mapgen); +//// +//// EmergeThread +//// - params->writeParams(settings); +EmergeThread::EmergeThread(Server *server, int ethreadid) : + enable_mapgen_debug_info(false), + id(ethreadid), + m_server(server), + m_map(NULL), + m_emerge(NULL), + m_mapgen(NULL) +{ + m_name = "Emerge-" + itos(ethreadid); } -void EmergeManager::registerMapgen(std::string mgname, MapgenFactory *mgfactory) { - mglist.insert(std::make_pair(mgname, mgfactory)); - infostream << "EmergeManager: registered mapgen " << mgname << std::endl; +void EmergeThread::signal() +{ + m_queue_event.signal(); } -////////////////////////////// Emerge Thread ////////////////////////////////// +bool EmergeThread::pushBlock(const v3s16 &pos) +{ + m_block_queue.push(pos); + return true; +} + -bool EmergeThread::popBlockEmerge(v3s16 *pos, u8 *flags) { - std::map::iterator iter; - JMutexAutoLock queuelock(emerge->queuemutex); +void EmergeThread::cancelPendingItems() +{ + MutexAutoLock queuelock(m_emerge->m_queue_mutex); - if (blockqueue.empty()) - return false; - v3s16 p = blockqueue.front(); - blockqueue.pop(); + while (!m_block_queue.empty()) { + BlockEmergeData bedata; + v3s16 pos; - *pos = p; + pos = m_block_queue.front(); + m_block_queue.pop(); - iter = emerge->blocks_enqueued.find(p); - if (iter == emerge->blocks_enqueued.end()) - return false; //uh oh, queue and map out of sync!! + m_emerge->popBlockEmergeData(pos, &bedata); - BlockEmergeData *bedata = iter->second; - *flags = bedata->flags; + runCompletionCallbacks(pos, EMERGE_CANCELLED, bedata.callbacks); + } +} - emerge->peer_queue_count[bedata->peer_requested]--; - delete bedata; - emerge->blocks_enqueued.erase(iter); +void EmergeThread::runCompletionCallbacks(const v3s16 &pos, EmergeAction action, + const EmergeCallbackList &callbacks) +{ + for (size_t i = 0; i != callbacks.size(); i++) { + EmergeCompletionCallback callback; + void *param; + + callback = callbacks[i].first; + param = callbacks[i].second; + + callback(pos, action, param); + } +} + + +bool EmergeThread::popBlockEmerge(v3s16 *pos, BlockEmergeData *bedata) +{ + MutexAutoLock queuelock(m_emerge->m_queue_mutex); + + if (m_block_queue.empty()) + return false; + + *pos = m_block_queue.front(); + m_block_queue.pop(); + + m_emerge->popBlockEmergeData(*pos, bedata); return true; } -bool EmergeThread::getBlockOrStartGen(v3s16 p, MapBlock **b, - BlockMakeData *data, bool allow_gen) { - v2s16 p2d(p.X, p.Z); - //envlock: usually takes <=1ms, sometimes 90ms or ~400ms to acquire - JMutexAutoLock envlock(m_server->m_env_mutex); +EmergeAction EmergeThread::getBlockOrStartGen( + const v3s16 &pos, bool allow_gen, MapBlock **block, BlockMakeData *bmdata) +{ + MutexAutoLock envlock(m_server->m_env_mutex); + + // 1). Attempt to fetch block from memory + *block = m_map->getBlockNoCreateNoEx(pos); + if (*block && !(*block)->isDummy()) { + if ((*block)->isGenerated()) + return EMERGE_FROM_MEMORY; + } else { + // 2). Attempt to load block from disk if it was not in the memory + *block = m_map->loadBlock(pos); + if (*block && (*block)->isGenerated()) + return EMERGE_FROM_DISK; + } + + // 3). Attempt to start generation + if (allow_gen && m_map->initBlockMake(pos, bmdata)) + return EMERGE_GENERATED; + + // All attempts failed; cancel this block emerge + return EMERGE_CANCELLED; +} + + +MapBlock *EmergeThread::finishGen(v3s16 pos, BlockMakeData *bmdata, + std::map *modified_blocks) +{ + MutexAutoLock envlock(m_server->m_env_mutex); + ScopeProfiler sp(g_profiler, + "EmergeThread: after Mapgen::makeChunk", SPT_AVG); - // Load sector if it isn't loaded - if (map->getSectorNoGenerateNoEx(p2d) == NULL) - map->loadSectorMeta(p2d); + /* + Perform post-processing on blocks (invalidate lighting, queue liquid + transforms, etc.) to finish block make + */ + m_map->finishBlockMake(bmdata, modified_blocks); - // Attempt to load block - MapBlock *block = map->getBlockNoCreateNoEx(p); - if (!block || block->isDummy() || !block->isGenerated()) { - EMERGE_DBG_OUT("not in memory, attempting to load from disk"); - block = map->loadBlock(p); - if (block && block->isGenerated()) - map->prepareBlock(block); + MapBlock *block = m_map->getBlockNoCreateNoEx(pos); + if (!block) { + errorstream << "EmergeThread::finishGen: Couldn't grab block we " + "just generated: " << PP(pos) << std::endl; + return NULL; } - // If could not load and allowed to generate, - // start generation inside this same envlock - if (allow_gen && (block == NULL || !block->isGenerated())) { - EMERGE_DBG_OUT("generating"); - *b = block; - return map->initBlockMake(data, p); + v3s16 minp = bmdata->blockpos_min * MAP_BLOCKSIZE; + v3s16 maxp = bmdata->blockpos_max * MAP_BLOCKSIZE + + v3s16(1,1,1) * (MAP_BLOCKSIZE - 1); + + // Ignore map edit events, they will not need to be sent + // to anybody because the block hasn't been sent to anybody + MapEditEventAreaIgnorer ign( + &m_server->m_ignore_map_edit_events_area, + VoxelArea(minp, maxp)); + + /* + Run Lua on_generated callbacks + */ + try { + m_server->getScriptIface()->environment_OnGenerated( + minp, maxp, m_mapgen->blockseed); + } catch (LuaError &e) { + m_server->setAsyncFatalError("Lua: finishGen" + std::string(e.what())); } - *b = block; - return false; + /* + Clear generate notifier events + */ + Mapgen *mg = m_emerge->getCurrentMapgen(); + mg->gennotify.clearEvents(); + + EMERGE_DBG_OUT("ended up with: " << analyze_block(block)); + + /* + Activate the block + */ + m_server->m_env->activateBlock(block, 0); + + return block; } -void *EmergeThread::Thread() { - ThreadStarted(); - log_register_thread("EmergeThread" + itos(id)); - DSTACK(__FUNCTION_NAME); +void *EmergeThread::run() +{ BEGIN_DEBUG_EXCEPTION_HANDLER - v3s16 last_tried_pos(-32768,-32768,-32768); // For error output - v3s16 p; - u8 flags; + v3s16 pos; - map = (ServerMap *)&(m_server->m_env->getMap()); - emerge = m_server->m_emerge; - mapgen = emerge->mapgen[id]; - enable_mapgen_debug_info = emerge->mapgen_debug_info; + m_map = (ServerMap *)&(m_server->m_env->getMap()); + m_emerge = m_server->m_emerge; + m_mapgen = m_emerge->m_mapgens[id]; + enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info; - while (getRun()) try { - if (!popBlockEmerge(&p, &flags)) { - qevent.wait(); + while (!stopRequested()) { + std::map modified_blocks; + BlockEmergeData bedata; + BlockMakeData bmdata; + EmergeAction action; + MapBlock *block; + + if (!popBlockEmerge(&pos, &bedata)) { + m_queue_event.wait(); continue; } - last_tried_pos = p; - if (blockpos_over_limit(p)) + if (blockpos_over_max_limit(pos)) continue; - bool allow_generate = flags & BLOCK_EMERGE_ALLOWGEN; - EMERGE_DBG_OUT("p=" PP(p) " allow_generate=" << allow_generate); + bool allow_gen = bedata.flags & BLOCK_EMERGE_ALLOW_GEN; + EMERGE_DBG_OUT("pos=" PP(pos) " allow_gen=" << allow_gen); - /* - Try to fetch block from memory or disk. - If not found and asked to generate, initialize generator. - */ - BlockMakeData data; - MapBlock *block = NULL; - std::map modified_blocks; - - if (getBlockOrStartGen(p, &block, &data, allow_generate) && mapgen) { + action = getBlockOrStartGen(pos, allow_gen, &block, &bmdata); + if (action == EMERGE_GENERATED) { { - ScopeProfiler sp(g_profiler, "EmergeThread: Mapgen::makeChunk", SPT_AVG); - TimeTaker t("mapgen::make_block()"); - - mapgen->makeChunk(&data); + ScopeProfiler sp(g_profiler, + "EmergeThread: Mapgen::makeChunk", SPT_AVG); - if (enable_mapgen_debug_info == false) - t.stop(true); // Hide output + m_mapgen->makeChunk(&bmdata); } - { - //envlock: usually 0ms, but can take either 30 or 400ms to acquire - JMutexAutoLock envlock(m_server->m_env_mutex); - ScopeProfiler sp(g_profiler, "EmergeThread: after " - "Mapgen::makeChunk (envlock)", SPT_AVG); - - map->finishBlockMake(&data, modified_blocks); - - block = map->getBlockNoCreateNoEx(p); - if (block) { - /* - Do some post-generate stuff - */ - v3s16 minp = data.blockpos_min * MAP_BLOCKSIZE; - v3s16 maxp = data.blockpos_max * MAP_BLOCKSIZE + - v3s16(1,1,1) * (MAP_BLOCKSIZE - 1); - - // Ignore map edit events, they will not need to be sent - // to anybody because the block hasn't been sent to anybody - MapEditEventAreaIgnorer - ign(&m_server->m_ignore_map_edit_events_area, - VoxelArea(minp, maxp)); - { // takes about 90ms with -O1 on an e3-1230v2 - m_server->getScriptIface()->environment_OnGenerated( - minp, maxp, emerge->getBlockSeed(minp)); - } - - EMERGE_DBG_OUT("ended up with: " << analyze_block(block)); - - m_server->m_env->activateBlock(block, 0); - } - } + block = finishGen(pos, &bmdata, &modified_blocks); } - /* - Set sent status of modified blocks on clients - */ + runCompletionCallbacks(pos, action, bedata.callbacks); - // NOTE: Server's clients are also behind the connection mutex - //conlock: consistently takes 30-40ms to acquire - JMutexAutoLock lock(m_server->m_con_mutex); - // Add the originally fetched block to the modified list if (block) - modified_blocks[p] = block; - - // Set the modified blocks unsent for all the clients - for (std::map::iterator - i = m_server->m_clients.begin(); - i != m_server->m_clients.end(); ++i) { - RemoteClient *client = i->second; - if (modified_blocks.size() > 0) { - // Remove block from sent history - client->SetBlocksNotSent(modified_blocks); - } - } + modified_blocks[pos] = block; + + if (!modified_blocks.empty()) + m_server->SetBlocksNotSent(modified_blocks); } - catch (VersionMismatchException &e) { + } catch (VersionMismatchException &e) { std::ostringstream err; - err << "World data version mismatch in MapBlock "<setAsyncFatalError(err.str()); - } - catch (SerializationError &e) { + } catch (SerializationError &e) { std::ostringstream err; - err << "Invalid data in MapBlock "<setAsyncFatalError(err.str()); } - END_DEBUG_EXCEPTION_HANDLER(errorstream) - log_deregister_thread(); + END_DEBUG_EXCEPTION_HANDLER return NULL; }