X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Femerge.cpp;h=8d818612618e17aeefa8f0b0ba2ec1ec3020819c;hb=7289d61e99625b46eb2c4d6b90a2a5de42f207e6;hp=167473ecf7c3bbd5dd83057d70aabbdc4e5f1449;hpb=e396fb29840c3b87b0442fe6d641c94e8165ed27;p=minetest.git diff --git a/src/emerge.cpp b/src/emerge.cpp index 167473ecf..8d8186126 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include #include +#include "jthread/jevent.h" #include "map.h" #include "environment.h" #include "util/container.h" @@ -38,15 +39,28 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "profiler.h" #include "log.h" #include "nodedef.h" -#include "biome.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" -#include "mapgen_math.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 SimpleThread +class EmergeThread : public JThread { public: Server *m_server; @@ -55,31 +69,22 @@ class EmergeThread : public SimpleThread Mapgen *mapgen; bool enable_mapgen_debug_info; int id; - + Event qevent; std::queue blockqueue; - + EmergeThread(Server *server, int ethreadid): - SimpleThread(), + JThread(), m_server(server), map(NULL), emerge(NULL), mapgen(NULL), + enable_mapgen_debug_info(false), id(ethreadid) { } void *Thread(); - - 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); @@ -88,160 +93,170 @@ class EmergeThread : public SimpleThread /////////////////////////////// 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()); - registerMapgen("math", new MapgenFactoryMath()); - - this->ndef = gamedef->getNodeDefManager(); - this->biomedef = new BiomeDefManager(); - this->params = NULL; - - this->luaoverride_params = NULL; - this->luaoverride_params_modified = 0; - this->luaoverride_flagmask = 0; - +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 = porting::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(); mapgen.clear(); - for (unsigned int i = 0; i < ores.size(); i++) - delete ores[i]; - ores.clear(); - - 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; + delete biomemgr; + delete oremgr; + delete decomgr; + delete schemmgr; + + if (params.sparams) { + delete params.sparams; + params.sparams = NULL; } - mglist.clear(); +} + - delete biomedef; +void EmergeManager::loadMapgenParams() +{ + 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)); + } } -void EmergeManager::initMapgens(MapgenParams *mgparams) { - Mapgen *mg; - +void EmergeManager::initMapgens() +{ if (mapgen.size()) return; - - // 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) - mgparams->mg_name = luaoverride_params->mg_name; - - if (luaoverride_params_modified & MGPARAMS_SET_SEED) - mgparams->seed = luaoverride_params->seed; - - 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; + + 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); } - - delete luaoverride_params; - luaoverride_params = NULL; + params.sparams->readParams(g_settings); } - + // Create the mapgens - this->params = mgparams; - for (size_t 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); - } + for (u32 i = 0; i != emergethread.size(); i++) { + Mapgen *mg = createMapgen(params.mg_name, i, ¶ms); + assert(mg); mapgen.push_back(mg); } } -Mapgen *EmergeManager::getCurrentMapgen() { - for (unsigned int i = 0; i != emergethread.size(); i++) { +Mapgen *EmergeManager::getCurrentMapgen() +{ + for (u32 i = 0; i != emergethread.size(); i++) { if (emergethread[i]->IsSameThread()) return emergethread[i]->mapgen; } - + return NULL; } -void EmergeManager::triggerAllThreads() { - for (unsigned int i = 0; i != emergethread.size(); i++) - emergethread[i]->trigger(); +void EmergeManager::startThreads() +{ + if (threads_active) + return; + + for (u32 i = 0; i != emergethread.size(); i++) + emergethread[i]->Start(); + + threads_active = true; } -bool EmergeManager::enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate) { + +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); - + count = blocks_enqueued.size(); if (count >= qlimit_total) return false; @@ -250,7 +265,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; @@ -262,39 +277,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); @@ -304,90 +321,89 @@ 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; + + MapgenFactory *mgfactory = reg_mapgens[i].factory; 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; +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; } - - MapgenFactory *mgfactory = iter->second; - return mgfactory->createMapgenParams(); -} - -MapgenParams *EmergeManager::getParamsFromSettings(Settings *settings) { - std::string mg_name = settings->get("mg_name"); - MapgenParams *mgparams = createMapgenParams(mg_name); - if (!mgparams) - return NULL; - - std::string seedstr = settings->get(settings == g_settings ? - "fixed_map_seed" : "seed"); - - 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); - - if (!mgparams->readParams(settings)) { - delete mgparams; - return NULL; - } - return mgparams; + MapgenFactory *mgfactory = reg_mapgens[i].factory; + return mgfactory->createMapgenParams(); } -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::loadParamsFromSettings(Settings *settings) +{ + std::string seed_str; + const char *setname = (settings == g_settings) ? "fixed_map_seed" : "seed"; + + if (settings->getNoEx(setname, seed_str)) + params.seed = read_seed(seed_str.c_str()); + + 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); + settings->getNoiseParams("mg_biome_np_heat", params.np_biome_heat); + settings->getNoiseParams("mg_biome_np_humidity", params.np_biome_humidity); + + delete params.sparams; + params.sparams = createMapgenParams(params.mg_name); + if (params.sparams) + params.sparams->readParams(settings); } -void EmergeManager::registerMapgen(std::string mgname, MapgenFactory *mgfactory) { - mglist.insert(std::make_pair(mgname, mgfactory)); - infostream << "EmergeManager: registered mapgen " << mgname << std::endl; +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, (u32)-1); + settings->setNoiseParams("mg_biome_np_heat", params.np_biome_heat); + settings->setNoiseParams("mg_biome_np_humidity", params.np_biome_humidity); + + if (params.sparams) + params.sparams->writeParams(settings); } -////////////////////////////// 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); @@ -395,31 +411,32 @@ bool EmergeThread::popBlockEmerge(v3s16 *pos, u8 *flags) { 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); - + JMutexAutoLock envlock(m_server->m_env_mutex); + // Load sector if it isn't loaded if (map->getSectorNoGenerateNoEx(p2d) == NULL) map->loadSectorMeta(p2d); @@ -440,13 +457,14 @@ bool EmergeThread::getBlockOrStartGen(v3s16 p, MapBlock **b, *b = block; return map->initBlockMake(data, p); } - + *b = block; return false; } -void *EmergeThread::Thread() { +void *EmergeThread::Thread() +{ ThreadStarted(); log_register_thread("EmergeThread" + itos(id)); DSTACK(__FUNCTION_NAME); @@ -454,14 +472,16 @@ void *EmergeThread::Thread() { 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()) + + porting::setThreadName("EmergeThread"); + + while (!StopRequested()) try { if (!popBlockEmerge(&p, &flags)) { qevent.wait(); @@ -474,7 +494,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. @@ -482,8 +502,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()"); @@ -496,12 +516,12 @@ void *EmergeThread::Thread() { { //envlock: usually 0ms, but can take either 30 or 400ms to acquire - JMutexAutoLock envlock(m_server->m_env_mutex); + 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) { /* @@ -513,16 +533,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 + try { // takes about 90ms with -O1 on an e3-1230v2 m_server->getScriptIface()->environment_OnGenerated( - minp, maxp, emerge->getBlockSeed(minp)); + minp, maxp, mapgen->blockseed); + } catch(LuaError &e) { + m_server->setAsyncFatalError(e.what()); } EMERGE_DBG_OUT("ended up with: " << analyze_block(block)); - + m_server->m_env->activateBlock(block, 0); } } @@ -531,23 +553,12 @@ 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) { @@ -568,7 +579,7 @@ void *EmergeThread::Thread() { err << "You can ignore this using [ignore_world_load_errors = true]."<setAsyncFatalError(err.str()); } - + END_DEBUG_EXCEPTION_HANDLER(errorstream) log_deregister_thread(); return NULL;