3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
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.
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.
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.
27 #include "util/container.h"
28 #include "util/thread.h"
29 #include "threading/event.h"
32 #include "constants.h"
33 #include "environment.h"
37 #include "mapgen/mg_biome.h"
38 #include "mapgen/mg_ore.h"
39 #include "mapgen/mg_decoration.h"
40 #include "mapgen/mg_schematic.h"
43 #include "scripting_server.h"
48 class EmergeThread
: public Thread
{
50 bool enable_mapgen_debug_info
;
53 EmergeThread(Server
*server
, int ethreadid
);
54 ~EmergeThread() = default;
59 // Requires queue mutex held
60 bool pushBlock(const v3s16
&pos
);
62 void cancelPendingItems();
64 static void runCompletionCallbacks(
65 const v3s16
&pos
, EmergeAction action
,
66 const EmergeCallbackList
&callbacks
);
71 EmergeManager
*m_emerge
;
75 std::queue
<v3s16
> m_block_queue
;
77 bool popBlockEmerge(v3s16
*pos
, BlockEmergeData
*bedata
);
79 EmergeAction
getBlockOrStartGen(
80 const v3s16
&pos
, bool allow_gen
, MapBlock
**block
, BlockMakeData
*data
);
81 MapBlock
*finishGen(v3s16 pos
, BlockMakeData
*bmdata
,
82 std::map
<v3s16
, MapBlock
*> *modified_blocks
);
84 friend class EmergeManager
;
87 class MapEditEventAreaIgnorer
90 MapEditEventAreaIgnorer(VoxelArea
*ignorevariable
, const VoxelArea
&a
):
91 m_ignorevariable(ignorevariable
)
93 if(m_ignorevariable
->getVolume() == 0)
94 *m_ignorevariable
= a
;
96 m_ignorevariable
= NULL
;
99 ~MapEditEventAreaIgnorer()
103 assert(m_ignorevariable
->getVolume() != 0);
104 *m_ignorevariable
= VoxelArea();
109 VoxelArea
*m_ignorevariable
;
112 EmergeParams::~EmergeParams()
114 infostream
<< "EmergeParams: destroying " << this << std::endl
;
115 // Delete everything that was cloned on creation of EmergeParams
122 EmergeParams::EmergeParams(EmergeManager
*parent
, const BiomeManager
*biomemgr
,
123 const OreManager
*oremgr
, const DecorationManager
*decomgr
,
124 const SchematicManager
*schemmgr
) :
126 enable_mapgen_debug_info(parent
->enable_mapgen_debug_info
),
127 gen_notify_on(parent
->gen_notify_on
),
128 gen_notify_on_deco_ids(&parent
->gen_notify_on_deco_ids
),
129 biomemgr(biomemgr
->clone()), oremgr(oremgr
->clone()),
130 decomgr(decomgr
->clone()), schemmgr(schemmgr
->clone())
138 EmergeManager::EmergeManager(Server
*server
)
140 this->ndef
= server
->getNodeDefManager();
141 this->biomemgr
= new BiomeManager(server
);
142 this->oremgr
= new OreManager(server
);
143 this->decomgr
= new DecorationManager(server
);
144 this->schemmgr
= new SchematicManager(server
);
146 // Note that accesses to this variable are not synchronized.
147 // This is because the *only* thread ever starting or stopping
148 // EmergeThreads should be the ServerThread.
150 enable_mapgen_debug_info
= g_settings
->getBool("enable_mapgen_debug_info");
153 g_settings
->getS16NoEx("num_emerge_threads", nthreads
);
154 // If automatic, leave a proc for the main thread and one for
155 // some other misc thread
157 nthreads
= Thread::getNumberOfProcessors() - 2;
161 m_qlimit_total
= g_settings
->getU16("emergequeue_limit_total");
162 // FIXME: these fallback values are probably not good
163 if (!g_settings
->getU16NoEx("emergequeue_limit_diskonly", m_qlimit_diskonly
))
164 m_qlimit_diskonly
= nthreads
* 5 + 1;
165 if (!g_settings
->getU16NoEx("emergequeue_limit_generate", m_qlimit_generate
))
166 m_qlimit_generate
= nthreads
+ 1;
168 // don't trust user input for something very important like this
169 if (m_qlimit_total
< 1)
171 if (m_qlimit_diskonly
< 1)
172 m_qlimit_diskonly
= 1;
173 if (m_qlimit_generate
< 1)
174 m_qlimit_generate
= 1;
176 for (s16 i
= 0; i
< nthreads
; i
++)
177 m_threads
.push_back(new EmergeThread(server
, i
));
179 infostream
<< "EmergeManager: using " << nthreads
<< " threads" << std::endl
;
183 EmergeManager::~EmergeManager()
185 for (u32 i
= 0; i
!= m_threads
.size(); i
++) {
186 EmergeThread
*thread
= m_threads
[i
];
188 if (m_threads_active
) {
196 // Mapgen init might not be finished if there is an error during startup.
197 if (m_mapgens
.size() > i
)
208 BiomeManager
*EmergeManager::getWritableBiomeManager()
210 FATAL_ERROR_IF(!m_mapgens
.empty(),
211 "Writable managers can only be returned before mapgen init");
215 OreManager
*EmergeManager::getWritableOreManager()
217 FATAL_ERROR_IF(!m_mapgens
.empty(),
218 "Writable managers can only be returned before mapgen init");
222 DecorationManager
*EmergeManager::getWritableDecorationManager()
224 FATAL_ERROR_IF(!m_mapgens
.empty(),
225 "Writable managers can only be returned before mapgen init");
229 SchematicManager
*EmergeManager::getWritableSchematicManager()
231 FATAL_ERROR_IF(!m_mapgens
.empty(),
232 "Writable managers can only be returned before mapgen init");
237 void EmergeManager::initMapgens(MapgenParams
*params
)
239 FATAL_ERROR_IF(!m_mapgens
.empty(), "Mapgen already initialised.");
243 for (u32 i
= 0; i
!= m_threads
.size(); i
++) {
244 EmergeParams
*p
= new EmergeParams(
245 this, biomemgr
, oremgr
, decomgr
, schemmgr
);
246 infostream
<< "EmergeManager: Created params " << p
247 << " for thread " << i
<< std::endl
;
248 m_mapgens
.push_back(Mapgen::createMapgen(params
->mgtype
, params
, p
));
253 Mapgen
*EmergeManager::getCurrentMapgen()
255 if (!m_threads_active
)
258 for (u32 i
= 0; i
!= m_threads
.size(); i
++) {
259 EmergeThread
*t
= m_threads
[i
];
260 if (t
->isRunning() && t
->isCurrentThread())
268 void EmergeManager::startThreads()
270 if (m_threads_active
)
273 for (u32 i
= 0; i
!= m_threads
.size(); i
++)
274 m_threads
[i
]->start();
276 m_threads_active
= true;
280 void EmergeManager::stopThreads()
282 if (!m_threads_active
)
285 // Request thread stop in parallel
286 for (u32 i
= 0; i
!= m_threads
.size(); i
++) {
287 m_threads
[i
]->stop();
288 m_threads
[i
]->signal();
291 // Then do the waiting for each
292 for (u32 i
= 0; i
!= m_threads
.size(); i
++)
293 m_threads
[i
]->wait();
295 m_threads_active
= false;
299 bool EmergeManager::isRunning()
301 return m_threads_active
;
305 bool EmergeManager::enqueueBlockEmerge(
309 bool ignore_queue_limits
)
313 flags
|= BLOCK_EMERGE_ALLOW_GEN
;
314 if (ignore_queue_limits
)
315 flags
|= BLOCK_EMERGE_FORCE_QUEUE
;
317 return enqueueBlockEmergeEx(blockpos
, peer_id
, flags
, NULL
, NULL
);
321 bool EmergeManager::enqueueBlockEmergeEx(
325 EmergeCompletionCallback callback
,
326 void *callback_param
)
328 EmergeThread
*thread
= NULL
;
329 bool entry_already_exists
= false;
332 MutexAutoLock
queuelock(m_queue_mutex
);
334 if (!pushBlockEmergeData(blockpos
, peer_id
, flags
,
335 callback
, callback_param
, &entry_already_exists
))
338 if (entry_already_exists
)
341 thread
= getOptimalThread();
342 thread
->pushBlock(blockpos
);
352 // Mapgen-related helper functions
356 // TODO(hmmmm): Move this to ServerMap
357 v3s16
EmergeManager::getContainingChunk(v3s16 blockpos
)
359 return getContainingChunk(blockpos
, mgparams
->chunksize
);
362 // TODO(hmmmm): Move this to ServerMap
363 v3s16
EmergeManager::getContainingChunk(v3s16 blockpos
, s16 chunksize
)
365 s16 coff
= -chunksize
/ 2;
366 v3s16
chunk_offset(coff
, coff
, coff
);
368 return getContainerPos(blockpos
- chunk_offset
, chunksize
)
369 * chunksize
+ chunk_offset
;
373 int EmergeManager::getSpawnLevelAtPoint(v2s16 p
)
375 if (m_mapgens
.empty() || !m_mapgens
[0]) {
376 errorstream
<< "EmergeManager: getSpawnLevelAtPoint() called"
377 " before mapgen init" << std::endl
;
381 return m_mapgens
[0]->getSpawnLevelAtPoint(p
);
385 int EmergeManager::getGroundLevelAtPoint(v2s16 p
)
387 if (m_mapgens
.empty() || !m_mapgens
[0]) {
388 errorstream
<< "EmergeManager: getGroundLevelAtPoint() called"
389 " before mapgen init" << std::endl
;
393 return m_mapgens
[0]->getGroundLevelAtPoint(p
);
396 // TODO(hmmmm): Move this to ServerMap
397 bool EmergeManager::isBlockUnderground(v3s16 blockpos
)
400 v2s16 p
= v2s16((blockpos
.X
* MAP_BLOCKSIZE
) + MAP_BLOCKSIZE
/ 2,
401 (blockpos
.Y
* MAP_BLOCKSIZE
) + MAP_BLOCKSIZE
/ 2);
402 int ground_level
= getGroundLevelAtPoint(p
);
403 return blockpos
.Y
* (MAP_BLOCKSIZE
+ 1) <= min(water_level
, ground_level
);
406 // Use a simple heuristic; the above method is wildly inaccurate anyway.
407 return blockpos
.Y
* (MAP_BLOCKSIZE
+ 1) <= mgparams
->water_level
;
410 bool EmergeManager::pushBlockEmergeData(
414 EmergeCompletionCallback callback
,
415 void *callback_param
,
416 bool *entry_already_exists
)
418 u16
&count_peer
= m_peer_queue_count
[peer_requested
];
420 if ((flags
& BLOCK_EMERGE_FORCE_QUEUE
) == 0) {
421 if (m_blocks_enqueued
.size() >= m_qlimit_total
)
424 if (peer_requested
!= PEER_ID_INEXISTENT
) {
425 u16 qlimit_peer
= (flags
& BLOCK_EMERGE_ALLOW_GEN
) ?
426 m_qlimit_generate
: m_qlimit_diskonly
;
427 if (count_peer
>= qlimit_peer
)
430 // limit block enqueue requests for active blocks to 1/2 of total
431 if (count_peer
* 2 >= m_qlimit_total
)
436 std::pair
<std::map
<v3s16
, BlockEmergeData
>::iterator
, bool> findres
;
437 findres
= m_blocks_enqueued
.insert(std::make_pair(pos
, BlockEmergeData()));
439 BlockEmergeData
&bedata
= findres
.first
->second
;
440 *entry_already_exists
= !findres
.second
;
443 bedata
.callbacks
.emplace_back(callback
, callback_param
);
445 if (*entry_already_exists
) {
446 bedata
.flags
|= flags
;
448 bedata
.flags
= flags
;
449 bedata
.peer_requested
= peer_requested
;
458 bool EmergeManager::popBlockEmergeData(v3s16 pos
, BlockEmergeData
*bedata
)
460 std::map
<v3s16
, BlockEmergeData
>::iterator it
;
461 std::unordered_map
<u16
, u16
>::iterator it2
;
463 it
= m_blocks_enqueued
.find(pos
);
464 if (it
== m_blocks_enqueued
.end())
467 *bedata
= it
->second
;
469 it2
= m_peer_queue_count
.find(bedata
->peer_requested
);
470 if (it2
== m_peer_queue_count
.end())
473 u16
&count_peer
= it2
->second
;
474 assert(count_peer
!= 0);
477 m_blocks_enqueued
.erase(it
);
483 EmergeThread
*EmergeManager::getOptimalThread()
485 size_t nthreads
= m_threads
.size();
487 FATAL_ERROR_IF(nthreads
== 0, "No emerge threads!");
490 size_t nitems_lowest
= m_threads
[0]->m_block_queue
.size();
492 for (size_t i
= 1; i
< nthreads
; i
++) {
493 size_t nitems
= m_threads
[i
]->m_block_queue
.size();
494 if (nitems
< nitems_lowest
) {
496 nitems_lowest
= nitems
;
500 return m_threads
[index
];
508 EmergeThread::EmergeThread(Server
*server
, int ethreadid
) :
509 enable_mapgen_debug_info(false),
516 m_name
= "Emerge-" + itos(ethreadid
);
520 void EmergeThread::signal()
522 m_queue_event
.signal();
526 bool EmergeThread::pushBlock(const v3s16
&pos
)
528 m_block_queue
.push(pos
);
533 void EmergeThread::cancelPendingItems()
535 MutexAutoLock
queuelock(m_emerge
->m_queue_mutex
);
537 while (!m_block_queue
.empty()) {
538 BlockEmergeData bedata
;
541 pos
= m_block_queue
.front();
544 m_emerge
->popBlockEmergeData(pos
, &bedata
);
546 runCompletionCallbacks(pos
, EMERGE_CANCELLED
, bedata
.callbacks
);
551 void EmergeThread::runCompletionCallbacks(const v3s16
&pos
, EmergeAction action
,
552 const EmergeCallbackList
&callbacks
)
554 for (size_t i
= 0; i
!= callbacks
.size(); i
++) {
555 EmergeCompletionCallback callback
;
558 callback
= callbacks
[i
].first
;
559 param
= callbacks
[i
].second
;
561 callback(pos
, action
, param
);
566 bool EmergeThread::popBlockEmerge(v3s16
*pos
, BlockEmergeData
*bedata
)
568 MutexAutoLock
queuelock(m_emerge
->m_queue_mutex
);
570 if (m_block_queue
.empty())
573 *pos
= m_block_queue
.front();
576 m_emerge
->popBlockEmergeData(*pos
, bedata
);
582 EmergeAction
EmergeThread::getBlockOrStartGen(
583 const v3s16
&pos
, bool allow_gen
, MapBlock
**block
, BlockMakeData
*bmdata
)
585 MutexAutoLock
envlock(m_server
->m_env_mutex
);
587 // 1). Attempt to fetch block from memory
588 *block
= m_map
->getBlockNoCreateNoEx(pos
);
589 if (*block
&& !(*block
)->isDummy()) {
590 if ((*block
)->isGenerated())
591 return EMERGE_FROM_MEMORY
;
593 // 2). Attempt to load block from disk if it was not in the memory
594 *block
= m_map
->loadBlock(pos
);
595 if (*block
&& (*block
)->isGenerated())
596 return EMERGE_FROM_DISK
;
599 // 3). Attempt to start generation
600 if (allow_gen
&& m_map
->initBlockMake(pos
, bmdata
))
601 return EMERGE_GENERATED
;
603 // All attempts failed; cancel this block emerge
604 return EMERGE_CANCELLED
;
608 MapBlock
*EmergeThread::finishGen(v3s16 pos
, BlockMakeData
*bmdata
,
609 std::map
<v3s16
, MapBlock
*> *modified_blocks
)
611 MutexAutoLock
envlock(m_server
->m_env_mutex
);
612 ScopeProfiler
sp(g_profiler
,
613 "EmergeThread: after Mapgen::makeChunk", SPT_AVG
);
616 Perform post-processing on blocks (invalidate lighting, queue liquid
617 transforms, etc.) to finish block make
619 m_map
->finishBlockMake(bmdata
, modified_blocks
);
621 MapBlock
*block
= m_map
->getBlockNoCreateNoEx(pos
);
623 errorstream
<< "EmergeThread::finishGen: Couldn't grab block we "
624 "just generated: " << PP(pos
) << std::endl
;
628 v3s16 minp
= bmdata
->blockpos_min
* MAP_BLOCKSIZE
;
629 v3s16 maxp
= bmdata
->blockpos_max
* MAP_BLOCKSIZE
+
630 v3s16(1,1,1) * (MAP_BLOCKSIZE
- 1);
632 // Ignore map edit events, they will not need to be sent
633 // to anybody because the block hasn't been sent to anybody
634 MapEditEventAreaIgnorer
ign(
635 &m_server
->m_ignore_map_edit_events_area
,
636 VoxelArea(minp
, maxp
));
639 Run Lua on_generated callbacks
642 m_server
->getScriptIface()->environment_OnGenerated(
643 minp
, maxp
, m_mapgen
->blockseed
);
644 } catch (LuaError
&e
) {
645 m_server
->setAsyncFatalError("Lua: finishGen" + std::string(e
.what()));
649 Clear generate notifier events
651 m_mapgen
->gennotify
.clearEvents();
653 EMERGE_DBG_OUT("ended up with: " << analyze_block(block
));
658 m_server
->m_env
->activateBlock(block
, 0);
664 void *EmergeThread::run()
666 BEGIN_DEBUG_EXCEPTION_HANDLER
670 m_map
= (ServerMap
*)&(m_server
->m_env
->getMap());
671 m_emerge
= m_server
->m_emerge
;
672 m_mapgen
= m_emerge
->m_mapgens
[id
];
673 enable_mapgen_debug_info
= m_emerge
->enable_mapgen_debug_info
;
676 while (!stopRequested()) {
677 std::map
<v3s16
, MapBlock
*> modified_blocks
;
678 BlockEmergeData bedata
;
679 BlockMakeData bmdata
;
683 if (!popBlockEmerge(&pos
, &bedata
)) {
684 m_queue_event
.wait();
688 if (blockpos_over_max_limit(pos
))
691 bool allow_gen
= bedata
.flags
& BLOCK_EMERGE_ALLOW_GEN
;
692 EMERGE_DBG_OUT("pos=" PP(pos
) " allow_gen=" << allow_gen
);
694 action
= getBlockOrStartGen(pos
, allow_gen
, &block
, &bmdata
);
695 if (action
== EMERGE_GENERATED
) {
697 ScopeProfiler
sp(g_profiler
,
698 "EmergeThread: Mapgen::makeChunk", SPT_AVG
);
700 m_mapgen
->makeChunk(&bmdata
);
703 block
= finishGen(pos
, &bmdata
, &modified_blocks
);
706 runCompletionCallbacks(pos
, action
, bedata
.callbacks
);
709 modified_blocks
[pos
] = block
;
711 if (!modified_blocks
.empty())
712 m_server
->SetBlocksNotSent(modified_blocks
);
714 } catch (VersionMismatchException
&e
) {
715 std::ostringstream err
;
716 err
<< "World data version mismatch in MapBlock " << PP(pos
) << std::endl
717 << "----" << std::endl
718 << "\"" << e
.what() << "\"" << std::endl
719 << "See debug.txt." << std::endl
720 << "World probably saved by a newer version of " PROJECT_NAME_C
"."
722 m_server
->setAsyncFatalError(err
.str());
723 } catch (SerializationError
&e
) {
724 std::ostringstream err
;
725 err
<< "Invalid data in MapBlock " << PP(pos
) << std::endl
726 << "----" << std::endl
727 << "\"" << e
.what() << "\"" << std::endl
728 << "See debug.txt." << std::endl
729 << "You can ignore this using [ignore_world_load_errors = true]."
731 m_server
->setAsyncFatalError(err
.str());
734 END_DEBUG_EXCEPTION_HANDLER