X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Femerge.cpp;h=0844707836ded07650d93ef4787acdffdb844d17;hb=9c635f28ac2b103bf7196e47947bf64673c13393;hp=9d3f94d48428f5d9a97a86af268b7c0783d5592c;hpb=7d002b60ff3d672d141e0de65702c8af5b60f83a;p=minetest.git diff --git a/src/emerge.cpp b/src/emerge.cpp index 9d3f94d48..084470783 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -19,138 +19,236 @@ with this program; if not, write to the Free Software Foundation, Inc., */ +#include "emerge.h" #include "server.h" #include #include -#include "clientserver.h" +#include "threading/event.h" #include "map.h" -#include "jmutexautolock.h" -#include "main.h" +#include "environment.h" +#include "util/container.h" +#include "util/thread.h" #include "constants.h" #include "voxel.h" #include "config.h" #include "mapblock.h" #include "serverobject.h" #include "settings.h" -#include "script.h" -#include "scriptapi.h" +#include "scripting_game.h" #include "profiler.h" #include "log.h" #include "nodedef.h" -#include "biome.h" -#include "emerge.h" +#include "mg_biome.h" +#include "mg_ore.h" +#include "mg_decoration.h" +#include "mg_schematic.h" +#include "mapgen_v5.h" #include "mapgen_v6.h" #include "mapgen_v7.h" -#include "mapgen_indev.h" #include "mapgen_singlenode.h" +struct MapgenDesc { + const char *name; + MapgenFactory *factory; +}; + +MapgenDesc reg_mapgens[] = { + {"v5", new MapgenFactoryV5}, + {"v6", new MapgenFactoryV6}, + {"v7", new MapgenFactoryV7}, + {"singlenode", new MapgenFactorySinglenode}, +}; + +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): + m_server(server), + map(NULL), + emerge(NULL), + mapgen(NULL), + enable_mapgen_debug_info(false), + id(ethreadid) + { + name = "Emerge-" + itos(id); + } + + void *run(); + bool popBlockEmerge(v3s16 *pos, u8 *flags); + bool getBlockOrStartGen(v3s16 p, MapBlock **b, + BlockMakeData *data, bool allow_generate); +}; + /////////////////////////////// Emerge Manager //////////////////////////////// -EmergeManager::EmergeManager(IGameDef *gamedef) { - //register built-in mapgens - registerMapgen("v6", new MapgenFactoryV6()); - //registerMapgen("v7", new MapgenFactoryV7()); - registerMapgen("indev", new MapgenFactoryIndev()); - registerMapgen("singlenode", new MapgenFactorySinglenode()); - - this->ndef = gamedef->getNodeDefManager(); - this->biomedef = new BiomeDefManager(); - this->params = NULL; - +EmergeManager::EmergeManager(IGameDef *gamedef) +{ + this->ndef = gamedef->getNodeDefManager(); + this->biomemgr = new BiomeManager(gamedef); + this->oremgr = new OreManager(gamedef); + this->decomgr = new DecorationManager(gamedef); + this->schemmgr = new SchematicManager(gamedef); + this->gen_notify_on = 0; + + // 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"); - queuemutex.Init(); - - 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"); - } + // if unspecified, leave a proc for the main thread and one for + // some other misc thread + s16 nthreads = 0; + if (!g_settings->getS16NoEx("num_emerge_threads", nthreads)) + nthreads = Thread::getNumberOfProcessors() - 2; if (nthreads < 1) 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"); - - for (int i = 0; i != nthreads; i++) - emergethread.push_back(new EmergeThread((Server *)gamedef, i)); - + + 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; + + // 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; + + for (s16 i = 0; i < nthreads; i++) + emergethread.push_back(new EmergeThread((Server *) gamedef, 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(); +EmergeManager::~EmergeManager() +{ + for (u32 i = 0; i != emergethread.size(); i++) { + if (threads_active) { + emergethread[i]->stop(); + emergethread[i]->qevent.signal(); + emergethread[i]->wait(); + } delete emergethread[i]; delete mapgen[i]; } emergethread.clear(); - - for (unsigned int i = 0; i < mapgen.size(); i++) - delete mapgen[i]; mapgen.clear(); - for (unsigned int i = 0; i < ores.size(); i++) - delete ores[i]; - ores.clear(); - - for (std::map::iterator iter = mglist.begin(); - iter != mglist.end(); iter ++) { - delete iter->second; + delete biomemgr; + delete oremgr; + delete decomgr; + delete schemmgr; + + if (params.sparams) { + delete params.sparams; + params.sparams = NULL; } - mglist.clear(); +} + - delete biomedef; +void EmergeManager::loadMapgenParams() +{ + params.load(*g_settings); } -void EmergeManager::initMapgens(MapgenParams *mgparams) { - Mapgen *mg; - +void EmergeManager::initMapgens() +{ if (mapgen.size()) return; - - biomedef->resolveNodeNames(ndef); - - this->params = mgparams; - for (unsigned int i = 0; i != emergethread.size(); i++) { - mg = createMapgen(params->mg_name, 0, params); - if (!mg) { - infostream << "EmergeManager: falling back to mapgen v6" << std::endl; - delete params; - params = createMapgenParams("v6"); - mg = createMapgen("v6", 0, 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); + } + + // Create the mapgens + for (u32 i = 0; i != emergethread.size(); i++) { + Mapgen *mg = createMapgen(params.mg_name, i, ¶ms); + assert(mg); mapgen.push_back(mg); } } -bool EmergeManager::enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate) { +Mapgen *EmergeManager::getCurrentMapgen() +{ + for (u32 i = 0; i != emergethread.size(); i++) { + if (emergethread[i]->isSameThread()) + return emergethread[i]->mapgen; + } + + return NULL; +} + + +void EmergeManager::startThreads() +{ + if (threads_active) + return; + + for (u32 i = 0; i != emergethread.size(); i++) + emergethread[i]->start(); + + threads_active = true; +} + + +void EmergeManager::stopThreads() +{ + if (!threads_active) + return; + + // Request thread stop in parallel + for (u32 i = 0; i != emergethread.size(); i++) { + emergethread[i]->stop(); + emergethread[i]->qevent.signal(); + } + + // Then do the waiting for each + for (u32 i = 0; i != emergethread.size(); i++) + emergethread[i]->wait(); + + 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; - + if (allow_generate) flags |= BLOCK_EMERGE_ALLOWGEN; { - JMutexAutoLock queuelock(queuemutex); - + MutexAutoLock queuelock(queuemutex); + count = blocks_enqueued.size(); if (count >= qlimit_total) return false; @@ -159,7 +257,7 @@ bool EmergeManager::enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate u16 qlimit_peer = allow_generate ? qlimit_generate : qlimit_diskonly; if (count >= qlimit_peer) return false; - + iter = blocks_enqueued.find(p); if (iter != blocks_enqueued.end()) { bedata = iter->second; @@ -171,39 +269,41 @@ bool EmergeManager::enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate bedata->flags = flags; bedata->peer_requested = peer_id; blocks_enqueued.insert(std::make_pair(p, bedata)); - + peer_queue_count[peer_id] = count + 1; - + // insert into the EmergeThread queue with the least items int lowestitems = emergethread[0]->blockqueue.size(); - for (unsigned int i = 1; i != emergethread.size(); i++) { + for (u32 i = 1; i != emergethread.size(); i++) { int nitems = emergethread[i]->blockqueue.size(); if (nitems < lowestitems) { idx = i; lowestitems = nitems; } } - + emergethread[idx]->blockqueue.push(p); } emergethread[idx]->qevent.signal(); - + return true; } -int EmergeManager::getGroundLevelAtPoint(v2s16 p) { +int EmergeManager::getGroundLevelAtPoint(v2s16 p) +{ if (mapgen.size() == 0 || !mapgen[0]) { errorstream << "EmergeManager: getGroundLevelAtPoint() called" - " before mapgen initialized" << std::endl; + " before mapgen initialized" << std::endl; return 0; } - + return mapgen[0]->getGroundLevelAtPoint(p); } -bool EmergeManager::isBlockUnderground(v3s16 blockpos) { +bool EmergeManager::isBlockUnderground(v3s16 blockpos) +{ /* v2s16 p = v2s16((blockpos.X * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2, (blockpos.Y * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2); @@ -213,119 +313,85 @@ bool EmergeManager::isBlockUnderground(v3s16 blockpos) { //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; + return blockpos.Y * (MAP_BLOCKSIZE + 1) <= params.water_level; } -u32 EmergeManager::getBlockSeed(v3s16 p) { - return (u32)(params->seed & 0xFFFFFFFF) + - p.Z * 38134234 + - p.Y * 42123 + - p.X * 23; +void EmergeManager::getMapgenNames(std::list &mgnames) +{ + for (u32 i = 0; i != ARRLEN(reg_mapgens); i++) + mgnames.push_back(reg_mapgens[i].name); } -Mapgen *EmergeManager::createMapgen(std::string mgname, int mgid, - MapgenParams *mgparams) { - std::map::const_iterator iter; - iter = mglist.find(mgname); - if (iter == mglist.end()) { +Mapgen *EmergeManager::createMapgen(const std::string &mgname, int mgid, + MapgenParams *mgparams) +{ + u32 i; + for (i = 0; i != ARRLEN(reg_mapgens) && mgname != reg_mapgens[i].name; i++); + if (i == ARRLEN(reg_mapgens)) { errorstream << "EmergeManager; mapgen " << mgname << - " not registered" << std::endl; + " not registered" << std::endl; return NULL; } - - MapgenFactory *mgfactory = iter->second; - return mgfactory->createMapgen(mgid, mgparams, this); -} - -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; - } - - MapgenFactory *mgfactory = iter->second; - return mgfactory->createMapgenParams(); + MapgenFactory *mgfactory = reg_mapgens[i].factory; + return mgfactory->createMapgen(mgid, mgparams, this); } -MapgenParams *EmergeManager::getParamsFromSettings(Settings *settings) { - std::string mg_name = settings->get("mg_name"); - MapgenParams *mgparams = createMapgenParams(mg_name); - if (!mgparams) - return NULL; - - mgparams->mg_name = mg_name; - mgparams->seed = settings->getU64(settings == g_settings ? "fixed_map_seed" : "seed"); - mgparams->water_level = settings->getS16("water_level"); - mgparams->chunksize = settings->getS16("chunksize"); - mgparams->flags = settings->getFlagStr("mg_flags", flagdesc_mapgen); - - if (!mgparams->readParams(settings)) { - delete mgparams; +MapgenSpecificParams *EmergeManager::createMapgenParams(const std::string &mgname) +{ + u32 i; + for (i = 0; i < ARRLEN(reg_mapgens) && mgname != reg_mapgens[i].name; i++); + if (i == ARRLEN(reg_mapgens)) { + errorstream << "EmergeManager: Mapgen " << mgname << + " not registered" << std::endl; return NULL; } - return mgparams; -} - - -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); - - params->writeParams(settings); -} - -void EmergeManager::registerMapgen(std::string mgname, MapgenFactory *mgfactory) { - mglist.insert(std::make_pair(mgname, mgfactory)); - infostream << "EmergeManager: registered mapgen " << mgname << std::endl; + MapgenFactory *mgfactory = reg_mapgens[i].factory; + return mgfactory->createMapgenParams(); } -////////////////////////////// Emerge Thread ////////////////////////////////// +////////////////////////////// Emerge Thread ////////////////////////////////// -bool EmergeThread::popBlockEmerge(v3s16 *pos, u8 *flags) { +bool EmergeThread::popBlockEmerge(v3s16 *pos, u8 *flags) +{ std::map::iterator iter; - JMutexAutoLock queuelock(emerge->queuemutex); + MutexAutoLock queuelock(emerge->queuemutex); if (blockqueue.empty()) return false; v3s16 p = blockqueue.front(); blockqueue.pop(); - + *pos = p; - + iter = emerge->blocks_enqueued.find(p); - if (iter == emerge->blocks_enqueued.end()) + if (iter == emerge->blocks_enqueued.end()) return false; //uh oh, queue and map out of sync!! BlockEmergeData *bedata = iter->second; *flags = bedata->flags; - + emerge->peer_queue_count[bedata->peer_requested]--; delete bedata; emerge->blocks_enqueued.erase(iter); - + return true; } -bool EmergeThread::getBlockOrStartGen(v3s16 p, MapBlock **b, - BlockMakeData *data, bool allow_gen) { +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); - + MutexAutoLock envlock(m_server->m_env_mutex); + // Load sector if it isn't loaded if (map->getSectorNoGenerateNoEx(p2d) == NULL) map->loadSectorMeta(p2d); @@ -335,6 +401,8 @@ bool EmergeThread::getBlockOrStartGen(v3s16 p, MapBlock **b, 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); } // If could not load and allowed to generate, @@ -344,28 +412,27 @@ bool EmergeThread::getBlockOrStartGen(v3s16 p, MapBlock **b, *b = block; return map->initBlockMake(data, p); } - + *b = block; return false; } -void *EmergeThread::Thread() { - ThreadStarted(); - log_register_thread("EmergeThread" + id); +void *EmergeThread::run() +{ DSTACK(__FUNCTION_NAME); BEGIN_DEBUG_EXCEPTION_HANDLER v3s16 last_tried_pos(-32768,-32768,-32768); // For error output v3s16 p; - u8 flags; - + u8 flags = 0; + map = (ServerMap *)&(m_server->m_env->getMap()); emerge = m_server->m_emerge; mapgen = emerge->mapgen[id]; enable_mapgen_debug_info = emerge->mapgen_debug_info; - - while (getRun()) + + while (!stopRequested()) try { if (!popBlockEmerge(&p, &flags)) { qevent.wait(); @@ -378,7 +445,7 @@ void *EmergeThread::Thread() { bool allow_generate = flags & BLOCK_EMERGE_ALLOWGEN; EMERGE_DBG_OUT("p=" PP(p) " allow_generate=" << allow_generate); - + /* Try to fetch block from memory or disk. If not found and asked to generate, initialize generator. @@ -386,8 +453,8 @@ void *EmergeThread::Thread() { BlockMakeData data; MapBlock *block = NULL; std::map modified_blocks; - - if (getBlockOrStartGen(p, &block, &data, allow_generate)) { + + if (getBlockOrStartGen(p, &block, &data, allow_generate) && mapgen) { { ScopeProfiler sp(g_profiler, "EmergeThread: Mapgen::makeChunk", SPT_AVG); TimeTaker t("mapgen::make_block()"); @@ -400,12 +467,12 @@ void *EmergeThread::Thread() { { //envlock: usually 0ms, but can take either 30 or 400ms to acquire - JMutexAutoLock envlock(m_server->m_env_mutex); + MutexAutoLock 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) { /* @@ -417,16 +484,18 @@ void *EmergeThread::Thread() { // Ignore map edit events, they will not need to be sent // to anybody because the block hasn't been sent to anybody - MapEditEventAreaIgnorer + MapEditEventAreaIgnorer ign(&m_server->m_ignore_map_edit_events_area, VoxelArea(minp, maxp)); - { // takes about 90ms with -O1 on an e3-1230v2 - scriptapi_environment_on_generated(m_server->m_lua, - minp, maxp, emerge->getBlockSeed(minp)); + try { // takes about 90ms with -O1 on an e3-1230v2 + m_server->getScriptIface()->environment_OnGenerated( + minp, maxp, mapgen->blockseed); + } catch (LuaError &e) { + m_server->setAsyncFatalError("Lua: " + std::string(e.what())); } EMERGE_DBG_OUT("ended up with: " << analyze_block(block)); - + m_server->m_env->activateBlock(block, 0); } } @@ -435,45 +504,52 @@ void *EmergeThread::Thread() { /* Set sent status of modified blocks on clients */ - - // 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); - } + if (modified_blocks.size() > 0) { + m_server->SetBlocksNotSent(modified_blocks); } } catch (VersionMismatchException &e) { std::ostringstream err; - err << "World data version mismatch in MapBlock "<setAsyncFatalError(err.str()); } catch (SerializationError &e) { std::ostringstream err; - err << "Invalid data in MapBlock "<setAsyncFatalError(err.str()); } - + + { + MutexAutoLock queuelock(emerge->queuemutex); + while (!blockqueue.empty()) + { + v3s16 p = blockqueue.front(); + blockqueue.pop(); + + std::map::iterator iter; + iter = emerge->blocks_enqueued.find(p); + if (iter == emerge->blocks_enqueued.end()) + continue; //uh oh, queue and map out of sync!! + + BlockEmergeData *bedata = iter->second; + delete bedata; + } + } + END_DEBUG_EXCEPTION_HANDLER(errorstream) - log_deregister_thread(); return NULL; }