]> git.lizzy.rs Git - minetest.git/blobdiff - src/emerge.cpp
Make MapEditEvent more complete
[minetest.git] / src / emerge.cpp
index f634e9114827daf3ca0c1444084ba636719386d1..34c13cbd5200b17973c45efc921099d45eeae3f7 100644 (file)
@@ -34,15 +34,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "map.h"
 #include "mapblock.h"
-#include "mg_biome.h"
-#include "mg_ore.h"
-#include "mg_decoration.h"
-#include "mg_schematic.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 "voxel.h"
 
@@ -62,7 +61,9 @@ class EmergeThread : public Thread {
 
        void cancelPendingItems();
 
-       static void runCompletionCallbacks(
+protected:
+
+       void runCompletionCallbacks(
                const v3s16 &pos, EmergeAction action,
                const EmergeCallbackList &callbacks);
 
@@ -110,11 +111,36 @@ class MapEditEventAreaIgnorer
        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;
+}
+
+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::EmergeManager(Server *server)
+EmergeManager::EmergeManager(Server *server, MetricsBackend *mb)
 {
        this->ndef      = server->getNodeDefManager();
        this->biomemgr  = new BiomeManager(server);
@@ -122,33 +148,47 @@ EmergeManager::EmergeManager(Server *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.
 
        enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
 
-       // If unspecified, leave a proc for the main thread and one for
+       STATIC_ASSERT(ARRLEN(emergeActionStrs) == ARRLEN(m_completed_emerge_counter),
+               enum_size_mismatches);
+       for (u32 i = 0; i < ARRLEN(m_completed_emerge_counter); i++) {
+               std::string help_str("Number of completed emerges with status ");
+               help_str.append(emergeActionStrs[i]);
+               m_completed_emerge_counter[i] = mb->addCounter(
+                       "minetest_emerge_completed", help_str,
+                       {{"status", emergeActionStrs[i]}}
+               );
+       }
+
+       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
-       s16 nthreads = 0;
-       if (!g_settings->getS16NoEx("num_emerge_threads", nthreads))
+       if (nthreads <= 0)
                nthreads = Thread::getNumberOfProcessors() - 2;
        if (nthreads < 1)
                nthreads = 1;
 
-       m_qlimit_total = g_settings->getU16("emergequeue_limit_total");
-       if (!g_settings->getU16NoEx("emergequeue_limit_diskonly", m_qlimit_diskonly))
+       m_qlimit_total = g_settings->getU32("emergequeue_limit_total");
+       // FIXME: these fallback values are probably not good
+       if (!g_settings->getU32NoEx("emergequeue_limit_diskonly", m_qlimit_diskonly))
                m_qlimit_diskonly = nthreads * 5 + 1;
-       if (!g_settings->getU16NoEx("emergequeue_limit_generate", m_qlimit_generate))
+       if (!g_settings->getU32NoEx("emergequeue_limit_generate", m_qlimit_generate))
                m_qlimit_generate = nthreads + 1;
 
        // 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;
+       m_qlimit_total = rangelim(m_qlimit_total, 1, 1000000);
+       m_qlimit_diskonly = rangelim(m_qlimit_diskonly, 1, 1000000);
+       m_qlimit_generate = rangelim(m_qlimit_generate, 1, 1000000);
 
        for (s16 i = 0; i < nthreads; i++)
                m_threads.push_back(new EmergeThread(server, i));
@@ -169,9 +209,13 @@ EmergeManager::~EmergeManager()
                }
 
                delete thread;
-               delete m_mapgens[i];
+
+               // Mapgen init might not be finished if there is an error during startup.
+               if (m_mapgens.size() > i)
+                       delete m_mapgens[i];
        }
 
+       delete biomegen;
        delete biomemgr;
        delete oremgr;
        delete decomgr;
@@ -179,33 +223,66 @@ EmergeManager::~EmergeManager()
 }
 
 
-bool EmergeManager::initMapgens(MapgenParams *params)
+BiomeManager *EmergeManager::getWritableBiomeManager()
 {
-       if (!m_mapgens.empty())
-               return false;
+       FATAL_ERROR_IF(!m_mapgens.empty(),
+               "Writable managers can only be returned before mapgen init");
+       return biomemgr;
+}
 
-       this->mgparams = params;
+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;
+}
+
+SchematicManager *EmergeManager::getWritableSchematicManager()
+{
+       FATAL_ERROR_IF(!m_mapgens.empty(),
+               "Writable managers can only be returned before mapgen init");
+       return schemmgr;
+}
+
+
+void EmergeManager::initMapgens(MapgenParams *params)
+{
+       FATAL_ERROR_IF(!m_mapgens.empty(), "Mapgen already initialised.");
+
+       mgparams = params;
+
+       v3s16 csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE);
+       biomegen = biomemgr->createBiomeGen(BIOMEGEN_ORIGINAL, params->bparams, csize);
 
        for (u32 i = 0; i != m_threads.size(); i++) {
-               Mapgen *mg = Mapgen::createMapgen(params->mgtype, i, params, this);
-               m_mapgens.push_back(mg);
+               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));
        }
-
-       return true;
 }
 
 
 Mapgen *EmergeManager::getCurrentMapgen()
 {
        if (!m_threads_active)
-               return NULL;
+               return nullptr;
 
        for (u32 i = 0; i != m_threads.size(); i++) {
-               if (m_threads[i]->isCurrentThread())
-                       return m_threads[i]->m_mapgen;
+               EmergeThread *t = m_threads[i];
+               if (t->isRunning() && t->isCurrentThread())
+                       return t->m_mapgen;
        }
 
-       return NULL;
+       return nullptr;
 }
 
 
@@ -247,7 +324,7 @@ bool EmergeManager::isRunning()
 
 
 bool EmergeManager::enqueueBlockEmerge(
-       u16 peer_id,
+       session_t peer_id,
        v3s16 blockpos,
        bool allow_generate,
        bool ignore_queue_limits)
@@ -264,7 +341,7 @@ bool EmergeManager::enqueueBlockEmerge(
 
 bool EmergeManager::enqueueBlockEmergeEx(
        v3s16 blockpos,
-       u16 peer_id,
+       session_t peer_id,
        u16 flags,
        EmergeCompletionCallback callback,
        void *callback_param)
@@ -292,17 +369,18 @@ bool EmergeManager::enqueueBlockEmergeEx(
 }
 
 
+bool EmergeManager::isBlockInQueue(v3s16 pos)
+{
+       MutexAutoLock queuelock(m_queue_mutex);
+       return m_blocks_enqueued.find(pos) != m_blocks_enqueued.end();
+}
+
+
 //
 // 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)
 {
@@ -326,28 +404,10 @@ int EmergeManager::getSpawnLevelAtPoint(v2s16 p)
 }
 
 
-int EmergeManager::getGroundLevelAtPoint(v2s16 p)
-{
-       if (m_mapgens.empty() || !m_mapgens[0]) {
-               errorstream << "EmergeManager: getGroundLevelAtPoint() called"
-                       " before mapgen init" << std::endl;
-               return 0;
-       }
-
-       return m_mapgens[0]->getGroundLevelAtPoint(p);
-}
-
 // 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
-
-       // Use a simple heuristic; the above method is wildly inaccurate anyway.
+       // Use a simple heuristic
        return blockpos.Y * (MAP_BLOCKSIZE + 1) <= mgparams->water_level;
 }
 
@@ -359,17 +419,21 @@ bool EmergeManager::pushBlockEmergeData(
        void *callback_param,
        bool *entry_already_exists)
 {
-       u16 &count_peer = m_peer_queue_count[peer_requested];
+       u32 &count_peer = m_peer_queue_count[peer_requested];
 
        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) ?
+                       u32 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;
                }
        }
 
@@ -397,20 +461,18 @@ bool EmergeManager::pushBlockEmergeData(
 
 bool EmergeManager::popBlockEmergeData(v3s16 pos, BlockEmergeData *bedata)
 {
-       std::map<v3s16, BlockEmergeData>::iterator it;
-       std::unordered_map<u16, u16>::iterator it2;
-
-       it = m_blocks_enqueued.find(pos);
+       auto 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);
+       auto it2 = m_peer_queue_count.find(bedata->peer_requested);
        if (it2 == m_peer_queue_count.end())
                return false;
 
-       u16 &count_peer = it2->second;
+       u32 &count_peer = it2->second;
+
        assert(count_peer != 0);
        count_peer--;
 
@@ -440,6 +502,12 @@ EmergeThread *EmergeManager::getOptimalThread()
        return m_threads[index];
 }
 
+void EmergeManager::reportCompletedEmerge(EmergeAction action)
+{
+       assert((size_t)action < ARRLEN(m_completed_emerge_counter));
+       m_completed_emerge_counter[(int)action]->increment();
+}
+
 
 ////
 //// EmergeThread
@@ -491,6 +559,8 @@ void EmergeThread::cancelPendingItems()
 void EmergeThread::runCompletionCallbacks(const v3s16 &pos, EmergeAction action,
        const EmergeCallbackList &callbacks)
 {
+       m_emerge->reportCompletedEmerge(action);
+
        for (size_t i = 0; i != callbacks.size(); i++) {
                EmergeCompletionCallback callback;
                void *param;
@@ -526,7 +596,7 @@ EmergeAction EmergeThread::getBlockOrStartGen(
 
        // 1). Attempt to fetch block from memory
        *block = m_map->getBlockNoCreateNoEx(pos);
-       if (*block && !(*block)->isDummy()) {
+       if (*block) {
                if ((*block)->isGenerated())
                        return EMERGE_FROM_MEMORY;
        } else {
@@ -582,11 +652,18 @@ MapBlock *EmergeThread::finishGen(v3s16 pos, BlockMakeData *bmdata,
                m_server->getScriptIface()->environment_OnGenerated(
                        minp, maxp, m_mapgen->blockseed);
        } catch (LuaError &e) {
-               m_server->setAsyncFatalError("Lua: finishGen" + std::string(e.what()));
+               m_server->setAsyncFatalError(e);
        }
 
        EMERGE_DBG_OUT("ended up with: " << analyze_block(block));
 
+       /*
+               Clear mapgen state
+       */
+       assert(!m_mapgen->generating);
+       m_mapgen->gennotify.clearEvents();
+       m_mapgen->vm = nullptr;
+
        /*
                Activate the block
        */
@@ -601,19 +678,19 @@ void *EmergeThread::run()
        BEGIN_DEBUG_EXCEPTION_HANDLER
 
        v3s16 pos;
+       std::map<v3s16, MapBlock *> modified_blocks;
 
-       m_map    = (ServerMap *)&(m_server->m_env->getMap());
+       m_map    = &m_server->m_env->getServerMap();
        m_emerge = m_server->m_emerge;
        m_mapgen = m_emerge->m_mapgens[id];
        enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
 
        try {
        while (!stopRequested()) {
-               std::map<v3s16, MapBlock *> modified_blocks;
                BlockEmergeData bedata;
                BlockMakeData bmdata;
                EmergeAction action;
-               MapBlock *block;
+               MapBlock *block = nullptr;
 
                if (!popBlockEmerge(&pos, &bedata)) {
                        m_queue_event.wait();
@@ -631,15 +708,13 @@ void *EmergeThread::run()
                        {
                                ScopeProfiler sp(g_profiler,
                                        "EmergeThread: Mapgen::makeChunk", SPT_AVG);
-                               TimeTaker t("mapgen::make_block()");
 
                                m_mapgen->makeChunk(&bmdata);
-
-                               if (!enable_mapgen_debug_info)
-                                       t.stop(true); // Hide output
                        }
 
                        block = finishGen(pos, &bmdata, &modified_blocks);
+                       if (!block)
+                               action = EMERGE_ERRORED;
                }
 
                runCompletionCallbacks(pos, action, bedata.callbacks);
@@ -647,8 +722,16 @@ void *EmergeThread::run()
                if (block)
                        modified_blocks[pos] = block;
 
-               if (!modified_blocks.empty())
-                       m_server->SetBlocksNotSent(modified_blocks);
+               if (!modified_blocks.empty()) {
+                       MapEditEvent event;
+                       event.type = MEET_OTHER;
+                       for (const auto &pair : modified_blocks) {
+                               event.modified_blocks.insert(pair.first);
+                       }
+                       MutexAutoLock envlock(m_server->m_env_mutex);
+                       m_map->dispatchEvent(event);
+               }
+               modified_blocks.clear();
        }
        } catch (VersionMismatchException &e) {
                std::ostringstream err;
@@ -670,6 +753,8 @@ void *EmergeThread::run()
                m_server->setAsyncFatalError(err.str());
        }
 
+       cancelPendingItems();
+
        END_DEBUG_EXCEPTION_HANDLER
        return NULL;
 }