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