X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Femerge.cpp;h=32e7d9f24d0492de3fc96a3df17599a8857329ad;hb=fb4815c660d6d9cfc9df76a3ba763095b9701925;hp=88bb3a1e65d78fa9fef4cfb3db79e8b23068b17d;hpb=7f743178db2a45bd3f68ef2c9c7df6deca1f3ab6;p=dragonfireclient.git diff --git a/src/emerge.cpp b/src/emerge.cpp index 88bb3a1e6..32e7d9f24 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -20,556 +20,720 @@ 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 "mapblock.h" -#include "serverobject.h" -#include "settings.h" -#include "scripting_game.h" -#include "profiler.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 "biome.h" -#include "mapgen_v6.h" -#include "mapgen_v7.h" -#include "mapgen_indev.h" -#include "mapgen_singlenode.h" -#include "mapgen_math.h" - +#include "profiler.h" +#include "scripting_server.h" +#include "server.h" +#include "settings.h" +#include "voxel.h" -class EmergeThread : public JThread -{ +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): - JThread(), - m_server(server), - map(NULL), - emerge(NULL), - mapgen(NULL), - enable_mapgen_debug_info(false), - id(ethreadid) + EmergeThread(Server *server, int ethreadid); + ~EmergeThread() = default; + + void *run(); + void signal(); + + // Requires queue mutex held + bool pushBlock(const v3s16 &pos); + + void cancelPendingItems(); + + static void runCompletionCallbacks( + const v3s16 &pos, EmergeAction action, + const EmergeCallbackList &callbacks); + +private: + Server *m_server; + ServerMap *m_map; + EmergeManager *m_emerge; + Mapgen *m_mapgen; + + Event m_queue_event; + std::queue m_block_queue; + + bool popBlockEmerge(v3s16 *pos, BlockEmergeData *bedata); + + EmergeAction getBlockOrStartGen( + const v3s16 &pos, bool allow_gen, MapBlock **block, BlockMakeData *data); + MapBlock *finishGen(v3s16 pos, BlockMakeData *bmdata, + std::map *modified_blocks); + + friend class EmergeManager; +}; + +class MapEditEventAreaIgnorer +{ +public: + MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a): + m_ignorevariable(ignorevariable) { + if(m_ignorevariable->getVolume() == 0) + *m_ignorevariable = a; + else + m_ignorevariable = NULL; } - void *Thread(); - bool popBlockEmerge(v3s16 *pos, u8 *flags); - bool getBlockOrStartGen(v3s16 p, MapBlock **b, - BlockMakeData *data, bool allow_generate); + ~MapEditEventAreaIgnorer() + { + if(m_ignorevariable) + { + assert(m_ignorevariable->getVolume() != 0); + *m_ignorevariable = VoxelArea(); + } + } + +private: + VoxelArea *m_ignorevariable; }; +EmergeParams::~EmergeParams() +{ + infostream << "EmergeParams: destroying " << this << std::endl; + // Delete everything that was cloned on creation of EmergeParams + delete biomegen; + delete biomemgr; + delete oremgr; + delete decomgr; + delete schemmgr; +} -/////////////////////////////// Emerge Manager //////////////////////////////// +EmergeParams::EmergeParams(EmergeManager *parent, const BiomeGen *biomegen, + const BiomeManager *biomemgr, + const OreManager *oremgr, const DecorationManager *decomgr, + const SchematicManager *schemmgr) : + ndef(parent->ndef), + enable_mapgen_debug_info(parent->enable_mapgen_debug_info), + gen_notify_on(parent->gen_notify_on), + gen_notify_on_deco_ids(&parent->gen_notify_on_deco_ids), + biomemgr(biomemgr->clone()), oremgr(oremgr->clone()), + decomgr(decomgr->clone()), schemmgr(schemmgr->clone()) +{ + this->biomegen = biomegen->clone(this->biomemgr); +} -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()); +//// +//// EmergeManager +//// - this->ndef = gamedef->getNodeDefManager(); - this->biomedef = new BiomeDefManager(); - this->gennotify = 0; +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); + + // initialized later + this->mgparams = nullptr; + this->biomegen = nullptr; // Note that accesses to this variable are not synchronized. // This is because the *only* thread ever starting or stopping // EmergeThreads should be the ServerThread. - this->threads_active = false; - mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info"); + enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info"); - // if unspecified, leave a proc for the main thread and one for + 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 - int nthreads = 0; - if (!g_settings->getS16NoEx("num_emerge_threads", nthreads)) - nthreads = porting::getNumberOfProcessors() - 2; + if (nthreads == 0) + nthreads = Thread::getNumberOfProcessors() - 2; if (nthreads < 1) nthreads = 1; - qlimit_total = g_settings->getU16("emergequeue_limit_total"); - if (!g_settings->getU16NoEx("emergequeue_limit_diskonly", qlimit_diskonly)) - qlimit_diskonly = nthreads * 5 + 1; - if (!g_settings->getU16NoEx("emergequeue_limit_generate", qlimit_generate)) - qlimit_generate = nthreads + 1; + m_qlimit_total = g_settings->getU16("emergequeue_limit_total"); + // FIXME: these fallback values are probably not good + 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; // don't trust user input for something very important like this - if (qlimit_total < 1) - qlimit_total = 1; - if (qlimit_diskonly < 1) - qlimit_diskonly = 1; - if (qlimit_generate < 1) - qlimit_generate = 1; + 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; - - loadParamsFromSettings(g_settings); - - if (g_settings->get("fixed_map_seed").empty()) { - params.seed = (((u64)(myrand() & 0xffff) << 0) - | ((u64)(myrand() & 0xffff) << 16) - | ((u64)(myrand() & 0xffff) << 32) - | ((u64)(myrand() & 0xffff) << 48)); - } } -EmergeManager::~EmergeManager() { - for (unsigned int i = 0; i != emergethread.size(); i++) { - if (threads_active) { - emergethread[i]->Stop(); - emergethread[i]->qevent.signal(); - emergethread[i]->Wait(); +EmergeManager::~EmergeManager() +{ + for (u32 i = 0; i != m_threads.size(); i++) { + EmergeThread *thread = m_threads[i]; + + if (m_threads_active) { + thread->stop(); + thread->signal(); + thread->wait(); } - delete emergethread[i]; - delete mapgen[i]; + + delete thread; + + // Mapgen init might not be finished if there is an error during startup. + if (m_mapgens.size() > i) + delete m_mapgens[i]; } - emergethread.clear(); - mapgen.clear(); - for (unsigned int i = 0; i < ores.size(); i++) - delete ores[i]; - ores.clear(); + delete biomemgr; + delete oremgr; + delete decomgr; + delete schemmgr; +} - for (unsigned int i = 0; i < decorations.size(); i++) - delete decorations[i]; - decorations.clear(); - for (std::map::iterator iter = mglist.begin(); - iter != mglist.end(); iter ++) { - delete iter->second; - } - mglist.clear(); +BiomeManager *EmergeManager::getWritableBiomeManager() +{ + FATAL_ERROR_IF(!m_mapgens.empty(), + "Writable managers can only be returned before mapgen init"); + return biomemgr; +} - delete biomedef; +OreManager *EmergeManager::getWritableOreManager() +{ + FATAL_ERROR_IF(!m_mapgens.empty(), + "Writable managers can only be returned before mapgen init"); + return oremgr; } +DecorationManager *EmergeManager::getWritableDecorationManager() +{ + FATAL_ERROR_IF(!m_mapgens.empty(), + "Writable managers can only be returned before mapgen init"); + return decomgr; +} -void EmergeManager::initMapgens() { - if (mapgen.size()) - return; +SchematicManager *EmergeManager::getWritableSchematicManager() +{ + FATAL_ERROR_IF(!m_mapgens.empty(), + "Writable managers can only be returned before mapgen init"); + return schemmgr; +} - // 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); +void EmergeManager::initMapgens(MapgenParams *params) +{ + FATAL_ERROR_IF(!m_mapgens.empty(), "Mapgen already initialised."); - for (size_t i = 0; i != decorations.size(); i++) - decorations[i]->resolveNodeNames(ndef); + mgparams = params; - if (!params.sparams) { - params.sparams = createMapgenParams(params.mg_name); - if (!params.sparams) { - params.mg_name = DEFAULT_MAPGEN; - params.sparams = createMapgenParams(params.mg_name); - assert(params.sparams); - } - params.sparams->readParams(g_settings); - } + v3s16 csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE); + biomegen = biomemgr->createBiomeGen(BIOMEGEN_ORIGINAL, params->bparams, csize); - // Create the mapgens - for (size_t i = 0; i != emergethread.size(); i++) { - Mapgen *mg = createMapgen(params.mg_name, i, ¶ms); - assert(mg); - mapgen.push_back(mg); + for (u32 i = 0; i != m_threads.size(); i++) { + EmergeParams *p = new EmergeParams(this, biomegen, + biomemgr, oremgr, decomgr, schemmgr); + infostream << "EmergeManager: Created params " << p + << " for thread " << i << std::endl; + m_mapgens.push_back(Mapgen::createMapgen(params->mgtype, params, p)); } } -Mapgen *EmergeManager::getCurrentMapgen() { - for (unsigned int i = 0; i != emergethread.size(); i++) { - if (emergethread[i]->IsSameThread()) - return emergethread[i]->mapgen; +Mapgen *EmergeManager::getCurrentMapgen() +{ + if (!m_threads_active) + return nullptr; + + for (u32 i = 0; i != m_threads.size(); i++) { + EmergeThread *t = m_threads[i]; + if (t->isRunning() && t->isCurrentThread()) + return t->m_mapgen; } - return NULL; + return nullptr; } -void EmergeManager::startThreads() { - if (threads_active) +void EmergeManager::startThreads() +{ + if (m_threads_active) return; - for (unsigned int i = 0; i != emergethread.size(); i++) - emergethread[i]->Start(); + for (u32 i = 0; i != m_threads.size(); i++) + m_threads[i]->start(); - threads_active = true; + m_threads_active = true; } -void EmergeManager::stopThreads() { - if (!threads_active) +void EmergeManager::stopThreads() +{ + if (!m_threads_active) return; // Request thread stop in parallel - for (unsigned int i = 0; i != emergethread.size(); i++) { - emergethread[i]->Stop(); - emergethread[i]->qevent.signal(); + for (u32 i = 0; i != m_threads.size(); i++) { + m_threads[i]->stop(); + m_threads[i]->signal(); } // Then do the waiting for each - for (unsigned int i = 0; i != emergethread.size(); i++) - emergethread[i]->Wait(); + for (u32 i = 0; i != m_threads.size(); i++) + m_threads[i]->wait(); - threads_active = false; + m_threads_active = false; } -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::isRunning() +{ + return m_threads_active; +} + +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(); - // 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; - } - } + return true; +} + + +// +// Mapgen-related helper functions +// + + +// TODO(hmmmm): Move this to ServerMap +v3s16 EmergeManager::getContainingChunk(v3s16 blockpos) +{ + return getContainingChunk(blockpos, mgparams->chunksize); +} - emergethread[idx]->blockqueue.push(p); +// 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; +} + + +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); } +// TODO(hmmmm): Move this to ServerMap +bool EmergeManager::isBlockUnderground(v3s16 blockpos) +{ + // Use a simple heuristic + return blockpos.Y * (MAP_BLOCKSIZE + 1) <= mgparams->water_level; +} -bool EmergeManager::isBlockUnderground(v3s16 blockpos) { - /* - 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); - */ +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]; - //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; -} + 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; + } else { + // limit block enqueue requests for active blocks to 1/2 of total + if (count_peer * 2 >= m_qlimit_total) + return false; + } + } + std::pair::iterator, bool> findres; + findres = m_blocks_enqueued.insert(std::make_pair(pos, BlockEmergeData())); -u32 EmergeManager::getBlockSeed(v3s16 p) { - return (u32)(params.seed & 0xFFFFFFFF) + - p.Z * 38134234 + - p.Y * 42123 + - p.X * 23; -} + BlockEmergeData &bedata = findres.first->second; + *entry_already_exists = !findres.second; + if (callback) + bedata.callbacks.emplace_back(callback, callback_param); -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; + 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; } -MapgenSpecificParams *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; + + 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); - MapgenFactory *mgfactory = iter->second; - return mgfactory->createMapgenParams(); + return true; } -void EmergeManager::loadParamsFromSettings(Settings *settings) { - std::string seed_str; - const char *setname = (settings == g_settings) ? "fixed_map_seed" : "seed"; +EmergeThread *EmergeManager::getOptimalThread() +{ + size_t nthreads = m_threads.size(); - if (settings->getNoEx(setname, seed_str)) - params.seed = read_seed(seed_str.c_str()); + FATAL_ERROR_IF(nthreads == 0, "No emerge threads!"); - settings->getNoEx("mg_name", params.mg_name); - settings->getS16NoEx("water_level", params.water_level); - settings->getS16NoEx("chunksize", params.chunksize); - settings->getFlagStrNoEx("mg_flags", params.flags, flagdesc_mapgen); + size_t index = 0; + size_t nitems_lowest = m_threads[0]->m_block_queue.size(); - delete params.sparams; - params.sparams = createMapgenParams(params.mg_name); - if (params.sparams) - params.sparams->readParams(settings); + 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 m_threads[index]; } -void EmergeManager::saveParamsToSettings(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 +//// - if (params.sparams) - params.sparams->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); - if (blockqueue.empty()) - return false; - v3s16 p = blockqueue.front(); - blockqueue.pop(); +void EmergeThread::cancelPendingItems() +{ + MutexAutoLock queuelock(m_emerge->m_queue_mutex); - *pos = p; + while (!m_block_queue.empty()) { + BlockEmergeData bedata; + v3s16 pos; - iter = emerge->blocks_enqueued.find(p); - if (iter == emerge->blocks_enqueued.end()) - return false; //uh oh, queue and map out of sync!! + pos = m_block_queue.front(); + m_block_queue.pop(); - BlockEmergeData *bedata = iter->second; - *flags = bedata->flags; + m_emerge->popBlockEmergeData(pos, &bedata); - emerge->peer_queue_count[bedata->peer_requested]--; + runCompletionCallbacks(pos, EMERGE_CANCELLED, bedata.callbacks); + } +} - 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; +} - // Load sector if it isn't loaded - if (map->getSectorNoGenerateNoEx(p2d) == NULL) - map->loadSectorMeta(p2d); - // 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 *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); + + /* + Perform post-processing on blocks (invalidate lighting, queue liquid + transforms, etc.) to finish block make + */ + m_map->finishBlockMake(bmdata, modified_blocks); + + 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 + */ + m_mapgen->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 (!StopRequested()) 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)); - try { // takes about 90ms with -O1 on an e3-1230v2 - m_server->getScriptIface()->environment_OnGenerated( - minp, maxp, emerge->getBlockSeed(minp)); - } catch(LuaError &e) { - m_server->setAsyncFatalError(e.what()); - } - - 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 - */ - // Add the originally fetched block to the modified list + runCompletionCallbacks(pos, action, bedata.callbacks); + if (block) - modified_blocks[p] = block; + modified_blocks[pos] = block; - if (modified_blocks.size() > 0) { + 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; }