1 /*
2 emerge.cpp
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
5 */
6
7 /*
8 This file is part of Freeminer.
9
10 Freeminer is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 Freeminer is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with Freeminer. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "emerge.h"
25 #include "server.h"
26 #include <iostream>
27 #include <queue>
28 #include "jthread/jevent.h"
29 #include "map.h"
30 #include "environment.h"
31 #include "util/container.h"
32 #include "util/thread.h"
33 #include "main.h"
34 #include "constants.h"
35 #include "voxel.h"
36 #include "config.h"
37 #include "mapblock.h"
38 #include "serverobject.h"
39 #include "settings.h"
40 #include "scripting_game.h"
41 #include "profiler.h"
42 #include "log_types.h"
43 #include "nodedef.h"
44 #include "mg_biome.h"
45 #include "mg_ore.h"
46 #include "mg_decoration.h"
47 #include "mg_schematic.h"
48 #include "mapgen_v5.h"
49 #include "mapgen_v6.h"
50 #include "mapgen_v7.h"
51 #include "mapgen_indev.h"
52 #include "mapgen_singlenode.h"
53 #include "mapgen_math.h"
54 #include "circuit.h"
55 #include "util/thread_pool.h"
56
57
58 class EmergeThread : public thread_pool
59 {
60 public:
61 Server *m_server;
62 ServerMap *map;
63 Circuit* m_circuit;
64 EmergeManager *emerge;
65 Mapgen *mapgen;
66 bool enable_mapgen_debug_info;
67 int id;
68
69 Event qevent;
70 std::queue<v3s16> blockqueue;
71
EmergeThread(Server * server,int ethreadid)72 EmergeThread(Server *server, int ethreadid):
73 m_server(server),
74 map(NULL),
75 emerge(NULL),
76 mapgen(NULL),
77 enable_mapgen_debug_info(false),
78 id(ethreadid)
79 {
80 }
81
82 void *Thread();
83 bool popBlockEmerge(v3s16 *pos, u8 *flags);
84 bool getBlockOrStartGen(v3s16 p, MapBlock **b,
85 BlockMakeData *data, bool allow_generate);
86 };
87
88
89 /////////////////////////////// Emerge Manager ////////////////////////////////
90
EmergeManager(IGameDef * gamedef)91 EmergeManager::EmergeManager(IGameDef *gamedef) {
92 //register built-in mapgens
93 registerMapgen("v5", new MapgenFactoryV5());
94 registerMapgen("v6", new MapgenFactoryV6());
95 registerMapgen("v7", new MapgenFactoryV7());
96 registerMapgen("indev", new MapgenFactoryIndev());
97 registerMapgen("singlenode", new MapgenFactorySinglenode());
98 registerMapgen("math", new MapgenFactoryMath());
99
100 this->ndef = gamedef->getNodeDefManager();
101 this->biomemgr = new BiomeManager(gamedef);
102 this->oremgr = new OreManager(gamedef);
103 this->decomgr = new DecorationManager(gamedef);
104 this->schemmgr = new SchematicManager(gamedef);
105 this->gennotify = 0;
106
107 // Note that accesses to this variable are not synchronized.
108 // This is because the *only* thread ever starting or stopping
109 // EmergeThreads should be the ServerThread.
110 this->threads_active = false;
111
112 mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
113
114 // if unspecified, leave a proc for the main thread and one for
115 // some other misc thread
116 s16 nthreads = 0;
117 if (!g_settings->getS16NoEx("num_emerge_threads", nthreads))
118 {}
119 if (nthreads < 1)
120 nthreads = porting::getNumberOfProcessors() - 2;
121 if (nthreads < 1)
122 nthreads = 1;
123
124 qlimit_total = g_settings->getU16("emergequeue_limit_total");
125 if (qlimit_total < 1)
126 qlimit_total = nthreads*128;
127 if (!g_settings->getU16NoEx("emergequeue_limit_diskonly", qlimit_diskonly))
128 {}
129 if (qlimit_diskonly < 1) {
130 qlimit_diskonly = nthreads * 100;
131 }
132 if (!g_settings->getU16NoEx("emergequeue_limit_generate", qlimit_generate))
133 {}
134 if (qlimit_generate < 1) {
135 qlimit_generate = nthreads * 32;
136 }
137 //errorstream<<"==> qlimit_generate="<<qlimit_generate<<" qlimit_diskonly="<<qlimit_diskonly<<" qlimit_total="<<qlimit_total<<std::endl;
138
139 // don't trust user input for something very important like this
140 if (qlimit_total < 1)
141 qlimit_total = 1;
142 if (qlimit_diskonly < 1)
143 qlimit_diskonly = 1;
144 if (qlimit_generate < 1)
145 qlimit_generate = 1;
146
147 for (s16 i = 0; i < nthreads; i++)
148 emergethread.push_back(new EmergeThread((Server *) gamedef, i));
149
150 infostream << "EmergeManager: using " << nthreads << " threads" << std::endl;
151 }
152
153
~EmergeManager()154 EmergeManager::~EmergeManager() {
155 for (unsigned int i = 0; i != emergethread.size(); i++) {
156 if (threads_active) {
157 emergethread[i]->Stop();
158 emergethread[i]->qevent.signal();
159 emergethread[i]->Wait();
160 }
161 delete emergethread[i];
162 delete mapgen[i];
163 }
164 emergethread.clear();
165 mapgen.clear();
166
167 std::map<std::string, MapgenFactory *>::iterator it;
168 for (it = mglist.begin(); it != mglist.end(); ++it)
169 delete it->second;
170 mglist.clear();
171
172 delete biomemgr;
173 delete oremgr;
174 delete decomgr;
175 delete schemmgr;
176
177 if (params.sparams) {
178 delete params.sparams;
179 params.sparams = NULL;
180 }
181 }
182
183
loadMapgenParams()184 void EmergeManager::loadMapgenParams() {
185 loadParamsFromSettings(g_settings);
186
187 if (g_settings->get("fixed_map_seed").empty()) {
188 params.seed = (((u64)(myrand() & 0xffff) << 0)
189 | ((u64)(myrand() & 0xffff) << 16)
190 | ((u64)(myrand() & 0xffff) << 32)
191 | ((u64)(myrand() & 0xffff) << 48));
192 }
193 }
194
195
initMapgens()196 void EmergeManager::initMapgens() {
197 if (mapgen.size())
198 return;
199
200 if (!params.sparams) {
201 params.sparams = createMapgenParams(params.mg_name);
202 if (!params.sparams) {
203 params.mg_name = DEFAULT_MAPGEN;
204 params.sparams = createMapgenParams(params.mg_name);
205 assert(params.sparams);
206 }
207 params.sparams->readParams(g_settings);
208 }
209
210 // Create the mapgens
211 for (size_t i = 0; i != emergethread.size(); i++) {
212 Mapgen *mg = createMapgen(params.mg_name, i, ¶ms);
213 if (!mg)
214 continue;
215 mapgen.push_back(mg);
216 }
217 }
218
219
getCurrentMapgen()220 Mapgen *EmergeManager::getCurrentMapgen() {
221 for (unsigned int i = 0; i != emergethread.size(); i++) {
222 if (emergethread[i]->IsSameThread())
223 return emergethread[i]->mapgen;
224 }
225
226 return NULL;
227 }
228
229
startThreads()230 void EmergeManager::startThreads() {
231 if (threads_active)
232 return;
233
234 for (unsigned int i = 0; i != emergethread.size(); i++)
235 emergethread[i]->Start();
236
237 threads_active = true;
238 }
239
240
stopThreads()241 void EmergeManager::stopThreads() {
242 if (!threads_active)
243 return;
244
245 // Request thread stop in parallel
246 for (unsigned int i = 0; i != emergethread.size(); i++) {
247 emergethread[i]->Stop();
248 emergethread[i]->qevent.signal();
249 }
250
251 // Then do the waiting for each
252 for (unsigned int i = 0; i != emergethread.size(); i++)
253 emergethread[i]->Wait();
254
255 threads_active = false;
256 }
257
258
enqueueBlockEmerge(u16 peer_id,v3s16 p,bool allow_generate)259 bool EmergeManager::enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate) {
260 std::map<v3s16, BlockEmergeData *>::const_iterator iter;
261 BlockEmergeData *bedata;
262 u16 count;
263 u8 flags = 0;
264 int idx = 0;
265
266 if (allow_generate)
267 flags |= BLOCK_EMERGE_ALLOWGEN;
268
269 {
270 JMutexAutoLock queuelock(queuemutex);
271
272 count = blocks_enqueued.size();
273 if (count >= qlimit_total)
274 return false;
275
276 count = peer_queue_count[peer_id];
277 u16 qlimit_peer = allow_generate ? qlimit_generate : qlimit_diskonly;
278 if (count >= qlimit_peer)
279 return false;
280
281 iter = blocks_enqueued.find(p);
282 if (iter != blocks_enqueued.end()) {
283 bedata = iter->second;
284 bedata->flags |= flags;
285 return true;
286 }
287
288 bedata = new BlockEmergeData;
289 bedata->flags = flags;
290 bedata->peer_requested = peer_id;
291 blocks_enqueued.insert(std::make_pair(p, bedata));
292
293 peer_queue_count[peer_id] = count + 1;
294
295 // insert into the EmergeThread queue with the least items
296 int lowestitems = emergethread[0]->blockqueue.size();
297 for (unsigned int i = 1; i != emergethread.size(); i++) {
298 int nitems = emergethread[i]->blockqueue.size();
299 if (nitems < lowestitems) {
300 idx = i;
301 lowestitems = nitems;
302 }
303 }
304
305 emergethread[idx]->blockqueue.push(p);
306 }
307 emergethread[idx]->qevent.signal();
308
309 return true;
310 }
311
312
getGroundLevelAtPoint(v2s16 p)313 int EmergeManager::getGroundLevelAtPoint(v2s16 p) {
314 if (mapgen.size() == 0 || !mapgen[0]) {
315 errorstream << "EmergeManager: getGroundLevelAtPoint() called"
316 " before mapgen initialized" << std::endl;
317 return 0;
318 }
319
320 return mapgen[0]->getGroundLevelAtPoint(p);
321 }
322
323
isBlockUnderground(v3s16 blockpos)324 bool EmergeManager::isBlockUnderground(v3s16 blockpos) {
325 /*
326 v2s16 p = v2s16((blockpos.X * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2,
327 (blockpos.Y * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2);
328 int ground_level = getGroundLevelAtPoint(p);
329 return blockpos.Y * (MAP_BLOCKSIZE + 1) <= min(water_level, ground_level);
330 */
331
332 //yuck, but then again, should i bother being accurate?
333 //the height of the nodes in a single block is quite variable
334 return blockpos.Y * (MAP_BLOCKSIZE + 1) <= params.water_level;
335 }
336
337
getBlockSeed(v3s16 p)338 u32 EmergeManager::getBlockSeed(v3s16 p) {
339 return (u32)(params.seed & 0xFFFFFFFF) +
340 p.Z * 38134234 +
341 p.Y * 42123 +
342 p.X * 23;
343 }
344
345
createMapgen(std::string mgname,int mgid,MapgenParams * mgparams)346 Mapgen *EmergeManager::createMapgen(std::string mgname, int mgid,
347 MapgenParams *mgparams) {
348 std::map<std::string, MapgenFactory *>::const_iterator iter;
349 iter = mglist.find(mgname);
350 if (iter == mglist.end()) {
351 errorstream << "EmergeManager; mapgen " << mgname <<
352 " not registered" << std::endl;
353 return NULL;
354 }
355
356 MapgenFactory *mgfactory = iter->second;
357 return mgfactory->createMapgen(mgid, mgparams, this);
358 }
359
360
createMapgenParams(std::string mgname)361 MapgenSpecificParams *EmergeManager::createMapgenParams(std::string mgname) {
362 std::map<std::string, MapgenFactory *>::const_iterator iter;
363 iter = mglist.find(mgname);
364 if (iter == mglist.end()) {
365 errorstream << "EmergeManager: mapgen " << mgname <<
366 " not registered" << std::endl;
367 return NULL;
368 }
369
370 MapgenFactory *mgfactory = iter->second;
371 return mgfactory->createMapgenParams();
372 }
373
374
loadParamsFromSettings(Settings * settings)375 void EmergeManager::loadParamsFromSettings(Settings *settings) {
376 std::string seed_str;
377 const char *setname = (settings == g_settings) ? "fixed_map_seed" : "seed";
378
379 if (settings->getNoEx(setname, seed_str))
380 params.seed = read_seed(seed_str.c_str());
381
382 settings->getNoEx("mg_name", params.mg_name);
383 settings->getS16NoEx("water_level", params.water_level);
384 settings->getS16NoEx("chunksize", params.chunksize);
385 settings->getFlagStrNoEx("mg_flags", params.flags, flagdesc_mapgen);
386
387 delete params.sparams;
388 params.sparams = createMapgenParams(params.mg_name);
389 if (params.sparams)
390 params.sparams->readParams(settings);
391 }
392
393
saveParamsToSettings(Settings * settings)394 void EmergeManager::saveParamsToSettings(Settings *settings) {
395 settings->set("mg_name", params.mg_name);
396 settings->setU64("seed", params.seed);
397 settings->setS16("water_level", params.water_level);
398 settings->setS16("chunksize", params.chunksize);
399 settings->setFlagStr("mg_flags", params.flags, flagdesc_mapgen, (u32)-1);
400
401 if (params.sparams)
402 params.sparams->writeParams(settings);
403 }
404
405
registerMapgen(std::string mgname,MapgenFactory * mgfactory)406 void EmergeManager::registerMapgen(std::string mgname, MapgenFactory *mgfactory) {
407 mglist.insert(std::make_pair(mgname, mgfactory));
408 infostream << "EmergeManager: registered mapgen " << mgname << std::endl;
409 }
410
411
412 ////////////////////////////// Emerge Thread //////////////////////////////////
413
popBlockEmerge(v3s16 * pos,u8 * flags)414 bool EmergeThread::popBlockEmerge(v3s16 *pos, u8 *flags) {
415 std::map<v3s16, BlockEmergeData *>::iterator iter;
416 JMutexAutoLock queuelock(emerge->queuemutex);
417
418 if (blockqueue.empty())
419 return false;
420 v3s16 p = blockqueue.front();
421 blockqueue.pop();
422
423 *pos = p;
424
425 iter = emerge->blocks_enqueued.find(p);
426 if (iter == emerge->blocks_enqueued.end())
427 return false; //uh oh, queue and map out of sync!!
428
429 BlockEmergeData *bedata = iter->second;
430 *flags = bedata->flags;
431
432 emerge->peer_queue_count[bedata->peer_requested]--;
433
434 delete bedata;
435 emerge->blocks_enqueued.erase(iter);
436
437 return true;
438 }
439
440
getBlockOrStartGen(v3s16 p,MapBlock ** b,BlockMakeData * data,bool allow_gen)441 bool EmergeThread::getBlockOrStartGen(v3s16 p, MapBlock **b,
442 BlockMakeData *data, bool allow_gen) {
443 //envlock: usually takes <=1ms, sometimes 90ms or ~400ms to acquire
444 //JMutexAutoLock envlock(m_server->m_env_mutex);
445
446 // Attempt to load block
447 MapBlock *block = map->getBlockNoCreateNoEx(p);
448 if (!block || block->isDummy()) {
449 EMERGE_DBG_OUT("not in memory, attempting to load from disk ag="<<allow_gen<<" block="<<block<<" p="<<p);
450 block = map->loadBlock(p);
451 if(block)
452 {
453 // block->pushElementsToCircuit(m_circuit);
454 // m_circuit->processElementsQueue(*map, map->getNodeDefManager());
455 }
456 if (block && block->isGenerated())
457 map->prepareBlock(block);
458 }
459
460 // If could not load and allowed to generate,
461 // start generation inside this same envlock
462 if (allow_gen && (!block)) {
463 EMERGE_DBG_OUT("generating b="<<block);
464 *b = block;
465 return map->initBlockMake(data, p);
466 }
467
468 *b = block;
469 return false;
470 }
471
472
Thread()473 void *EmergeThread::Thread() {
474 ThreadStarted();
475 log_register_thread("EmergeThread" + itos(id));
476 DSTACK(__FUNCTION_NAME);
477 BEGIN_DEBUG_EXCEPTION_HANDLER
478
479 v3s16 last_tried_pos(-32768,-32768,-32768); // For error output
480 v3s16 p;
481 u8 flags = 0;
482
483 map = (ServerMap *)&(m_server->m_env->getMap());
484 m_circuit = m_server->m_circuit;
485 emerge = m_server->m_emerge;
486 mapgen = emerge->mapgen[id];
487 enable_mapgen_debug_info = emerge->mapgen_debug_info;
488
489 porting::setThreadName(("EmergeThread" + itos(id)).c_str());
490 porting::setThreadPriority(5);
491
492 while (!StopRequested())
493 try {
494 if (!popBlockEmerge(&p, &flags)) {
495 qevent.wait();
496 continue;
497 }
498
499 last_tried_pos = p;
500 if (blockpos_over_limit(p))
501 continue;
502
503 bool allow_generate = flags & BLOCK_EMERGE_ALLOWGEN;
504 EMERGE_DBG_OUT("p=" PP(p) " allow_generate=" << allow_generate);
505
506 /*
507 Try to fetch block from memory or disk.
508 If not found and asked to generate, initialize generator.
509 */
510 BlockMakeData data;
511 MapBlock *block = NULL;
512 std::map<v3s16, MapBlock *> modified_blocks;
513
514 if (getBlockOrStartGen(p, &block, &data, allow_generate) && mapgen) {
515 {
516 ScopeProfiler sp(g_profiler, "EmergeThread: Mapgen::makeChunk", SPT_AVG);
517 TimeTaker t("mapgen::make_block()");
518
519 mapgen->makeChunk(&data);
520
521 if (enable_mapgen_debug_info == false)
522 t.stop(true); // Hide output
523 }
524
525 {
526 //envlock: usually 0ms, but can take either 30 or 400ms to acquire
527 //JMutexAutoLock envlock(m_server->m_env_mutex);
528 ScopeProfiler sp(g_profiler, "EmergeThread: after "
529 "Mapgen::makeChunk (envlock)", SPT_AVG);
530
531 map->finishBlockMake(&data, modified_blocks);
532
533 block = map->getBlockNoCreateNoEx(p);
534 if (block) {
535 /*
536 Do some post-generate stuff
537 */
538 v3s16 minp = data.blockpos_min * MAP_BLOCKSIZE;
539 v3s16 maxp = data.blockpos_max * MAP_BLOCKSIZE +
540 v3s16(1,1,1) * (MAP_BLOCKSIZE - 1);
541
542 // Ignore map edit events, they will not need to be sent
543 // to anybody because the block hasn't been sent to anybody
544 MapEditEventAreaIgnorer
545 ign(&m_server->m_ignore_map_edit_events_area,
546 VoxelArea(minp, maxp));
547 try { // takes about 90ms with -O1 on an e3-1230v2
548 m_server->getScriptIface()->environment_OnGenerated(
549 minp, maxp, emerge->getBlockSeed(minp));
550 } catch(LuaError &e) {
551 m_server->setAsyncFatalError(e.what());
552 }
553
554 EMERGE_DBG_OUT("ended up with: " << analyze_block(block));
555
556 m_server->m_env->activateBlock(block, 0);
557 }
558 }
559
560 /*
561 Set sent status of modified blocks on clients
562 */
563 // Add the originally fetched block to the modified list
564 if (block)
565 modified_blocks[p] = block;
566 else if (allow_generate)
567 infostream<<"nothing generated at "<<PP(p)<<std::endl;
568
569 }
570
571 if (modified_blocks.size() > 0) {
572 m_server->SetBlocksNotSent(modified_blocks);
573 }
574 if (mapgen->heat_cache.size() > 1000) {
575 mapgen->heat_cache.clear();
576 mapgen->humidity_cache.clear();
577 }
578 }
579 catch (VersionMismatchException &e) {
580 std::ostringstream err;
581 err << "World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl;
582 err << "----"<<std::endl;
583 err << "\""<<e.what()<<"\""<<std::endl;
584 err << "See debug.txt."<<std::endl;
585 err << "World probably saved by a newer version of Freeminer."<<std::endl;
586 m_server->setAsyncFatalError(err.str());
587 }
588 catch (SerializationError &e) {
589 std::ostringstream err;
590 err << "Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl;
591 err << "----"<<std::endl;
592 err << "\""<<e.what()<<"\""<<std::endl;
593 err << "See debug.txt."<<std::endl;
594 err << "You can ignore this using [ignore_world_load_errors = true]."<<std::endl;
595 m_server->setAsyncFatalError(err.str());
596 }
597
598 END_DEBUG_EXCEPTION_HANDLER(errorstream)
599 log_deregister_thread();
600 return NULL;
601 }
602