#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 "serverscripting.h"
+#include "scripting_server.h"
#include "server.h"
-#include "serverobject.h"
#include "settings.h"
#include "voxel.h"
int id;
EmergeThread(Server *server, int ethreadid);
- ~EmergeThread();
+ ~EmergeThread() = default;
void *run();
void signal();
// Requires queue mutex held
- bool pushBlock(v3s16 pos);
+ bool pushBlock(const v3s16 &pos);
void cancelPendingItems();
- static void runCompletionCallbacks(
- v3s16 pos, EmergeAction action,
+protected:
+
+ void runCompletionCallbacks(
+ const v3s16 &pos, EmergeAction action,
const EmergeCallbackList &callbacks);
private:
bool popBlockEmerge(v3s16 *pos, BlockEmergeData *bedata);
EmergeAction getBlockOrStartGen(
- v3s16 pos, bool allow_gen, MapBlock **block, BlockMakeData *data);
+ const v3s16 &pos, bool allow_gen, MapBlock **block, BlockMakeData *data);
MapBlock *finishGen(v3s16 pos, BlockMakeData *bmdata,
std::map<v3s16, MapBlock *> *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;
+ }
+
+ ~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;
+}
+
+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);
this->oremgr = new OreManager(server);
this->decomgr = new DecorationManager(server);
this->schemmgr = new SchematicManager(server);
- this->gen_notify_on = 0;
+
+ // 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->m_threads_active = false;
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));
}
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;
}
-bool EmergeManager::initMapgens(MapgenParams *params)
+BiomeManager *EmergeManager::getWritableBiomeManager()
{
- if (m_mapgens.size())
- return false;
+ FATAL_ERROR_IF(!m_mapgens.empty(),
+ "Writable managers can only be returned before mapgen init");
+ return biomemgr;
+}
+
+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;
+}
- this->mgparams = params;
+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;
}
bool EmergeManager::enqueueBlockEmerge(
- u16 peer_id,
+ session_t peer_id,
v3s16 blockpos,
bool allow_generate,
bool ignore_queue_limits)
bool EmergeManager::enqueueBlockEmergeEx(
v3s16 blockpos,
- u16 peer_id,
+ session_t peer_id,
u16 flags,
EmergeCompletionCallback callback,
void *callback_param)
}
+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)
{
int EmergeManager::getSpawnLevelAtPoint(v2s16 p)
{
- if (m_mapgens.size() == 0 || !m_mapgens[0]) {
+ if (m_mapgens.empty() || !m_mapgens[0]) {
errorstream << "EmergeManager: getSpawnLevelAtPoint() called"
" before mapgen init" << std::endl;
return 0;
}
-int EmergeManager::getGroundLevelAtPoint(v2s16 p)
-{
- if (m_mapgens.size() == 0 || !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;
}
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;
}
}
*entry_already_exists = !findres.second;
if (callback)
- bedata.callbacks.push_back(std::make_pair(callback, callback_param));
+ bedata.callbacks.emplace_back(callback, callback_param);
if (*entry_already_exists) {
bedata.flags |= flags;
bool EmergeManager::popBlockEmergeData(v3s16 pos, BlockEmergeData *bedata)
{
- std::map<v3s16, BlockEmergeData>::iterator it;
- 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--;
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
}
-EmergeThread::~EmergeThread()
-{
- //cancelPendingItems();
-}
-
-
void EmergeThread::signal()
{
m_queue_event.signal();
}
-bool EmergeThread::pushBlock(v3s16 pos)
+bool EmergeThread::pushBlock(const v3s16 &pos)
{
m_block_queue.push(pos);
return true;
}
-void EmergeThread::runCompletionCallbacks(
- v3s16 pos,
- EmergeAction action,
+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;
EmergeAction EmergeThread::getBlockOrStartGen(
- v3s16 pos, bool allow_gen, MapBlock **block, BlockMakeData *bmdata)
+ 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) {
if ((*block)->isGenerated())
return EMERGE_FROM_MEMORY;
} else {
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
*/
void *EmergeThread::run()
{
- DSTACK(FUNCTION_NAME);
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();
{
ScopeProfiler sp(g_profiler,
"EmergeThread: Mapgen::makeChunk", SPT_AVG);
- TimeTaker t("mapgen::make_block()");
m_mapgen->makeChunk(&bmdata);
-
- if (enable_mapgen_debug_info == false)
- t.stop(true); // Hide output
}
block = finishGen(pos, &bmdata, &modified_blocks);
+ if (!block)
+ action = EMERGE_ERRORED;
}
runCompletionCallbacks(pos, action, bedata.callbacks);
if (block)
modified_blocks[pos] = block;
- if (modified_blocks.size() > 0)
+ if (!modified_blocks.empty())
m_server->SetBlocksNotSent(modified_blocks);
+ modified_blocks.clear();
}
} catch (VersionMismatchException &e) {
std::ostringstream err;
m_server->setAsyncFatalError(err.str());
}
+ cancelPendingItems();
+
END_DEBUG_EXCEPTION_HANDLER
return NULL;
}