]> git.lizzy.rs Git - minetest.git/blob - src/emerge.cpp
Add emerge.cpp, initial EmergeThread changes
[minetest.git] / src / emerge.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20
21 #include "server.h"
22 #include <iostream>
23 #include <queue>
24 #include "clientserver.h"
25 #include "map.h"
26 #include "jmutexautolock.h"
27 #include "main.h"
28 #include "constants.h"
29 #include "voxel.h"
30 #include "config.h"
31 #include "mapblock.h"
32 #include "serverobject.h"
33 #include "settings.h"
34 #include "script.h"
35 #include "scriptapi.h"
36 #include "profiler.h"
37 #include "log.h"
38 #include "nodedef.h"
39 #include "biome.h"
40 #include "emerge.h"
41 #include "mapgen_v6.h"
42
43
44 EmergeManager::EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef) {
45         //register built-in mapgens
46         registerMapgen("v6", new MapgenFactoryV6());
47
48         this->biomedef = bdef ? bdef : new BiomeDefManager(gamedef);
49         this->params   = NULL;
50         this->mapgen   = NULL;
51         
52         queuemutex.Init();
53         emergethread = new EmergeThread((Server *)gamedef);
54 }
55
56
57 EmergeManager::~EmergeManager() {
58         emergethread->setRun(false);
59         emergethread->stop();
60         
61         delete emergethread;
62         delete biomedef;
63         delete mapgen;
64         delete params;
65 }
66
67
68 void EmergeManager::initMapgens(MapgenParams *mgparams) {
69         if (mapgen)
70                 return;
71         
72         this->params = mgparams;
73         this->mapgen = getMapgen(); //only one mapgen for now!
74 }
75
76
77 Mapgen *EmergeManager::getMapgen() {
78         if (!mapgen) {
79                 mapgen = createMapgen(params->mg_name, 0, params, this);
80                 if (!mapgen) {
81                         infostream << "EmergeManager: falling back to mapgen v6" << std::endl;
82                         delete params;
83                         params = createMapgenParams("v6");
84                         mapgen = createMapgen("v6", 0, params, this);
85                 }
86         }
87         return mapgen;
88 }
89
90
91 bool EmergeManager::enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate) { ///return false if adding failed, or queue full!
92         u8 flags = 0;
93         
94         if (allow_generate)
95                 flags |= BLOCK_EMERGE_ALLOWGEN;
96
97         //TODO:
98         // add logic to select which emergethread to add it to
99         //  - one with the least queue contents?
100         //  - if a queue is too full, move onto another one
101         //  - use the peer id sometime
102
103         {
104                 JMutexAutoLock queuelock(queuemutex);
105                 
106                 std::map<v3s16, u8>::const_iterator iter = blocks_enqueued.find(p);
107                 if (iter != blocks_enqueued.end()) {
108                         flags |= iter->second;
109                         blocks_enqueued[p] = flags;
110                         return true;
111                 }
112                 
113                 blocks_enqueued.insert(std::make_pair(p, flags));
114                 emergethread->blockqueue.push(p);
115         }
116         emergethread->qevent.signal();
117         
118         return true;
119 }
120
121
122 bool EmergeManager::popBlockEmerge(v3s16 *pos, u8 *flags) {
123         JMutexAutoLock queuelock(queuemutex);
124
125         if (emergethread->blockqueue.empty())
126                 return false;
127         v3s16 p = emergethread->blockqueue.front();
128         emergethread->blockqueue.pop();
129         
130         *pos = p;
131
132         std::map<v3s16, u8>::iterator iter = blocks_enqueued.find(p);
133         if (iter == blocks_enqueued.end()) //uh oh, this isn't right!!!!!!!!!!!!!!!!!!
134                 return false;
135
136         *flags = iter->second;
137         blocks_enqueued.erase(iter);
138         
139         return true;
140 }
141
142
143 int EmergeManager::getGroundLevelAtPoint(v2s16 p) {
144         if (!mapgen)
145                 return 0;
146         return mapgen->getGroundLevelAtPoint(p);
147 }
148
149
150 bool EmergeManager::isBlockUnderground(v3s16 blockpos) {
151         /*
152         v2s16 p = v2s16((blockpos.X * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2,
153                                         (blockpos.Y * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2);
154         int ground_level = getGroundLevelAtPoint(p);
155         return blockpos.Y * (MAP_BLOCKSIZE + 1) <= min(water_level, ground_level);
156         */
157
158         //yuck, but then again, should i bother being accurate?
159         //the height of the nodes in a single block is quite variable
160         return blockpos.Y * (MAP_BLOCKSIZE + 1) <= params->water_level;
161 }
162
163
164 u32 EmergeManager::getBlockSeed(v3s16 p) {
165         return (u32)(params->seed & 0xFFFFFFFF) +
166                 p.Z * 38134234 +
167                 p.Y * 42123 +
168                 p.Y * 23;
169 }
170
171
172 Mapgen *EmergeManager::createMapgen(std::string mgname, int mgid,
173                                                                         MapgenParams *mgparams, EmergeManager *emerge) {
174         std::map<std::string, MapgenFactory *>::const_iterator iter = mglist.find(mgname);
175         if (iter == mglist.end()) {
176                 errorstream << "EmergeManager; mapgen " << mgname <<
177                  " not registered" << std::endl;
178                 return NULL;
179         }
180         
181         MapgenFactory *mgfactory = iter->second;
182         return mgfactory->createMapgen(mgid, mgparams, emerge);
183 }
184
185
186 MapgenParams *EmergeManager::createMapgenParams(std::string mgname) {
187         std::map<std::string, MapgenFactory *>::const_iterator iter = mglist.find(mgname);
188         if (iter == mglist.end()) {
189                 errorstream << "EmergeManager: mapgen " << mgname <<
190                  " not registered" << std::endl;
191                 return NULL;
192         }
193         
194         MapgenFactory *mgfactory = iter->second;
195         return mgfactory->createMapgenParams();
196 }
197
198
199 MapgenParams *EmergeManager::getParamsFromSettings(Settings *settings) {
200         std::string mg_name = settings->get("mg_name");
201         MapgenParams *mgparams = createMapgenParams(mg_name);
202         
203         mgparams->mg_name     = mg_name;
204         mgparams->seed        = settings->getU64(settings == g_settings ? "fixed_map_seed" : "seed");
205         mgparams->water_level = settings->getS16("water_level");
206         mgparams->chunksize   = settings->getS16("chunksize");
207         mgparams->flags       = settings->getS32("mg_flags");
208
209         if (!mgparams->readParams(settings)) {
210                 delete mgparams;
211                 return NULL;
212         }
213         return mgparams;
214 }
215
216
217 void EmergeManager::setParamsToSettings(Settings *settings) {
218         settings->set("mg_name",         params->mg_name);
219         settings->setU64("seed",         params->seed);
220         settings->setS16("water_level",  params->water_level);
221         settings->setS16("chunksize",    params->chunksize);
222         settings->setFlagStr("mg_flags", params->flags, flagdesc_mapgen);
223
224         params->writeParams(settings);
225 }
226
227
228 bool EmergeManager::registerMapgen(std::string mgname, MapgenFactory *mgfactory) {
229         mglist.insert(std::make_pair(mgname, mgfactory));
230         infostream << "EmergeManager: registered mapgen " << mgname << std::endl;
231 }
232
233
234
235 class MapEditEventIgnorer
236 {
237 public:
238         MapEditEventIgnorer(bool *flag):
239                 m_flag(flag)
240         {
241                 if(*m_flag == false)
242                         *m_flag = true;
243                 else
244                         m_flag = NULL;
245         }
246
247         ~MapEditEventIgnorer()
248         {
249                 if(m_flag)
250                 {
251                         assert(*m_flag);
252                         *m_flag = false;
253                 }
254         }
255
256 private:
257         bool *m_flag;
258 };
259
260 class MapEditEventAreaIgnorer
261 {
262 public:
263         MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a):
264                 m_ignorevariable(ignorevariable)
265         {
266                 if(m_ignorevariable->getVolume() == 0)
267                         *m_ignorevariable = a;
268                 else
269                         m_ignorevariable = NULL;
270         }
271
272         ~MapEditEventAreaIgnorer()
273         {
274                 if(m_ignorevariable)
275                 {
276                         assert(m_ignorevariable->getVolume() != 0);
277                         *m_ignorevariable = VoxelArea();
278                 }
279         }
280
281 private:
282         VoxelArea *m_ignorevariable;
283 };
284
285
286 #if 1
287
288 #define EMERGE_DBG_OUT(x) \
289         { if (enable_mapgen_debug_info) \
290         infostream << "EmergeThread: " x << std::endl; }
291
292 bool EmergeThread::getBlockOrStartGen(v3s16 p, MapBlock **b, 
293                                                                         BlockMakeData *data, bool allow_gen) {
294         v2s16 p2d(p.X, p.Z);
295         //envlock: usually takes <=1ms, sometimes 90ms or ~400ms to acquire
296         JMutexAutoLock envlock(m_server->m_env_mutex); 
297         
298         // Load sector if it isn't loaded
299         if (map->getSectorNoGenerateNoEx(p2d) == NULL)
300                 map->loadSectorMeta(p2d);
301
302         // Attempt to load block
303         MapBlock *block = map->getBlockNoCreateNoEx(p);
304         if (!block || block->isDummy() || !block->isGenerated()) {
305                 EMERGE_DBG_OUT("not in memory, attempting to load from disk");
306                 block = map->loadBlock(p);
307         }
308
309         // If could not load and allowed to generate,
310         // start generation inside this same envlock
311         if (allow_gen && (block == NULL || !block->isGenerated())) {
312                 EMERGE_DBG_OUT("generating");
313                 map->initBlockMake(data, p);
314                 return true;
315         }
316         
317         *b = block;
318         return false;
319 }
320
321
322 void *EmergeThread::Thread() {
323         ThreadStarted();
324         log_register_thread("EmergeThread");
325         DSTACK(__FUNCTION_NAME);
326         BEGIN_DEBUG_EXCEPTION_HANDLER
327
328         v3s16 last_tried_pos(-32768,-32768,-32768); // For error output
329         v3s16 p;
330         u8 flags;
331         
332         map    = (ServerMap *)&(m_server->m_env->getMap());
333         emerge = m_server->m_emerge;
334         mapgen = emerge->getMapgen();
335         
336         while (getRun())
337         try {
338                 while (!emerge->popBlockEmerge(&p, &flags))
339                         qevent.wait();
340
341                 last_tried_pos = p;
342                 if (blockpos_over_limit(p))
343                         continue;
344
345                 bool allow_generate = flags & BLOCK_EMERGE_ALLOWGEN;
346                 EMERGE_DBG_OUT("p=" PP(p) " allow_generate=" << allow_generate);
347                 
348                 /*
349                         Try to fetch block from memory or disk.
350                         If not found and asked to generate, initialize generator.
351                 */
352                 BlockMakeData data;
353                 MapBlock *block = NULL;
354                 core::map<v3s16, MapBlock *> modified_blocks;
355                 
356                 if (getBlockOrStartGen(p, &block, &data, allow_generate)) {
357                         {
358                                 ScopeProfiler sp(g_profiler, "EmergeThread: Mapgen::makeChunk", SPT_AVG);
359                                 TimeTaker t("mapgen::make_block()");
360
361                                 mapgen->makeChunk(&data);
362
363                                 if (enable_mapgen_debug_info == false)
364                                         t.stop(true); // Hide output
365                         }
366
367                         {
368                                 //envlock: usually 0ms, but can take either 30 or 400ms to acquire
369                                 JMutexAutoLock envlock(m_server->m_env_mutex); 
370                                 ScopeProfiler sp(g_profiler, "EmergeThread: after "
371                                                 "mapgen::make_block (envlock)", SPT_AVG);
372
373                                 map->finishBlockMake(&data, modified_blocks);
374                                 
375                                 block = map->getBlockNoCreateNoEx(p);
376                                 if (block) {
377                                         /*
378                                                 Do some post-generate stuff
379                                         */
380                                         v3s16 minp = data.blockpos_min * MAP_BLOCKSIZE;
381                                         v3s16 maxp = data.blockpos_max * MAP_BLOCKSIZE +
382                                                                  v3s16(1,1,1) * (MAP_BLOCKSIZE - 1);
383
384                                         // Ignore map edit events, they will not need to be sent
385                                         // to anybody because the block hasn't been sent to anybody
386                                         MapEditEventAreaIgnorer 
387                                                 ign(&m_server->m_ignore_map_edit_events_area,
388                                                 VoxelArea(minp, maxp));
389                                         {  // takes about 90ms with -O1 on an e3-1230v2
390                                                 scriptapi_environment_on_generated(m_server->m_lua,
391                                                                 minp, maxp, emerge->getBlockSeed(minp));
392                                         }
393
394                                         EMERGE_DBG_OUT("ended up with: " << analyze_block(block));
395                                         
396                                         m_server->m_env->activateBlock(block, 0);
397                                 }
398                         }
399                 }
400
401                 /*
402                         Set sent status of modified blocks on clients
403                 */
404
405                 // NOTE: Server's clients are also behind the connection mutex
406                 //conlock: consistently takes 30-40ms to acquire
407                 JMutexAutoLock lock(m_server->m_con_mutex);
408                 // Add the originally fetched block to the modified list
409                 if (block)
410                         modified_blocks.insert(p, block);
411
412                 // Set the modified blocks unsent for all the clients
413                 for (core::map<u16, RemoteClient*>::Iterator
414                          i = m_server->m_clients.getIterator();
415                          i.atEnd() == false; i++) {
416                         RemoteClient *client = i.getNode()->getValue();
417                         if (modified_blocks.size() > 0) {
418                                 // Remove block from sent history
419                                 client->SetBlocksNotSent(modified_blocks);
420                         }
421                 }
422         }
423         catch (VersionMismatchException &e) {
424                 std::ostringstream err;
425                 err << "World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl;
426                 err << "----"<<std::endl;
427                 err << "\""<<e.what()<<"\""<<std::endl;
428                 err << "See debug.txt."<<std::endl;
429                 err << "World probably saved by a newer version of Minetest."<<std::endl;
430                 m_server->setAsyncFatalError(err.str());
431         }
432         catch (SerializationError &e) {
433                 std::ostringstream err;
434                 err << "Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl;
435                 err << "----"<<std::endl;
436                 err << "\""<<e.what()<<"\""<<std::endl;
437                 err << "See debug.txt."<<std::endl;
438                 err << "You can ignore this using [ignore_world_load_errors = true]."<<std::endl;
439                 m_server->setAsyncFatalError(err.str());
440         }
441         
442         END_DEBUG_EXCEPTION_HANDLER(errorstream)
443         log_deregister_thread();
444         return NULL;
445 }
446
447 #else
448
449 void *EmergeThread::Thread() {
450         ThreadStarted();
451         log_register_thread("EmergeThread");
452         DSTACK(__FUNCTION_NAME);
453         BEGIN_DEBUG_EXCEPTION_HANDLER
454
455         bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
456
457         v3s16 last_tried_pos(-32768,-32768,-32768); // For error output
458         ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
459         EmergeManager *emerge = m_server->m_emerge;
460         Mapgen *mapgen = emerge->getMapgen();
461
462         while(getRun())
463         try {
464                 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
465                 if(qptr == NULL)
466                         break;
467                 SharedPtr<QueuedBlockEmerge> q(qptr);
468
469                 v3s16 &p = q->pos;
470                 v2s16 p2d(p.X,p.Z);
471
472                 last_tried_pos = p;
473
474                 /*
475                         Do not generate over-limit
476                 */
477                 if (blockpos_over_limit(p))
478                         continue;
479
480                 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
481
482                 //TimeTaker timer("block emerge");
483
484                 /*
485                         Try to emerge it from somewhere.
486
487                         If it is only wanted as optional, only loading from disk
488                         will be allowed.
489                 */
490
491                 /*
492                         Check if any peer wants it as non-optional. In that case it
493                         will be generated.
494
495                         Also decrement the emerge queue count in clients.
496                 */
497
498                 bool only_from_disk = true;
499                 {
500                         core::map<u16, u8>::Iterator i;
501                         for (i=q->s.getIterator(); !i.atEnd(); i++) {
502                                 u8 flags = i.getNode()->getValue();
503                                 if (!(flags & BLOCK_EMERGE_FLAG_FROMDISK)) {
504                                         only_from_disk = false;
505                                         break;
506                                 }
507                         }
508                 }
509
510                 if (enable_mapgen_debug_info)
511                         infostream<<"EmergeThread: p="
512                                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
513                                         <<"only_from_disk="<<only_from_disk<<std::endl;
514                                         
515                 MapBlock *block = NULL;
516                 bool got_block = true;
517                 core::map<v3s16, MapBlock*> modified_blocks;
518
519                 /*
520                         Try to fetch block from memory or disk.
521                         If not found and asked to generate, initialize generator.
522                 */
523
524                 bool started_generate = false;
525                 BlockMakeData data;
526                 {
527                         JMutexAutoLock envlock(m_server->m_env_mutex);
528                         
529                         // Load sector if it isn't loaded
530                         if(map.getSectorNoGenerateNoEx(p2d) == NULL)
531                                 map.loadSectorMeta(p2d);
532
533                         // Attempt to load block
534                         block = map.getBlockNoCreateNoEx(p);
535                         if(!block || block->isDummy() || !block->isGenerated()) {
536                                 if(enable_mapgen_debug_info)
537                                         infostream<<"EmergeThread: not in memory, "
538                                                         <<"attempting to load from disk"<<std::endl;
539
540                                 block = map.loadBlock(p);
541                         }
542
543                         // If could not load and allowed to generate, start generation
544                         // inside this same envlock
545                         if(only_from_disk == false &&
546                                         (block == NULL || block->isGenerated() == false)){
547                                 if(enable_mapgen_debug_info)
548                                         infostream<<"EmergeThread: generating"<<std::endl;
549                                 started_generate = true;
550
551                                 map.initBlockMake(&data, p);
552                         }
553                 }
554
555                 /*
556                         If generator was initialized, generate now when envlock is free.
557                 */
558                 if(started_generate) {
559                         {
560                                 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
561                                                 SPT_AVG);
562                                 TimeTaker t("mapgen::make_block()");
563
564                                 mapgen->makeChunk(&data);
565
566                                 if (enable_mapgen_debug_info == false)
567                                         t.stop(true); // Hide output
568                         }
569
570                         do{ // enable break
571                                 // Lock environment again to access the map
572                                 JMutexAutoLock envlock(m_server->m_env_mutex);
573
574                                 ScopeProfiler sp(g_profiler, "EmergeThread: after "
575                                                 "mapgen::make_block (envlock)", SPT_AVG);
576
577                                 // Blit data back on map, update lighting, add mobs and
578                                 // whatever this does
579                                 map.finishBlockMake(&data, modified_blocks);
580                                 
581                                 // Get central block
582                                 block = map.getBlockNoCreateNoEx(p);
583
584                                 // If block doesn't exist, don't try doing anything with it
585                                 // This happens if the block is not in generation boundaries
586                                 if(!block)
587                                         break;
588
589                                 /*
590                                         Do some post-generate stuff
591                                 */
592                                 v3s16 minp = data.blockpos_min * MAP_BLOCKSIZE;
593                                 v3s16 maxp = data.blockpos_max * MAP_BLOCKSIZE +
594                                                 v3s16(1,1,1) * (MAP_BLOCKSIZE - 1);
595
596                                 /*
597                                         Ignore map edit events, they will not need to be
598                                         sent to anybody because the block hasn't been sent
599                                         to anybody
600                                 */
601                                 MapEditEventAreaIgnorer ign(
602                                                 &m_server->m_ignore_map_edit_events_area,
603                                                 VoxelArea(minp, maxp));
604                                 {
605                                         TimeTaker timer("on_generated");
606                                         scriptapi_environment_on_generated(m_server->m_lua,
607                                                         minp, maxp, emerge->getBlockSeed(minp));
608                                         //int t = timer.stop(true);
609                                         //dstream<<"on_generated took "<<t<<"ms"<<std::endl;
610                                 }
611
612                                 if (enable_mapgen_debug_info)
613                                         infostream << "EmergeThread: ended up with: "
614                                                         << analyze_block(block) << std::endl;
615
616                                 // Activate objects and stuff
617                                 m_server->m_env->activateBlock(block, 0);
618                         }while(false);
619                 }
620
621                 if(block == NULL)
622                         got_block = false;
623
624                 /*
625                         Set sent status of modified blocks on clients
626                 */
627
628                 // NOTE: Server's clients are also behind the connection mutex
629                 JMutexAutoLock lock(m_server->m_con_mutex);
630
631                 /*
632                         Add the originally fetched block to the modified list
633                 */
634                 if(got_block)
635                         modified_blocks.insert(p, block);
636
637                 /*
638                         Set the modified blocks unsent for all the clients
639                 */
640                 for(core::map<u16, RemoteClient*>::Iterator
641                                 i = m_server->m_clients.getIterator();
642                                 i.atEnd() == false; i++) {
643                         RemoteClient *client = i.getNode()->getValue();
644                         if(modified_blocks.size() > 0) {
645                                 // Remove block from sent history
646                                 client->SetBlocksNotSent(modified_blocks);
647                         }
648                 }
649                                                         
650
651 niters++;
652         }
653         catch (VersionMismatchException &e) {
654                 std::ostringstream err;
655                 err << "World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl;
656                 err << "----"<<std::endl;
657                 err << "\""<<e.what()<<"\""<<std::endl;
658                 err << "See debug.txt."<<std::endl;
659                 err << "World probably saved by a newer version of Minetest."<<std::endl;
660                 m_server->setAsyncFatalError(err.str());
661         }
662         catch (SerializationError &e) {
663                 std::ostringstream err;
664                 err << "Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl;
665                 err << "----"<<std::endl;
666                 err << "\""<<e.what()<<"\""<<std::endl;
667                 err << "See debug.txt."<<std::endl;
668                 err << "You can ignore this using [ignore_world_load_errors = true]."<<std::endl;
669                 m_server->setAsyncFatalError(err.str());
670         }
671 printf("emergethread iterated %d times\n", niters);
672         END_DEBUG_EXCEPTION_HANDLER(errorstream)
673         log_deregister_thread();
674         return NULL;
675 }
676
677 #endif