diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/emerge.cpp | 677 | ||||
-rw-r--r-- | src/emerge.h | 122 | ||||
-rw-r--r-- | src/environment.h | 1 | ||||
-rw-r--r-- | src/jthread/jmutex.h | 50 | ||||
-rw-r--r-- | src/map.cpp | 1 | ||||
-rw-r--r-- | src/map.h | 4 | ||||
-rw-r--r-- | src/mapgen.cpp | 153 | ||||
-rw-r--r-- | src/mapgen.h | 52 | ||||
-rw-r--r-- | src/mapgen_v6.cpp | 1 | ||||
-rw-r--r-- | src/server.cpp | 345 | ||||
-rw-r--r-- | src/server.h | 39 |
12 files changed, 886 insertions, 560 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c06da20c4..d2f080c90 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -209,6 +209,7 @@ set(common_SRCS script.cpp log.cpp content_sao.cpp + emerge.cpp mapgen.cpp mapgen_v6.cpp treegen.cpp diff --git a/src/emerge.cpp b/src/emerge.cpp new file mode 100644 index 000000000..728ea7196 --- /dev/null +++ b/src/emerge.cpp @@ -0,0 +1,677 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#include "server.h" +#include <iostream> +#include <queue> +#include "clientserver.h" +#include "map.h" +#include "jmutexautolock.h" +#include "main.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 "profiler.h" +#include "log.h" +#include "nodedef.h" +#include "biome.h" +#include "emerge.h" +#include "mapgen_v6.h" + + +EmergeManager::EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef) { + //register built-in mapgens + registerMapgen("v6", new MapgenFactoryV6()); + + this->biomedef = bdef ? bdef : new BiomeDefManager(gamedef); + this->params = NULL; + this->mapgen = NULL; + + queuemutex.Init(); + emergethread = new EmergeThread((Server *)gamedef); +} + + +EmergeManager::~EmergeManager() { + emergethread->setRun(false); + emergethread->stop(); + + delete emergethread; + delete biomedef; + delete mapgen; + delete params; +} + + +void EmergeManager::initMapgens(MapgenParams *mgparams) { + if (mapgen) + return; + + this->params = mgparams; + this->mapgen = getMapgen(); //only one mapgen for now! +} + + +Mapgen *EmergeManager::getMapgen() { + if (!mapgen) { + mapgen = createMapgen(params->mg_name, 0, params, this); + if (!mapgen) { + infostream << "EmergeManager: falling back to mapgen v6" << std::endl; + delete params; + params = createMapgenParams("v6"); + mapgen = createMapgen("v6", 0, params, this); + } + } + return mapgen; +} + + +bool EmergeManager::enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate) { ///return false if adding failed, or queue full! + u8 flags = 0; + + if (allow_generate) + flags |= BLOCK_EMERGE_ALLOWGEN; + + //TODO: + // add logic to select which emergethread to add it to + // - one with the least queue contents? + // - if a queue is too full, move onto another one + // - use the peer id sometime + + { + JMutexAutoLock queuelock(queuemutex); + + std::map<v3s16, u8>::const_iterator iter = blocks_enqueued.find(p); + if (iter != blocks_enqueued.end()) { + flags |= iter->second; + blocks_enqueued[p] = flags; + return true; + } + + blocks_enqueued.insert(std::make_pair(p, flags)); + emergethread->blockqueue.push(p); + } + emergethread->qevent.signal(); + + return true; +} + + +bool EmergeManager::popBlockEmerge(v3s16 *pos, u8 *flags) { + JMutexAutoLock queuelock(queuemutex); + + if (emergethread->blockqueue.empty()) + return false; + v3s16 p = emergethread->blockqueue.front(); + emergethread->blockqueue.pop(); + + *pos = p; + + std::map<v3s16, u8>::iterator iter = blocks_enqueued.find(p); + if (iter == blocks_enqueued.end()) //uh oh, this isn't right!!!!!!!!!!!!!!!!!! + return false; + + *flags = iter->second; + blocks_enqueued.erase(iter); + + return true; +} + + +int EmergeManager::getGroundLevelAtPoint(v2s16 p) { + if (!mapgen) + return 0; + return mapgen->getGroundLevelAtPoint(p); +} + + +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); + */ + + //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; +} + + +u32 EmergeManager::getBlockSeed(v3s16 p) { + return (u32)(params->seed & 0xFFFFFFFF) + + p.Z * 38134234 + + p.Y * 42123 + + p.Y * 23; +} + + +Mapgen *EmergeManager::createMapgen(std::string mgname, int mgid, + MapgenParams *mgparams, EmergeManager *emerge) { + std::map<std::string, MapgenFactory *>::const_iterator iter = mglist.find(mgname); + if (iter == mglist.end()) { + errorstream << "EmergeManager; mapgen " << mgname << + " not registered" << std::endl; + return NULL; + } + + MapgenFactory *mgfactory = iter->second; + return mgfactory->createMapgen(mgid, mgparams, emerge); +} + + +MapgenParams *EmergeManager::createMapgenParams(std::string mgname) { + std::map<std::string, MapgenFactory *>::const_iterator 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(); +} + + +MapgenParams *EmergeManager::getParamsFromSettings(Settings *settings) { + std::string mg_name = settings->get("mg_name"); + MapgenParams *mgparams = createMapgenParams(mg_name); + + 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->getS32("mg_flags"); + + if (!mgparams->readParams(settings)) { + delete mgparams; + 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); +} + + +bool EmergeManager::registerMapgen(std::string mgname, MapgenFactory *mgfactory) { + mglist.insert(std::make_pair(mgname, mgfactory)); + infostream << "EmergeManager: registered mapgen " << mgname << std::endl; +} + + + +class MapEditEventIgnorer +{ +public: + MapEditEventIgnorer(bool *flag): + m_flag(flag) + { + if(*m_flag == false) + *m_flag = true; + else + m_flag = NULL; + } + + ~MapEditEventIgnorer() + { + if(m_flag) + { + assert(*m_flag); + *m_flag = false; + } + } + +private: + bool *m_flag; +}; + +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; +}; + + +#if 1 + +#define EMERGE_DBG_OUT(x) \ + { if (enable_mapgen_debug_info) \ + infostream << "EmergeThread: " x << std::endl; } + +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); + + // 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 could not load and allowed to generate, + // start generation inside this same envlock + if (allow_gen && (block == NULL || !block->isGenerated())) { + EMERGE_DBG_OUT("generating"); + map->initBlockMake(data, p); + return true; + } + + *b = block; + return false; +} + + +void *EmergeThread::Thread() { + ThreadStarted(); + log_register_thread("EmergeThread"); + DSTACK(__FUNCTION_NAME); + BEGIN_DEBUG_EXCEPTION_HANDLER + + v3s16 last_tried_pos(-32768,-32768,-32768); // For error output + v3s16 p; + u8 flags; + + map = (ServerMap *)&(m_server->m_env->getMap()); + emerge = m_server->m_emerge; + mapgen = emerge->getMapgen(); + + while (getRun()) + try { + while (!emerge->popBlockEmerge(&p, &flags)) + qevent.wait(); + + last_tried_pos = p; + if (blockpos_over_limit(p)) + continue; + + 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. + */ + BlockMakeData data; + MapBlock *block = NULL; + core::map<v3s16, MapBlock *> modified_blocks; + + if (getBlockOrStartGen(p, &block, &data, allow_generate)) { + { + ScopeProfiler sp(g_profiler, "EmergeThread: Mapgen::makeChunk", SPT_AVG); + TimeTaker t("mapgen::make_block()"); + + mapgen->makeChunk(&data); + + if (enable_mapgen_debug_info == false) + t.stop(true); // Hide output + } + + { + //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::make_block (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)); + { // takes about 90ms with -O1 on an e3-1230v2 + scriptapi_environment_on_generated(m_server->m_lua, + minp, maxp, emerge->getBlockSeed(minp)); + } + + EMERGE_DBG_OUT("ended up with: " << analyze_block(block)); + + m_server->m_env->activateBlock(block, 0); + } + } + } + + /* + 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.insert(p, block); + + // Set the modified blocks unsent for all the clients + for (core::map<u16, RemoteClient*>::Iterator + i = m_server->m_clients.getIterator(); + i.atEnd() == false; i++) { + RemoteClient *client = i.getNode()->getValue(); + if (modified_blocks.size() > 0) { + // Remove block from sent history + client->SetBlocksNotSent(modified_blocks); + } + } + } + catch (VersionMismatchException &e) { + std::ostringstream err; + err << "World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl; + err << "----"<<std::endl; + err << "\""<<e.what()<<"\""<<std::endl; + err << "See debug.txt."<<std::endl; + err << "World probably saved by a newer version of Minetest."<<std::endl; + m_server->setAsyncFatalError(err.str()); + } + catch (SerializationError &e) { + std::ostringstream err; + err << "Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl; + err << "----"<<std::endl; + err << "\""<<e.what()<<"\""<<std::endl; + err << "See debug.txt."<<std::endl; + err << "You can ignore this using [ignore_world_load_errors = true]."<<std::endl; + m_server->setAsyncFatalError(err.str()); + } + + END_DEBUG_EXCEPTION_HANDLER(errorstream) + log_deregister_thread(); + return NULL; +} + +#else + +void *EmergeThread::Thread() { + ThreadStarted(); + log_register_thread("EmergeThread"); + DSTACK(__FUNCTION_NAME); + BEGIN_DEBUG_EXCEPTION_HANDLER + + bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info"); + + v3s16 last_tried_pos(-32768,-32768,-32768); // For error output + ServerMap &map = ((ServerMap&)m_server->m_env->getMap()); + EmergeManager *emerge = m_server->m_emerge; + Mapgen *mapgen = emerge->getMapgen(); + + while(getRun()) + try { + QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop(); + if(qptr == NULL) + break; + SharedPtr<QueuedBlockEmerge> q(qptr); + + v3s16 &p = q->pos; + v2s16 p2d(p.X,p.Z); + + last_tried_pos = p; + + /* + Do not generate over-limit + */ + if (blockpos_over_limit(p)) + continue; + + //infostream<<"EmergeThread::Thread(): running"<<std::endl; + + //TimeTaker timer("block emerge"); + + /* + Try to emerge it from somewhere. + + If it is only wanted as optional, only loading from disk + will be allowed. + */ + + /* + Check if any peer wants it as non-optional. In that case it + will be generated. + + Also decrement the emerge queue count in clients. + */ + + bool only_from_disk = true; + { + core::map<u16, u8>::Iterator i; + for (i=q->s.getIterator(); !i.atEnd(); i++) { + u8 flags = i.getNode()->getValue(); + if (!(flags & BLOCK_EMERGE_FLAG_FROMDISK)) { + only_from_disk = false; + break; + } + } + } + + if (enable_mapgen_debug_info) + infostream<<"EmergeThread: p=" + <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") " + <<"only_from_disk="<<only_from_disk<<std::endl; + + MapBlock *block = NULL; + bool got_block = true; + core::map<v3s16, MapBlock*> modified_blocks; + + /* + Try to fetch block from memory or disk. + If not found and asked to generate, initialize generator. + */ + + bool started_generate = false; + BlockMakeData data; + { + JMutexAutoLock envlock(m_server->m_env_mutex); + + // Load sector if it isn't loaded + if(map.getSectorNoGenerateNoEx(p2d) == NULL) + map.loadSectorMeta(p2d); + + // Attempt to load block + block = map.getBlockNoCreateNoEx(p); + if(!block || block->isDummy() || !block->isGenerated()) { + if(enable_mapgen_debug_info) + infostream<<"EmergeThread: not in memory, " + <<"attempting to load from disk"<<std::endl; + + block = map.loadBlock(p); + } + + // If could not load and allowed to generate, start generation + // inside this same envlock + if(only_from_disk == false && + (block == NULL || block->isGenerated() == false)){ + if(enable_mapgen_debug_info) + infostream<<"EmergeThread: generating"<<std::endl; + started_generate = true; + + map.initBlockMake(&data, p); + } + } + + /* + If generator was initialized, generate now when envlock is free. + */ + if(started_generate) { + { + ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block", + SPT_AVG); + TimeTaker t("mapgen::make_block()"); + + mapgen->makeChunk(&data); + + if (enable_mapgen_debug_info == false) + t.stop(true); // Hide output + } + + do{ // enable break + // Lock environment again to access the map + JMutexAutoLock envlock(m_server->m_env_mutex); + + ScopeProfiler sp(g_profiler, "EmergeThread: after " + "mapgen::make_block (envlock)", SPT_AVG); + + // Blit data back on map, update lighting, add mobs and + // whatever this does + map.finishBlockMake(&data, modified_blocks); + + // Get central block + block = map.getBlockNoCreateNoEx(p); + + // If block doesn't exist, don't try doing anything with it + // This happens if the block is not in generation boundaries + if(!block) + break; + + /* + 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)); + { + TimeTaker timer("on_generated"); + scriptapi_environment_on_generated(m_server->m_lua, + minp, maxp, emerge->getBlockSeed(minp)); + //int t = timer.stop(true); + //dstream<<"on_generated took "<<t<<"ms"<<std::endl; + } + + if (enable_mapgen_debug_info) + infostream << "EmergeThread: ended up with: " + << analyze_block(block) << std::endl; + + // Activate objects and stuff + m_server->m_env->activateBlock(block, 0); + }while(false); + } + + if(block == NULL) + got_block = false; + + /* + Set sent status of modified blocks on clients + */ + + // NOTE: Server's clients are also behind the connection mutex + JMutexAutoLock lock(m_server->m_con_mutex); + + /* + Add the originally fetched block to the modified list + */ + if(got_block) + modified_blocks.insert(p, block); + + /* + Set the modified blocks unsent for all the clients + */ + for(core::map<u16, RemoteClient*>::Iterator + i = m_server->m_clients.getIterator(); + i.atEnd() == false; i++) { + RemoteClient *client = i.getNode()->getValue(); + if(modified_blocks.size() > 0) { + // Remove block from sent history + client->SetBlocksNotSent(modified_blocks); + } + } + + +niters++; + } + catch (VersionMismatchException &e) { + std::ostringstream err; + err << "World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl; + err << "----"<<std::endl; + err << "\""<<e.what()<<"\""<<std::endl; + err << "See debug.txt."<<std::endl; + err << "World probably saved by a newer version of Minetest."<<std::endl; + m_server->setAsyncFatalError(err.str()); + } + catch (SerializationError &e) { + std::ostringstream err; + err << "Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl; + err << "----"<<std::endl; + err << "\""<<e.what()<<"\""<<std::endl; + err << "See debug.txt."<<std::endl; + err << "You can ignore this using [ignore_world_load_errors = true]."<<std::endl; + m_server->setAsyncFatalError(err.str()); + } +printf("emergethread iterated %d times\n", niters); + END_DEBUG_EXCEPTION_HANDLER(errorstream) + log_deregister_thread(); + return NULL; +} + +#endif diff --git a/src/emerge.h b/src/emerge.h new file mode 100644 index 000000000..0acc89a6d --- /dev/null +++ b/src/emerge.h @@ -0,0 +1,122 @@ +#ifndef EMERGE_HEADER +#define EMERGE_HEADER + +#include <map> +#include <queue> +#include "util/thread.h" + +#define BLOCK_EMERGE_ALLOWGEN (1<<0) + +class Mapgen; +class MapgenParams; +class MapgenFactory; +class Biome; +class BiomeDefManager; +class EmergeThread; +class ManualMapVoxelManipulator; +//class ServerMap; +//class MapBlock; + +#include "server.h" + +struct BlockMakeData { + bool no_op; + ManualMapVoxelManipulator *vmanip; + u64 seed; + v3s16 blockpos_min; + v3s16 blockpos_max; + v3s16 blockpos_requested; + UniqueQueue<v3s16> transforming_liquid; + INodeDefManager *nodedef; + +// BlockMakeData(); +// ~BlockMakeData(); + +BlockMakeData(): + no_op(false), + vmanip(NULL), + seed(0), + nodedef(NULL) +{} + +~BlockMakeData() +{ + delete vmanip; +} +}; + +class EmergeManager { +public: + std::map<std::string, MapgenFactory *> mglist; + + //settings + MapgenParams *params; + + JMutex queuemutex; + std::map<v3s16, u8> blocks_enqueued; //change to a hashtable later + Mapgen *mapgen; + EmergeThread *emergethread; + + //biome manager + BiomeDefManager *biomedef; + + EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef); + ~EmergeManager(); + + void initMapgens(MapgenParams *mgparams); + Mapgen *createMapgen(std::string mgname, int mgid, + MapgenParams *mgparams, EmergeManager *emerge); + MapgenParams *createMapgenParams(std::string mgname); + Mapgen *getMapgen(); + bool enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate); + bool popBlockEmerge(v3s16 *pos, u8 *flags); + + bool registerMapgen(std::string name, MapgenFactory *mgfactory); + MapgenParams *getParamsFromSettings(Settings *settings); + void setParamsToSettings(Settings *settings); + + //mapgen helper methods + Biome *getBiomeAtPoint(v3s16 p); + int getGroundLevelAtPoint(v2s16 p); + bool isBlockUnderground(v3s16 blockpos); + u32 getBlockSeed(v3s16 p); +}; + +class EmergeThread : public SimpleThread +{ + Server *m_server; + ServerMap *map; + EmergeManager *emerge; + Mapgen *mapgen; + bool enable_mapgen_debug_info; + +public: + Event qevent; + std::queue<v3s16> blockqueue; + + EmergeThread(Server *server): + SimpleThread(), + m_server(server), + map(NULL), + emerge(NULL), + mapgen(NULL) + { + enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info"); + } + + void *Thread(); + + void trigger() + { + setRun(true); + if(IsRunning() == false) + { + Start(); + } + } + + bool getBlockOrStartGen(v3s16 p, MapBlock **b, + BlockMakeData *data, bool allow_generate); +}; + +#endif diff --git a/src/environment.h b/src/environment.h index a79ccc63d..07a4d7635 100644 --- a/src/environment.h +++ b/src/environment.h @@ -40,7 +40,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapnode.h" #include "mapblock.h" -class Server; class ServerEnvironment; class ActiveBlockModifier; class ServerActiveObject; diff --git a/src/jthread/jmutex.h b/src/jthread/jmutex.h index 9ce013096..6675162a5 100644 --- a/src/jthread/jmutex.h +++ b/src/jthread/jmutex.h @@ -67,4 +67,54 @@ private: bool initialized; }; +#ifdef _WIN32 + +class Event { + HANDLE hEvent; + +public: + Event() { + hEvent = CreateEvent(NULL, 0, 0, NULL); + } + + ~Event() { + CloseHandle(hEvent); + } + + void wait() { + WaitForSingleObject(hEvent, INFINITE); + } + + void signal() { + SetEvent(hEvent); + } +} + +#else + +#include <semaphore.h> + +class Event { + sem_t sem; + +public: + Event() { + sem_init(&sem, 0, 0); + } + + ~Event() { + sem_destroy(&sem); + } + + void wait() { + sem_wait(&sem); + } + + void signal() { + sem_post(&sem); + } +}; + +#endif + #endif // JMUTEX_H diff --git a/src/map.cpp b/src/map.cpp index 696d73182..7eb45463f 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gamedef.h" #include "util/directiontables.h" #include "rollback_interface.h" +#include "emerge.h" #include "mapgen_v6.h" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" @@ -46,6 +46,8 @@ class MapBlock; class NodeMetadata; class IGameDef; class IRollbackReportSink; +class EmergeManager; +class BlockMakeData; /* @@ -378,7 +380,7 @@ public: Blocks are generated by using these and makeBlock(). */ void initBlockMake(BlockMakeData *data, v3s16 blockpos); - MapBlock* finishBlockMake(BlockMakeData *data, + MapBlock *finishBlockMake(BlockMakeData *data, core::map<v3s16, MapBlock*> &changed_blocks); // A non-threaded wrapper to the above - DEFUNCT diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 73fe63318..ef5da6bf1 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -46,144 +46,6 @@ FlagDesc flagdesc_mapgen[] = { }; /////////////////////////////////////////////////////////////////////////////// -/////////////////////////////// Emerge Manager //////////////////////////////// - - -EmergeManager::EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef) { - //register built-in mapgens - registerMapgen("v6", new MapgenFactoryV6()); - - //the order of these assignments is pretty important - this->biomedef = bdef ? bdef : new BiomeDefManager(gamedef); - this->params = NULL; - this->mapgen = NULL; -} - - -EmergeManager::~EmergeManager() { - delete biomedef; - delete mapgen; - delete params; -} - - -void EmergeManager::initMapgens(MapgenParams *mgparams) { - if (mapgen) - return; - - this->params = mgparams; - this->mapgen = getMapgen(); //only one mapgen for now! -} - - -Mapgen *EmergeManager::getMapgen() { - if (!mapgen) { - mapgen = createMapgen(params->mg_name, 0, params, this); - if (!mapgen) { - infostream << "EmergeManager: falling back to mapgen v6" << std::endl; - delete params; - params = createMapgenParams("v6"); - mapgen = createMapgen("v6", 0, params, this); - } - } - return mapgen; -} - -void EmergeManager::addBlockToQueue() { - //STUB -} - - -int EmergeManager::getGroundLevelAtPoint(v2s16 p) { - if (!mapgen) - return 0; - return mapgen->getGroundLevelAtPoint(p); -} - - -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); - */ - - //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; -} - - -u32 EmergeManager::getBlockSeed(v3s16 p) { - return (u32)(params->seed & 0xFFFFFFFF) + - p.Z * 38134234 + - p.Y * 42123 + - p.Y * 23; -} - - -Mapgen *EmergeManager::createMapgen(std::string mgname, int mgid, - MapgenParams *mgparams, EmergeManager *emerge) { - std::map<std::string, MapgenFactory *>::const_iterator iter = mglist.find(mgname); - if (iter == mglist.end()) { - errorstream << "EmergeManager; mapgen " << mgname << - " not registered" << std::endl; - return NULL; - } - - MapgenFactory *mgfactory = iter->second; - return mgfactory->createMapgen(mgid, mgparams, emerge); -} - - -MapgenParams *EmergeManager::createMapgenParams(std::string mgname) { - std::map<std::string, MapgenFactory *>::const_iterator 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(); -} - - -MapgenParams *EmergeManager::getParamsFromSettings(Settings *settings) { - std::string mg_name = settings->get("mg_name"); - MapgenParams *mgparams = createMapgenParams(mg_name); - - 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; - 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; -} - ///////////////////// @@ -2986,18 +2848,3 @@ void make_block(BlockMakeData *data) #endif ///BIG COMMENT -BlockMakeData::BlockMakeData(): - no_op(false), - vmanip(NULL), - seed(0), - nodedef(NULL) -{} - -BlockMakeData::~BlockMakeData() -{ - delete vmanip; -} - -//}; // namespace mapgen - - diff --git a/src/mapgen.h b/src/mapgen.h index 765ac3bb2..c3c209ad1 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapnode.h" #include "noise.h" #include "settings.h" +//#include "emerge.h" #include <map> /////////////////// Mapgen flags @@ -36,6 +37,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MGV6_BIOME_BLEND 0x10 #define MG_FLAT 0x20 +extern FlagDesc flagdesc_mapgen[]; + class BiomeDefManager; class Biome; class EmergeManager; @@ -43,20 +46,7 @@ class MapBlock; class ManualMapVoxelManipulator; class VoxelManipulator; class INodeDefManager; - -struct BlockMakeData { - bool no_op; - ManualMapVoxelManipulator *vmanip; - u64 seed; - v3s16 blockpos_min; - v3s16 blockpos_max; - v3s16 blockpos_requested; - UniqueQueue<v3s16> transforming_liquid; - INodeDefManager *nodedef; - - BlockMakeData(); - ~BlockMakeData(); -}; +class BlockMakeData; struct MapgenParams { std::string mg_name; @@ -99,39 +89,5 @@ struct MapgenFactory { virtual MapgenParams *createMapgenParams() = 0; }; -class EmergeManager { -public: - std::map<std::string, MapgenFactory *> mglist; - - //settings - MapgenParams *params; - - //mapgen objects here - Mapgen *mapgen; - - //biome manager - BiomeDefManager *biomedef; - - EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef); - ~EmergeManager(); - - void initMapgens(MapgenParams *mgparams); - Mapgen *createMapgen(std::string mgname, int mgid, - MapgenParams *mgparams, EmergeManager *emerge); - MapgenParams *createMapgenParams(std::string mgname); - Mapgen *getMapgen(); - void addBlockToQueue(); - - void registerMapgen(std::string name, MapgenFactory *mgfactory); - MapgenParams *getParamsFromSettings(Settings *settings); - void setParamsToSettings(Settings *settings); - - //mapgen helper methods - Biome *getBiomeAtPoint(v3s16 p); - int getGroundLevelAtPoint(v2s16 p); - bool isBlockUnderground(v3s16 blockpos); - u32 getBlockSeed(v3s16 p); -}; - #endif diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index dd4452928..ef2cf5f52 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "profiler.h" #include "settings.h" // For g_settings #include "main.h" // For g_profiler +#include "emerge.h" #include "mapgen_v6.h" /////////////////// Mapgen V6 perlin noise default values diff --git a/src/server.cpp b/src/server.cpp index 686a3fea1..f2897d46d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "itemdef.h" #include "craftdef.h" +#include "emerge.h" #include "mapgen.h" #include "biome.h" #include "content_mapnode.h" @@ -58,60 +59,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "rollback.h" #include "util/serialize.h" -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - -#define BLOCK_EMERGE_FLAG_FROMDISK (1<<0) - -class MapEditEventIgnorer -{ -public: - MapEditEventIgnorer(bool *flag): - m_flag(flag) - { - if(*m_flag == false) - *m_flag = true; - else - m_flag = NULL; - } - - ~MapEditEventIgnorer() - { - if(m_flag) - { - assert(*m_flag); - *m_flag = false; - } - } - -private: - bool *m_flag; -}; - -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; -}; - void * ServerThread::Thread() { ThreadStarted(); @@ -157,265 +104,6 @@ void * ServerThread::Thread() return NULL; } -void * EmergeThread::Thread() -{ - ThreadStarted(); - - log_register_thread("EmergeThread"); - - DSTACK(__FUNCTION_NAME); - - BEGIN_DEBUG_EXCEPTION_HANDLER - - bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info"); - - v3s16 last_tried_pos(-32768,-32768,-32768); // For error output - - ServerMap &map = ((ServerMap&)m_server->m_env->getMap()); - EmergeManager *emerge = m_server->m_emerge; - Mapgen *mapgen = emerge->getMapgen(); - - /* - Get block info from queue, emerge them and send them - to clients. - - After queue is empty, exit. - */ - while(getRun()) - try{ - QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop(); - if(qptr == NULL) - break; - - SharedPtr<QueuedBlockEmerge> q(qptr); - - v3s16 &p = q->pos; - v2s16 p2d(p.X,p.Z); - - last_tried_pos = p; - - /* - Do not generate over-limit - */ - if(blockpos_over_limit(p)) - continue; - - //infostream<<"EmergeThread::Thread(): running"<<std::endl; - - //TimeTaker timer("block emerge"); - - /* - Try to emerge it from somewhere. - - If it is only wanted as optional, only loading from disk - will be allowed. - */ - - /* - Check if any peer wants it as non-optional. In that case it - will be generated. - - Also decrement the emerge queue count in clients. - */ - - bool only_from_disk = true; - - { - core::map<u16, u8>::Iterator i; - for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++) - { - //u16 peer_id = i.getNode()->getKey(); - - // Check flags - u8 flags = i.getNode()->getValue(); - if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false) - only_from_disk = false; - - } - } - - if(enable_mapgen_debug_info) - infostream<<"EmergeThread: p=" - <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") " - <<"only_from_disk="<<only_from_disk<<std::endl; - - - - MapBlock *block = NULL; - bool got_block = true; - core::map<v3s16, MapBlock*> modified_blocks; - - /* - Try to fetch block from memory or disk. - If not found and asked to generate, initialize generator. - */ - - bool started_generate = false; - BlockMakeData data; - - { - JMutexAutoLock envlock(m_server->m_env_mutex); - - // Load sector if it isn't loaded - if(map.getSectorNoGenerateNoEx(p2d) == NULL) - map.loadSectorMeta(p2d); - - // Attempt to load block - block = map.getBlockNoCreateNoEx(p); - if(!block || block->isDummy() || !block->isGenerated()) - { - if(enable_mapgen_debug_info) - infostream<<"EmergeThread: not in memory, " - <<"attempting to load from disk"<<std::endl; - - block = map.loadBlock(p); - } - - // If could not load and allowed to generate, start generation - // inside this same envlock - if(only_from_disk == false && - (block == NULL || block->isGenerated() == false)){ - if(enable_mapgen_debug_info) - infostream<<"EmergeThread: generating"<<std::endl; - started_generate = true; - - map.initBlockMake(&data, p); - } - } - - /* - If generator was initialized, generate now when envlock is free. - */ - if(started_generate) - { - { - ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block", - SPT_AVG); - TimeTaker t("mapgen::make_block()"); - - mapgen->makeChunk(&data); - //mapgen::make_block(&data); - - if(enable_mapgen_debug_info == false) - t.stop(true); // Hide output - } - - do{ // enable break - // Lock environment again to access the map - JMutexAutoLock envlock(m_server->m_env_mutex); - - ScopeProfiler sp(g_profiler, "EmergeThread: after " - "mapgen::make_block (envlock)", SPT_AVG); - - // Blit data back on map, update lighting, add mobs and - // whatever this does - map.finishBlockMake(&data, modified_blocks); - - // Get central block - block = map.getBlockNoCreateNoEx(p); - - // If block doesn't exist, don't try doing anything with it - // This happens if the block is not in generation boundaries - if(!block) - break; - - /* - 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 - */ - //MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events); - MapEditEventAreaIgnorer ign( - &m_server->m_ignore_map_edit_events_area, - VoxelArea(minp, maxp)); - { - TimeTaker timer("on_generated"); - scriptapi_environment_on_generated(m_server->m_lua, - minp, maxp, emerge->getBlockSeed(minp)); - /*int t = timer.stop(true); - dstream<<"on_generated took "<<t<<"ms"<<std::endl;*/ - } - - if(enable_mapgen_debug_info) - infostream<<"EmergeThread: ended up with: " - <<analyze_block(block)<<std::endl; - - // Activate objects and stuff - m_server->m_env->activateBlock(block, 0); - }while(false); - } - - if(block == NULL) - got_block = false; - - /* - Set sent status of modified blocks on clients - */ - - // NOTE: Server's clients are also behind the connection mutex - JMutexAutoLock lock(m_server->m_con_mutex); - - /* - Add the originally fetched block to the modified list - */ - if(got_block) - { - modified_blocks.insert(p, block); - } - - /* - Set the modified blocks unsent for all the clients - */ - - for(core::map<u16, RemoteClient*>::Iterator - i = m_server->m_clients.getIterator(); - i.atEnd() == false; i++) - { - RemoteClient *client = i.getNode()->getValue(); - - if(modified_blocks.size() > 0) - { - // Remove block from sent history - client->SetBlocksNotSent(modified_blocks); - } - } - } - catch(VersionMismatchException &e) - { - std::ostringstream err; - err<<"World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl; - err<<"----"<<std::endl; - err<<"\""<<e.what()<<"\""<<std::endl; - err<<"See debug.txt."<<std::endl; - err<<"World probably saved by a newer version of Minetest."<<std::endl; - m_server->setAsyncFatalError(err.str()); - } - catch(SerializationError &e) - { - std::ostringstream err; - err<<"Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl; - err<<"----"<<std::endl; - err<<"\""<<e.what()<<"\""<<std::endl; - err<<"See debug.txt."<<std::endl; - err<<"You can ignore this using [ignore_world_load_errors = true]."<<std::endl; - m_server->setAsyncFatalError(err.str()); - } - - END_DEBUG_EXCEPTION_HANDLER(errorstream) - - log_deregister_thread(); - - return NULL; -} - v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const { if(pos_exists) *pos_exists = false; @@ -770,7 +458,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, */ if(block == NULL || surely_not_found_on_disk || block_is_invalid) { - //TODO: Get value from somewhere + /* //TODO: Get value from somewhere // Allow only one block in emerge queue //if(server->m_emerge_queue.peerItemCount(peer_id) < 1) // Allow two blocks in queue per client @@ -799,7 +487,17 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, nearest_emergefull_d = d; goto queue_full_break; } + */ + if (server->m_emerge->enqueueBlockEmerge(peer_id, p, generate)) { + if (nearest_emerged_d == -1) + nearest_emerged_d = d; + } else { + if (nearest_emergefull_d == -1) + nearest_emergefull_d = d; + goto queue_full_break; + } + // get next one. continue; } @@ -953,7 +651,7 @@ Server::Server( m_craftdef(createCraftDefManager()), m_event(new EventManager()), m_thread(this), - m_emergethread(this), + //m_emergethread(this), m_time_of_day_send_timer(0), m_uptime(0), m_shutdown_requested(false), @@ -1278,9 +976,9 @@ void Server::stop() // Stop threads (set run=false first so both start stopping) m_thread.setRun(false); - m_emergethread.setRun(false); + //m_emergethread.setRun(false); m_thread.stop(); - m_emergethread.stop(); + //m_emergethread.stop(); infostream<<"Server: Threads stopped"<<std::endl; } @@ -1951,7 +1649,7 @@ void Server::AsyncRunStep() { counter = 0.0; - m_emergethread.trigger(); + m_emerge->emergethread->trigger(); // Update m_enable_rollback_recording here too m_enable_rollback_recording = @@ -3115,8 +2813,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) infostream<<"Server: Not punching: Node not found." <<" Adding block to emerge queue." <<std::endl; - m_emerge_queue.addBlock(peer_id, - getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK); + m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false); } if(n.getContent() != CONTENT_IGNORE) scriptapi_node_on_punch(m_lua, p_under, n, playersao); @@ -3172,8 +2869,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) infostream<<"Server: Not finishing digging: Node not found." <<" Adding block to emerge queue." <<std::endl; - m_emerge_queue.addBlock(peer_id, - getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK); + m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false); } /* Cheat prevention */ @@ -4728,10 +4424,7 @@ void Server::notifyPlayers(const std::wstring msg) void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate) { - u8 flags = 0; - if(!allow_generate) - flags |= BLOCK_EMERGE_FLAG_FROMDISK; - m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags); + m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, blockpos, allow_generate); } Inventory* Server::createDetachedInventory(const std::string &name) diff --git a/src/server.h b/src/server.h index 26973643a..e92cbb564 100644 --- a/src/server.h +++ b/src/server.h @@ -39,6 +39,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "rollback_interface.h" // Needed for rollbackRevertActions() #include <list> // Needed for rollbackRevertActions() +#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" + struct LuaState; typedef struct lua_State lua_State; class IWritableItemDefManager; @@ -47,6 +49,7 @@ class IWritableCraftDefManager; class EventManager; class PlayerSAO; class IRollbackManager; +class EmergeManager; class ServerError : public std::exception { @@ -120,11 +123,9 @@ public: If it is, update the peer to it and quit. */ core::list<QueuedBlockEmerge*>::Iterator i; - for(i=m_queue.begin(); i!=m_queue.end(); i++) - { + for(i=m_queue.begin(); i!=m_queue.end(); i++) { QueuedBlockEmerge *q = *i; - if(q->pos == pos) - { + if (q->pos == pos) { q->peer_ids[peer_id] = flags; return; } @@ -136,7 +137,7 @@ public: */ QueuedBlockEmerge *q = new QueuedBlockEmerge; q->pos = pos; - if(peer_id != 0) + if (peer_id != 0) q->peer_ids[peer_id] = flags; m_queue.push_back(q); } @@ -200,30 +201,6 @@ public: void * Thread(); }; -class EmergeThread : public SimpleThread -{ - Server *m_server; - -public: - - EmergeThread(Server *server): - SimpleThread(), - m_server(server) - { - } - - void * Thread(); - - void trigger() - { - setRun(true); - if(IsRunning() == false) - { - Start(); - } - } -}; - struct PlayerInfo { u16 id; @@ -785,9 +762,9 @@ private: // The server mainly operates in this thread ServerThread m_thread; // This thread fetches and generates map - EmergeThread m_emergethread; + //EmergeThread m_emergethread; // Queue of block coordinates to be processed by the emerge thread - BlockEmergeQueue m_emerge_queue; + //BlockEmergeQueue m_emerge_queue; /* Time related stuff |