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