Line data Source code
1 : /*
2 : Minetest
3 : Copyright (C) 2010-2013 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 : #include "map.h"
21 : #include "mapsector.h"
22 : #include "mapblock.h"
23 : #include "filesys.h"
24 : #include "voxel.h"
25 : #include "porting.h"
26 : #include "serialization.h"
27 : #include "nodemetadata.h"
28 : #include "settings.h"
29 : #include "log.h"
30 : #include "profiler.h"
31 : #include "nodedef.h"
32 : #include "gamedef.h"
33 : #include "util/directiontables.h"
34 : #include "util/mathconstants.h"
35 : #include "rollback_interface.h"
36 : #include "environment.h"
37 : #include "emerge.h"
38 : #include "mapgen_v6.h"
39 : #include "mg_biome.h"
40 : #include "config.h"
41 : #include "server.h"
42 : #include "database.h"
43 : #include "database-dummy.h"
44 : #include "database-sqlite3.h"
45 : #include <deque>
46 : #if USE_LEVELDB
47 : #include "database-leveldb.h"
48 : #endif
49 : #if USE_REDIS
50 : #include "database-redis.h"
51 : #endif
52 :
53 : #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
54 :
55 :
56 : /*
57 : Map
58 : */
59 :
60 1 : Map::Map(std::ostream &dout, IGameDef *gamedef):
61 : m_dout(dout),
62 : m_gamedef(gamedef),
63 : m_sector_cache(NULL),
64 : m_transforming_liquid_loop_count_multiplier(1.0f),
65 : m_unprocessed_count(0),
66 : m_inc_trending_up_start_time(0),
67 1 : m_queue_size_timer_started(false)
68 : {
69 1 : }
70 :
71 2 : Map::~Map()
72 : {
73 : /*
74 : Free all MapSectors
75 : */
76 735 : for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
77 490 : i != m_sectors.end(); ++i)
78 : {
79 244 : delete i->second;
80 : }
81 1 : }
82 :
83 0 : void Map::addEventReceiver(MapEventReceiver *event_receiver)
84 : {
85 0 : m_event_receivers.insert(event_receiver);
86 0 : }
87 :
88 0 : void Map::removeEventReceiver(MapEventReceiver *event_receiver)
89 : {
90 0 : m_event_receivers.erase(event_receiver);
91 0 : }
92 :
93 0 : void Map::dispatchEvent(MapEditEvent *event)
94 : {
95 0 : for(std::set<MapEventReceiver*>::iterator
96 0 : i = m_event_receivers.begin();
97 0 : i != m_event_receivers.end(); ++i)
98 : {
99 0 : (*i)->onMapEditEvent(event);
100 : }
101 0 : }
102 :
103 2350001 : MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
104 : {
105 2350001 : if(m_sector_cache != NULL && p == m_sector_cache_p){
106 1822174 : MapSector * sector = m_sector_cache;
107 1822174 : return sector;
108 : }
109 :
110 527827 : std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
111 :
112 527827 : if(n == m_sectors.end())
113 52096 : return NULL;
114 :
115 475731 : MapSector *sector = n->second;
116 :
117 : // Cache the last result
118 475731 : m_sector_cache_p = p;
119 475731 : m_sector_cache = sector;
120 :
121 475731 : return sector;
122 : }
123 :
124 2350001 : MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
125 : {
126 2350001 : return getSectorNoGenerateNoExNoLock(p);
127 : }
128 :
129 786 : MapSector * Map::getSectorNoGenerate(v2s16 p)
130 : {
131 786 : MapSector *sector = getSectorNoGenerateNoEx(p);
132 786 : if(sector == NULL)
133 244 : throw InvalidPositionException();
134 :
135 542 : return sector;
136 : }
137 :
138 2349215 : MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
139 : {
140 2349215 : v2s16 p2d(p3d.X, p3d.Z);
141 2349215 : MapSector * sector = getSectorNoGenerateNoEx(p2d);
142 2349215 : if(sector == NULL)
143 51852 : return NULL;
144 2297363 : MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
145 2297363 : return block;
146 : }
147 :
148 7988 : MapBlock * Map::getBlockNoCreate(v3s16 p3d)
149 : {
150 7988 : MapBlock *block = getBlockNoCreateNoEx(p3d);
151 7988 : if(block == NULL)
152 0 : throw InvalidPositionException();
153 7988 : return block;
154 : }
155 :
156 0 : bool Map::isNodeUnderground(v3s16 p)
157 : {
158 0 : v3s16 blockpos = getNodeBlockPos(p);
159 : try{
160 0 : MapBlock * block = getBlockNoCreate(blockpos);
161 0 : return block->getIsUnderground();
162 : }
163 0 : catch(InvalidPositionException &e)
164 : {
165 0 : return false;
166 : }
167 : }
168 :
169 0 : bool Map::isValidPosition(v3s16 p)
170 : {
171 0 : v3s16 blockpos = getNodeBlockPos(p);
172 0 : MapBlock *block = getBlockNoCreate(blockpos);
173 0 : return (block != NULL);
174 : }
175 :
176 : // Returns a CONTENT_IGNORE node if not found
177 2239998 : MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
178 : {
179 2239998 : v3s16 blockpos = getNodeBlockPos(p);
180 2239998 : MapBlock *block = getBlockNoCreateNoEx(blockpos);
181 2239998 : if (block == NULL) {
182 139569 : if (is_valid_position != NULL)
183 87061 : *is_valid_position = false;
184 139569 : return MapNode(CONTENT_IGNORE);
185 : }
186 :
187 2100429 : v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
188 : bool is_valid_p;
189 2100429 : MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
190 2100429 : if (is_valid_position != NULL)
191 1078063 : *is_valid_position = is_valid_p;
192 2100429 : return node;
193 : }
194 :
195 : #if 0
196 : // Deprecated
197 : // throws InvalidPositionException if not found
198 : // TODO: Now this is deprecated, getNodeNoEx should be renamed
199 : MapNode Map::getNode(v3s16 p)
200 : {
201 : v3s16 blockpos = getNodeBlockPos(p);
202 : MapBlock *block = getBlockNoCreateNoEx(blockpos);
203 : if (block == NULL)
204 : throw InvalidPositionException();
205 : v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
206 : bool is_valid_position;
207 : MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
208 : if (!is_valid_position)
209 : throw InvalidPositionException();
210 : return node;
211 : }
212 : #endif
213 :
214 : // throws InvalidPositionException if not found
215 56 : void Map::setNode(v3s16 p, MapNode & n)
216 : {
217 56 : v3s16 blockpos = getNodeBlockPos(p);
218 56 : MapBlock *block = getBlockNoCreate(blockpos);
219 56 : v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
220 : // Never allow placing CONTENT_IGNORE, it fucks up stuff
221 56 : if(n.getContent() == CONTENT_IGNORE){
222 : bool temp_bool;
223 0 : errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
224 0 : <<" while trying to replace \""
225 0 : <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name
226 0 : <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
227 0 : debug_stacks_print_to(infostream);
228 0 : return;
229 : }
230 56 : block->setNodeNoCheck(relpos, n);
231 : }
232 :
233 :
234 : /*
235 : Goes recursively through the neighbours of the node.
236 :
237 : Alters only transparent nodes.
238 :
239 : If the lighting of the neighbour is lower than the lighting of
240 : the node was (before changing it to 0 at the step before), the
241 : lighting of the neighbour is set to 0 and then the same stuff
242 : repeats for the neighbour.
243 :
244 : The ending nodes of the routine are stored in light_sources.
245 : This is useful when a light is removed. In such case, this
246 : routine can be called for the light node and then again for
247 : light_sources to re-light the area without the removed light.
248 :
249 : values of from_nodes are lighting values.
250 : */
251 240 : void Map::unspreadLight(enum LightBank bank,
252 : std::map<v3s16, u8> & from_nodes,
253 : std::set<v3s16> & light_sources,
254 : std::map<v3s16, MapBlock*> & modified_blocks)
255 : {
256 240 : INodeDefManager *nodemgr = m_gamedef->ndef();
257 :
258 : v3s16 dirs[6] = {
259 : v3s16(0,0,1), // back
260 : v3s16(0,1,0), // top
261 : v3s16(1,0,0), // right
262 : v3s16(0,0,-1), // front
263 : v3s16(0,-1,0), // bottom
264 : v3s16(-1,0,0), // left
265 240 : };
266 :
267 240 : if(from_nodes.empty())
268 0 : return;
269 :
270 240 : u32 blockchangecount = 0;
271 :
272 480 : std::map<v3s16, u8> unlighted_nodes;
273 :
274 : /*
275 : Initialize block cache
276 : */
277 240 : v3s16 blockpos_last;
278 240 : MapBlock *block = NULL;
279 : // Cache this a bit, too
280 240 : bool block_checked_in_modified = false;
281 :
282 2322 : for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
283 1548 : j != from_nodes.end(); ++j)
284 : {
285 534 : v3s16 pos = j->first;
286 534 : v3s16 blockpos = getNodeBlockPos(pos);
287 :
288 : // Only fetch a new block if the block position has changed
289 : try{
290 534 : if(block == NULL || blockpos != blockpos_last){
291 442 : block = getBlockNoCreate(blockpos);
292 442 : blockpos_last = blockpos;
293 :
294 442 : block_checked_in_modified = false;
295 442 : blockchangecount++;
296 : }
297 : }
298 0 : catch(InvalidPositionException &e)
299 : {
300 0 : continue;
301 : }
302 :
303 534 : if(block->isDummy())
304 0 : continue;
305 :
306 : // Calculate relative position in block
307 : //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
308 :
309 : // Get node straight from the block
310 : //MapNode n = block->getNode(relpos);
311 :
312 534 : u8 oldlight = j->second;
313 :
314 : // Loop through 6 neighbors
315 3738 : for(u16 i=0; i<6; i++)
316 : {
317 : // Get the position of the neighbor node
318 3204 : v3s16 n2pos = pos + dirs[i];
319 :
320 : // Get the block where the node is located
321 3204 : v3s16 blockpos, relpos;
322 3204 : getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
323 :
324 : // Only fetch a new block if the block position has changed
325 : try {
326 3204 : if(block == NULL || blockpos != blockpos_last){
327 1162 : block = getBlockNoCreate(blockpos);
328 1162 : blockpos_last = blockpos;
329 :
330 1162 : block_checked_in_modified = false;
331 1162 : blockchangecount++;
332 : }
333 : }
334 0 : catch(InvalidPositionException &e) {
335 0 : continue;
336 : }
337 :
338 : // Get node straight from the block
339 : bool is_valid_position;
340 3204 : MapNode n2 = block->getNode(relpos, &is_valid_position);
341 3204 : if (!is_valid_position)
342 0 : continue;
343 :
344 3204 : bool changed = false;
345 :
346 : //TODO: Optimize output by optimizing light_sources?
347 :
348 : /*
349 : If the neighbor is dimmer than what was specified
350 : as oldlight (the light of the previous node)
351 : */
352 3204 : if(n2.getLight(bank, nodemgr) < oldlight)
353 : {
354 : /*
355 : And the neighbor is transparent and it has some light
356 : */
357 4162 : if(nodemgr->get(n2).light_propagates
358 2081 : && n2.getLight(bank, nodemgr) != 0)
359 : {
360 : /*
361 : Set light to 0 and add to queue
362 : */
363 :
364 424 : u8 current_light = n2.getLight(bank, nodemgr);
365 424 : n2.setLight(bank, 0, nodemgr);
366 424 : block->setNode(relpos, n2);
367 :
368 424 : unlighted_nodes[n2pos] = current_light;
369 424 : changed = true;
370 :
371 : /*
372 : Remove from light_sources if it is there
373 : NOTE: This doesn't happen nearly at all
374 : */
375 : /*if(light_sources.find(n2pos))
376 : {
377 : infostream<<"Removed from light_sources"<<std::endl;
378 : light_sources.remove(n2pos);
379 : }*/
380 : }
381 :
382 : /*// DEBUG
383 : if(light_sources.find(n2pos) != NULL)
384 : light_sources.remove(n2pos);*/
385 : }
386 : else{
387 1123 : light_sources.insert(n2pos);
388 : }
389 :
390 : // Add to modified_blocks
391 3204 : if(changed == true && block_checked_in_modified == false)
392 : {
393 : // If the block is not found in modified_blocks, add.
394 378 : if(modified_blocks.find(blockpos) == modified_blocks.end())
395 : {
396 33 : modified_blocks[blockpos] = block;
397 : }
398 378 : block_checked_in_modified = true;
399 : }
400 : }
401 : }
402 :
403 : /*infostream<<"unspreadLight(): Changed block "
404 : <<blockchangecount<<" times"
405 : <<" for "<<from_nodes.size()<<" nodes"
406 : <<std::endl;*/
407 :
408 240 : if(!unlighted_nodes.empty())
409 130 : unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
410 : }
411 :
412 : /*
413 : A single-node wrapper of the above
414 : */
415 110 : void Map::unLightNeighbors(enum LightBank bank,
416 : v3s16 pos, u8 lightwas,
417 : std::set<v3s16> & light_sources,
418 : std::map<v3s16, MapBlock*> & modified_blocks)
419 : {
420 220 : std::map<v3s16, u8> from_nodes;
421 110 : from_nodes[pos] = lightwas;
422 :
423 110 : unspreadLight(bank, from_nodes, light_sources, modified_blocks);
424 110 : }
425 :
426 : /*
427 : Lights neighbors of from_nodes, collects all them and then
428 : goes on recursively.
429 : */
430 306 : void Map::spreadLight(enum LightBank bank,
431 : std::set<v3s16> & from_nodes,
432 : std::map<v3s16, MapBlock*> & modified_blocks)
433 : {
434 306 : INodeDefManager *nodemgr = m_gamedef->ndef();
435 :
436 : const v3s16 dirs[6] = {
437 : v3s16(0,0,1), // back
438 : v3s16(0,1,0), // top
439 : v3s16(1,0,0), // right
440 : v3s16(0,0,-1), // front
441 : v3s16(0,-1,0), // bottom
442 : v3s16(-1,0,0), // left
443 306 : };
444 :
445 306 : if(from_nodes.empty())
446 0 : return;
447 :
448 306 : u32 blockchangecount = 0;
449 :
450 612 : std::set<v3s16> lighted_nodes;
451 :
452 : /*
453 : Initialize block cache
454 : */
455 306 : v3s16 blockpos_last;
456 306 : MapBlock *block = NULL;
457 : // Cache this a bit, too
458 306 : bool block_checked_in_modified = false;
459 :
460 9744 : for(std::set<v3s16>::iterator j = from_nodes.begin();
461 6496 : j != from_nodes.end(); ++j)
462 : {
463 2942 : v3s16 pos = *j;
464 2942 : v3s16 blockpos, relpos;
465 :
466 2942 : getNodeBlockPosWithOffset(pos, blockpos, relpos);
467 :
468 : // Only fetch a new block if the block position has changed
469 : try {
470 2942 : if(block == NULL || blockpos != blockpos_last){
471 1242 : block = getBlockNoCreate(blockpos);
472 1242 : blockpos_last = blockpos;
473 :
474 1242 : block_checked_in_modified = false;
475 1242 : blockchangecount++;
476 : }
477 : }
478 0 : catch(InvalidPositionException &e) {
479 0 : continue;
480 : }
481 :
482 2942 : if(block->isDummy())
483 0 : continue;
484 :
485 : // Get node straight from the block
486 : bool is_valid_position;
487 2942 : MapNode n = block->getNode(relpos, &is_valid_position);
488 :
489 2942 : u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
490 2942 : u8 newlight = diminish_light(oldlight);
491 :
492 : // Loop through 6 neighbors
493 20594 : for(u16 i=0; i<6; i++){
494 : // Get the position of the neighbor node
495 17652 : v3s16 n2pos = pos + dirs[i];
496 :
497 : // Get the block where the node is located
498 17652 : v3s16 blockpos, relpos;
499 17652 : getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
500 :
501 : // Only fetch a new block if the block position has changed
502 : try {
503 17652 : if(block == NULL || blockpos != blockpos_last){
504 4972 : block = getBlockNoCreate(blockpos);
505 4972 : blockpos_last = blockpos;
506 :
507 4972 : block_checked_in_modified = false;
508 4972 : blockchangecount++;
509 : }
510 : }
511 0 : catch(InvalidPositionException &e) {
512 0 : continue;
513 : }
514 :
515 : // Get node straight from the block
516 17652 : MapNode n2 = block->getNode(relpos, &is_valid_position);
517 17652 : if (!is_valid_position)
518 0 : continue;
519 :
520 17652 : bool changed = false;
521 : /*
522 : If the neighbor is brighter than the current node,
523 : add to list (it will light up this node on its turn)
524 : */
525 17652 : if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
526 : {
527 776 : lighted_nodes.insert(n2pos);
528 776 : changed = true;
529 : }
530 : /*
531 : If the neighbor is dimmer than how much light this node
532 : would spread on it, add to list
533 : */
534 17652 : if(n2.getLight(bank, nodemgr) < newlight)
535 : {
536 2968 : if(nodemgr->get(n2).light_propagates)
537 : {
538 532 : n2.setLight(bank, newlight, nodemgr);
539 532 : block->setNode(relpos, n2);
540 532 : lighted_nodes.insert(n2pos);
541 532 : changed = true;
542 : }
543 : }
544 :
545 : // Add to modified_blocks
546 17652 : if(changed == true && block_checked_in_modified == false)
547 : {
548 : // If the block is not found in modified_blocks, add.
549 854 : if(modified_blocks.find(blockpos) == modified_blocks.end())
550 : {
551 26 : modified_blocks[blockpos] = block;
552 : }
553 854 : block_checked_in_modified = true;
554 : }
555 : }
556 : }
557 :
558 : /*infostream<<"spreadLight(): Changed block "
559 : <<blockchangecount<<" times"
560 : <<" for "<<from_nodes.size()<<" nodes"
561 : <<std::endl;*/
562 :
563 306 : if(!lighted_nodes.empty())
564 179 : spreadLight(bank, lighted_nodes, modified_blocks);
565 : }
566 :
567 : /*
568 : A single-node source variation of the above.
569 : */
570 17 : void Map::lightNeighbors(enum LightBank bank,
571 : v3s16 pos,
572 : std::map<v3s16, MapBlock*> & modified_blocks)
573 : {
574 34 : std::set<v3s16> from_nodes;
575 17 : from_nodes.insert(pos);
576 17 : spreadLight(bank, from_nodes, modified_blocks);
577 17 : }
578 :
579 12 : v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
580 : {
581 12 : INodeDefManager *nodemgr = m_gamedef->ndef();
582 :
583 : v3s16 dirs[6] = {
584 : v3s16(0,0,1), // back
585 : v3s16(0,1,0), // top
586 : v3s16(1,0,0), // right
587 : v3s16(0,0,-1), // front
588 : v3s16(0,-1,0), // bottom
589 : v3s16(-1,0,0), // left
590 12 : };
591 :
592 12 : u8 brightest_light = 0;
593 12 : v3s16 brightest_pos(0,0,0);
594 12 : bool found_something = false;
595 :
596 : // Loop through 6 neighbors
597 84 : for(u16 i=0; i<6; i++){
598 : // Get the position of the neighbor node
599 72 : v3s16 n2pos = p + dirs[i];
600 72 : MapNode n2;
601 : bool is_valid_position;
602 72 : n2 = getNodeNoEx(n2pos, &is_valid_position);
603 72 : if (!is_valid_position)
604 0 : continue;
605 :
606 72 : if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
607 17 : brightest_light = n2.getLight(bank, nodemgr);
608 17 : brightest_pos = n2pos;
609 17 : found_something = true;
610 : }
611 : }
612 :
613 12 : if(found_something == false)
614 0 : throw InvalidPositionException();
615 :
616 12 : return brightest_pos;
617 : }
618 :
619 : /*
620 : Propagates sunlight down from a node.
621 : Starting point gets sunlight.
622 :
623 : Returns the lowest y value of where the sunlight went.
624 :
625 : Mud is turned into grass in where the sunlight stops.
626 : */
627 5 : s16 Map::propagateSunlight(v3s16 start,
628 : std::map<v3s16, MapBlock*> & modified_blocks)
629 : {
630 5 : INodeDefManager *nodemgr = m_gamedef->ndef();
631 :
632 5 : s16 y = start.Y;
633 5 : for(; ; y--)
634 : {
635 10 : v3s16 pos(start.X, y, start.Z);
636 :
637 10 : v3s16 blockpos = getNodeBlockPos(pos);
638 : MapBlock *block;
639 : try{
640 10 : block = getBlockNoCreate(blockpos);
641 : }
642 0 : catch(InvalidPositionException &e)
643 : {
644 0 : break;
645 : }
646 :
647 10 : v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
648 : bool is_valid_position;
649 10 : MapNode n = block->getNode(relpos, &is_valid_position);
650 10 : if (!is_valid_position)
651 0 : break;
652 :
653 10 : if(nodemgr->get(n).sunlight_propagates)
654 : {
655 5 : n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
656 5 : block->setNode(relpos, n);
657 :
658 5 : modified_blocks[blockpos] = block;
659 : }
660 : else
661 : {
662 : // Sunlight goes no further
663 5 : break;
664 : }
665 : }
666 5 : return y + 1;
667 : }
668 :
669 0 : void Map::updateLighting(enum LightBank bank,
670 : std::map<v3s16, MapBlock*> & a_blocks,
671 : std::map<v3s16, MapBlock*> & modified_blocks)
672 : {
673 0 : INodeDefManager *nodemgr = m_gamedef->ndef();
674 :
675 : /*m_dout<<DTIME<<"Map::updateLighting(): "
676 : <<a_blocks.size()<<" blocks."<<std::endl;*/
677 :
678 : //TimeTaker timer("updateLighting");
679 :
680 : // For debugging
681 : //bool debug=true;
682 : //u32 count_was = modified_blocks.size();
683 :
684 : //std::map<v3s16, MapBlock*> blocks_to_update;
685 :
686 0 : std::set<v3s16> light_sources;
687 :
688 0 : std::map<v3s16, u8> unlight_from;
689 :
690 0 : int num_bottom_invalid = 0;
691 :
692 : {
693 : //TimeTaker t("first stuff");
694 :
695 0 : for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
696 0 : i != a_blocks.end(); ++i)
697 : {
698 0 : MapBlock *block = i->second;
699 :
700 0 : for(;;)
701 : {
702 : // Don't bother with dummy blocks.
703 0 : if(block->isDummy())
704 0 : break;
705 :
706 0 : v3s16 pos = block->getPos();
707 0 : v3s16 posnodes = block->getPosRelative();
708 0 : modified_blocks[pos] = block;
709 : //blocks_to_update[pos] = block;
710 :
711 : /*
712 : Clear all light from block
713 : */
714 0 : for(s16 z=0; z<MAP_BLOCKSIZE; z++)
715 0 : for(s16 x=0; x<MAP_BLOCKSIZE; x++)
716 0 : for(s16 y=0; y<MAP_BLOCKSIZE; y++)
717 : {
718 0 : v3s16 p(x,y,z);
719 : bool is_valid_position;
720 0 : MapNode n = block->getNode(p, &is_valid_position);
721 0 : if (!is_valid_position) {
722 : /* This would happen when dealing with a
723 : dummy block.
724 : */
725 0 : infostream<<"updateLighting(): InvalidPositionException"
726 0 : <<std::endl;
727 0 : continue;
728 : }
729 0 : u8 oldlight = n.getLight(bank, nodemgr);
730 0 : n.setLight(bank, 0, nodemgr);
731 0 : block->setNode(p, n);
732 :
733 : // If node sources light, add to list
734 0 : u8 source = nodemgr->get(n).light_source;
735 0 : if(source != 0)
736 0 : light_sources.insert(p + posnodes);
737 :
738 : // Collect borders for unlighting
739 0 : if((x==0 || x == MAP_BLOCKSIZE-1
740 0 : || y==0 || y == MAP_BLOCKSIZE-1
741 0 : || z==0 || z == MAP_BLOCKSIZE-1)
742 0 : && oldlight != 0)
743 : {
744 0 : v3s16 p_map = p + posnodes;
745 0 : unlight_from[p_map] = oldlight;
746 : }
747 :
748 :
749 : }
750 :
751 0 : if(bank == LIGHTBANK_DAY)
752 : {
753 0 : bool bottom_valid = block->propagateSunlight(light_sources);
754 :
755 0 : if(!bottom_valid)
756 0 : num_bottom_invalid++;
757 :
758 : // If bottom is valid, we're done.
759 0 : if(bottom_valid)
760 0 : break;
761 : }
762 0 : else if(bank == LIGHTBANK_NIGHT)
763 : {
764 : // For night lighting, sunlight is not propagated
765 0 : break;
766 : }
767 : else
768 : {
769 : assert("Invalid lighting bank" == NULL);
770 : }
771 :
772 : /*infostream<<"Bottom for sunlight-propagated block ("
773 : <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
774 : <<std::endl;*/
775 :
776 : // Bottom sunlight is not valid; get the block and loop to it
777 :
778 0 : pos.Y--;
779 : try{
780 0 : block = getBlockNoCreate(pos);
781 : }
782 0 : catch(InvalidPositionException &e)
783 : {
784 0 : FATAL_ERROR("Invalid position");
785 : }
786 :
787 : }
788 : }
789 :
790 : }
791 :
792 : /*
793 : Enable this to disable proper lighting for speeding up map
794 : generation for testing or whatever
795 : */
796 : #if 0
797 : //if(g_settings->get(""))
798 : {
799 : core::map<v3s16, MapBlock*>::Iterator i;
800 : i = blocks_to_update.getIterator();
801 : for(; i.atEnd() == false; i++)
802 : {
803 : MapBlock *block = i.getNode()->getValue();
804 : v3s16 p = block->getPos();
805 : block->setLightingExpired(false);
806 : }
807 : return;
808 : }
809 : #endif
810 :
811 : #if 1
812 : {
813 : //TimeTaker timer("unspreadLight");
814 0 : unspreadLight(bank, unlight_from, light_sources, modified_blocks);
815 : }
816 :
817 : /*if(debug)
818 : {
819 : u32 diff = modified_blocks.size() - count_was;
820 : count_was = modified_blocks.size();
821 : infostream<<"unspreadLight modified "<<diff<<std::endl;
822 : }*/
823 :
824 : {
825 : //TimeTaker timer("spreadLight");
826 0 : spreadLight(bank, light_sources, modified_blocks);
827 : }
828 :
829 : /*if(debug)
830 : {
831 : u32 diff = modified_blocks.size() - count_was;
832 : count_was = modified_blocks.size();
833 : infostream<<"spreadLight modified "<<diff<<std::endl;
834 : }*/
835 : #endif
836 :
837 : #if 0
838 : {
839 : //MapVoxelManipulator vmanip(this);
840 :
841 : // Make a manual voxel manipulator and load all the blocks
842 : // that touch the requested blocks
843 : ManualMapVoxelManipulator vmanip(this);
844 :
845 : {
846 : //TimeTaker timer("initialEmerge");
847 :
848 : core::map<v3s16, MapBlock*>::Iterator i;
849 : i = blocks_to_update.getIterator();
850 : for(; i.atEnd() == false; i++)
851 : {
852 : MapBlock *block = i.getNode()->getValue();
853 : v3s16 p = block->getPos();
854 :
855 : // Add all surrounding blocks
856 : vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
857 :
858 : /*
859 : Add all surrounding blocks that have up-to-date lighting
860 : NOTE: This doesn't quite do the job (not everything
861 : appropriate is lighted)
862 : */
863 : /*for(s16 z=-1; z<=1; z++)
864 : for(s16 y=-1; y<=1; y++)
865 : for(s16 x=-1; x<=1; x++)
866 : {
867 : v3s16 p2 = p + v3s16(x,y,z);
868 : MapBlock *block = getBlockNoCreateNoEx(p2);
869 : if(block == NULL)
870 : continue;
871 : if(block->isDummy())
872 : continue;
873 : if(block->getLightingExpired())
874 : continue;
875 : vmanip.initialEmerge(p2, p2);
876 : }*/
877 :
878 : // Lighting of block will be updated completely
879 : block->setLightingExpired(false);
880 : }
881 : }
882 :
883 : {
884 : //TimeTaker timer("unSpreadLight");
885 : vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
886 : }
887 : {
888 : //TimeTaker timer("spreadLight");
889 : vmanip.spreadLight(bank, light_sources, nodemgr);
890 : }
891 : {
892 : //TimeTaker timer("blitBack");
893 : vmanip.blitBack(modified_blocks);
894 : }
895 : /*infostream<<"emerge_time="<<emerge_time<<std::endl;
896 : emerge_time = 0;*/
897 : }
898 : #endif
899 :
900 : //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
901 0 : }
902 :
903 0 : void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
904 : std::map<v3s16, MapBlock*> & modified_blocks)
905 : {
906 0 : updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
907 0 : updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
908 :
909 : /*
910 : Update information about whether day and night light differ
911 : */
912 0 : for(std::map<v3s16, MapBlock*>::iterator
913 0 : i = modified_blocks.begin();
914 0 : i != modified_blocks.end(); ++i)
915 : {
916 0 : MapBlock *block = i->second;
917 0 : block->expireDayNightDiff();
918 : }
919 0 : }
920 :
921 : /*
922 : */
923 49 : void Map::addNodeAndUpdate(v3s16 p, MapNode n,
924 : std::map<v3s16, MapBlock*> &modified_blocks,
925 : bool remove_metadata)
926 : {
927 49 : INodeDefManager *ndef = m_gamedef->ndef();
928 :
929 : /*PrintInfo(m_dout);
930 : m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
931 : <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
932 :
933 : /*
934 : From this node to nodes underneath:
935 : If lighting is sunlight (1.0), unlight neighbours and
936 : set lighting to 0.
937 : Else discontinue.
938 : */
939 :
940 49 : v3s16 toppos = p + v3s16(0,1,0);
941 : //v3s16 bottompos = p + v3s16(0,-1,0);
942 :
943 49 : bool node_under_sunlight = true;
944 98 : std::set<v3s16> light_sources;
945 :
946 : /*
947 : Collect old node for rollback
948 : */
949 98 : RollbackNode rollback_oldnode(this, p, m_gamedef);
950 :
951 : /*
952 : If there is a node at top and it doesn't have sunlight,
953 : there has not been any sunlight going down.
954 :
955 : Otherwise there probably is.
956 : */
957 :
958 : bool is_valid_position;
959 49 : MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
960 :
961 49 : if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
962 37 : node_under_sunlight = false;
963 :
964 : /*
965 : Remove all light that has come out of this node
966 : */
967 :
968 : enum LightBank banks[] =
969 : {
970 : LIGHTBANK_DAY,
971 : LIGHTBANK_NIGHT
972 49 : };
973 147 : for(s32 i=0; i<2; i++)
974 : {
975 98 : enum LightBank bank = banks[i];
976 :
977 98 : u8 lightwas = getNodeNoEx(p).getLight(bank, ndef);
978 :
979 : // Add the block of the added node to modified_blocks
980 98 : v3s16 blockpos = getNodeBlockPos(p);
981 98 : MapBlock * block = getBlockNoCreate(blockpos);
982 : assert(block != NULL);
983 98 : modified_blocks[blockpos] = block;
984 :
985 : assert(isValidPosition(p));
986 :
987 : // Unlight neighbours of node.
988 : // This means setting light of all consequent dimmer nodes
989 : // to 0.
990 : // This also collects the nodes at the border which will spread
991 : // light again into this.
992 98 : unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
993 :
994 98 : n.setLight(bank, 0, ndef);
995 : }
996 :
997 : /*
998 : If node lets sunlight through and is under sunlight, it has
999 : sunlight too.
1000 : */
1001 49 : if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1002 : {
1003 0 : n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1004 : }
1005 :
1006 : /*
1007 : Remove node metadata
1008 : */
1009 49 : if (remove_metadata) {
1010 7 : removeNodeMetadata(p);
1011 : }
1012 :
1013 : /*
1014 : Set the node on the map
1015 : */
1016 :
1017 49 : setNode(p, n);
1018 :
1019 : /*
1020 : If node is under sunlight and doesn't let sunlight through,
1021 : take all sunlighted nodes under it and clear light from them
1022 : and from where the light has been spread.
1023 : TODO: This could be optimized by mass-unlighting instead
1024 : of looping
1025 : */
1026 49 : if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1027 : {
1028 12 : s16 y = p.Y - 1;
1029 0 : for(;; y--){
1030 : //m_dout<<DTIME<<"y="<<y<<std::endl;
1031 12 : v3s16 n2pos(p.X, y, p.Z);
1032 :
1033 12 : MapNode n2;
1034 :
1035 12 : n2 = getNodeNoEx(n2pos, &is_valid_position);
1036 12 : if (!is_valid_position)
1037 12 : break;
1038 :
1039 12 : if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1040 : {
1041 0 : unLightNeighbors(LIGHTBANK_DAY,
1042 0 : n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1043 0 : light_sources, modified_blocks);
1044 0 : n2.setLight(LIGHTBANK_DAY, 0, ndef);
1045 0 : setNode(n2pos, n2);
1046 : }
1047 : else
1048 12 : break;
1049 : }
1050 : }
1051 :
1052 147 : for(s32 i=0; i<2; i++)
1053 : {
1054 98 : enum LightBank bank = banks[i];
1055 :
1056 : /*
1057 : Spread light from all nodes that might be capable of doing so
1058 : */
1059 98 : spreadLight(bank, light_sources, modified_blocks);
1060 : }
1061 :
1062 : /*
1063 : Update information about whether day and night light differ
1064 : */
1065 389 : for(std::map<v3s16, MapBlock*>::iterator
1066 49 : i = modified_blocks.begin();
1067 292 : i != modified_blocks.end(); ++i)
1068 : {
1069 97 : i->second->expireDayNightDiff();
1070 : }
1071 :
1072 : /*
1073 : Report for rollback
1074 : */
1075 49 : if(m_gamedef->rollback())
1076 : {
1077 0 : RollbackNode rollback_newnode(this, p, m_gamedef);
1078 0 : RollbackAction action;
1079 0 : action.setSetNode(p, rollback_oldnode, rollback_newnode);
1080 0 : m_gamedef->rollback()->reportAction(action);
1081 : }
1082 :
1083 : /*
1084 : Add neighboring liquid nodes and the node itself if it is
1085 : liquid (=water node was added) to transform queue.
1086 : */
1087 : v3s16 dirs[7] = {
1088 : v3s16(0,0,0), // self
1089 : v3s16(0,0,1), // back
1090 : v3s16(0,1,0), // top
1091 : v3s16(1,0,0), // right
1092 : v3s16(0,0,-1), // front
1093 : v3s16(0,-1,0), // bottom
1094 : v3s16(-1,0,0), // left
1095 49 : };
1096 392 : for(u16 i=0; i<7; i++)
1097 : {
1098 343 : v3s16 p2 = p + dirs[i];
1099 :
1100 343 : MapNode n2 = getNodeNoEx(p2, &is_valid_position);
1101 343 : if(is_valid_position
1102 343 : && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1103 : {
1104 141 : m_transforming_liquid.push_back(p2);
1105 : }
1106 : }
1107 49 : }
1108 :
1109 : /*
1110 : */
1111 6 : void Map::removeNodeAndUpdate(v3s16 p,
1112 : std::map<v3s16, MapBlock*> &modified_blocks)
1113 : {
1114 6 : INodeDefManager *ndef = m_gamedef->ndef();
1115 :
1116 : /*PrintInfo(m_dout);
1117 : m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1118 : <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1119 :
1120 6 : bool node_under_sunlight = true;
1121 :
1122 6 : v3s16 toppos = p + v3s16(0,1,0);
1123 :
1124 : // Node will be replaced with this
1125 6 : content_t replace_material = CONTENT_AIR;
1126 :
1127 : /*
1128 : Collect old node for rollback
1129 : */
1130 12 : RollbackNode rollback_oldnode(this, p, m_gamedef);
1131 :
1132 : /*
1133 : If there is a node at top and it doesn't have sunlight,
1134 : there will be no sunlight going down.
1135 : */
1136 : bool is_valid_position;
1137 6 : MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
1138 :
1139 6 : if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1140 1 : node_under_sunlight = false;
1141 :
1142 12 : std::set<v3s16> light_sources;
1143 :
1144 : enum LightBank banks[] =
1145 : {
1146 : LIGHTBANK_DAY,
1147 : LIGHTBANK_NIGHT
1148 6 : };
1149 18 : for(s32 i=0; i<2; i++)
1150 : {
1151 12 : enum LightBank bank = banks[i];
1152 :
1153 : /*
1154 : Unlight neighbors (in case the node is a light source)
1155 : */
1156 24 : unLightNeighbors(bank, p,
1157 24 : getNodeNoEx(p).getLight(bank, ndef),
1158 12 : light_sources, modified_blocks);
1159 : }
1160 :
1161 : /*
1162 : Remove node metadata
1163 : */
1164 :
1165 6 : removeNodeMetadata(p);
1166 :
1167 : /*
1168 : Remove the node.
1169 : This also clears the lighting.
1170 : */
1171 :
1172 6 : MapNode n(replace_material);
1173 6 : setNode(p, n);
1174 :
1175 18 : for(s32 i=0; i<2; i++)
1176 : {
1177 12 : enum LightBank bank = banks[i];
1178 :
1179 : /*
1180 : Recalculate lighting
1181 : */
1182 12 : spreadLight(bank, light_sources, modified_blocks);
1183 : }
1184 :
1185 : // Add the block of the removed node to modified_blocks
1186 6 : v3s16 blockpos = getNodeBlockPos(p);
1187 6 : MapBlock * block = getBlockNoCreate(blockpos);
1188 : assert(block != NULL);
1189 6 : modified_blocks[blockpos] = block;
1190 :
1191 : /*
1192 : If the removed node was under sunlight, propagate the
1193 : sunlight down from it and then light all neighbors
1194 : of the propagated blocks.
1195 : */
1196 6 : if(node_under_sunlight)
1197 : {
1198 5 : s16 ybottom = propagateSunlight(p, modified_blocks);
1199 : /*m_dout<<DTIME<<"Node was under sunlight. "
1200 : "Propagating sunlight";
1201 : m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1202 5 : s16 y = p.Y;
1203 15 : for(; y >= ybottom; y--)
1204 : {
1205 5 : v3s16 p2(p.X, y, p.Z);
1206 : /*m_dout<<DTIME<<"lighting neighbors of node ("
1207 : <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1208 : <<std::endl;*/
1209 5 : lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1210 : }
1211 : }
1212 : else
1213 : {
1214 : // Set the lighting of this node to 0
1215 : // TODO: Is this needed? Lighting is cleared up there already.
1216 1 : MapNode n = getNodeNoEx(p, &is_valid_position);
1217 1 : if (is_valid_position) {
1218 1 : n.setLight(LIGHTBANK_DAY, 0, ndef);
1219 1 : setNode(p, n);
1220 : } else {
1221 0 : FATAL_ERROR("Invalid position");
1222 : }
1223 : }
1224 :
1225 18 : for(s32 i=0; i<2; i++)
1226 : {
1227 12 : enum LightBank bank = banks[i];
1228 :
1229 : // Get the brightest neighbour node and propagate light from it
1230 12 : v3s16 n2p = getBrightestNeighbour(bank, p);
1231 : try{
1232 : //MapNode n2 = getNode(n2p);
1233 12 : lightNeighbors(bank, n2p, modified_blocks);
1234 : }
1235 0 : catch(InvalidPositionException &e)
1236 : {
1237 : }
1238 : }
1239 :
1240 : /*
1241 : Update information about whether day and night light differ
1242 : */
1243 45 : for(std::map<v3s16, MapBlock*>::iterator
1244 6 : i = modified_blocks.begin();
1245 34 : i != modified_blocks.end(); ++i)
1246 : {
1247 11 : i->second->expireDayNightDiff();
1248 : }
1249 :
1250 : /*
1251 : Report for rollback
1252 : */
1253 6 : if(m_gamedef->rollback())
1254 : {
1255 0 : RollbackNode rollback_newnode(this, p, m_gamedef);
1256 0 : RollbackAction action;
1257 0 : action.setSetNode(p, rollback_oldnode, rollback_newnode);
1258 0 : m_gamedef->rollback()->reportAction(action);
1259 : }
1260 :
1261 : /*
1262 : Add neighboring liquid nodes and this node to transform queue.
1263 : (it's vital for the node itself to get updated last.)
1264 : */
1265 : v3s16 dirs[7] = {
1266 : v3s16(0,0,1), // back
1267 : v3s16(0,1,0), // top
1268 : v3s16(1,0,0), // right
1269 : v3s16(0,0,-1), // front
1270 : v3s16(0,-1,0), // bottom
1271 : v3s16(-1,0,0), // left
1272 : v3s16(0,0,0), // self
1273 6 : };
1274 48 : for(u16 i=0; i<7; i++)
1275 : {
1276 42 : v3s16 p2 = p + dirs[i];
1277 :
1278 : bool is_position_valid;
1279 42 : MapNode n2 = getNodeNoEx(p2, &is_position_valid);
1280 42 : if (is_position_valid
1281 42 : && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1282 : {
1283 32 : m_transforming_liquid.push_back(p2);
1284 : }
1285 : }
1286 6 : }
1287 :
1288 0 : bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1289 : {
1290 0 : MapEditEvent event;
1291 0 : event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1292 0 : event.p = p;
1293 0 : event.n = n;
1294 :
1295 0 : bool succeeded = true;
1296 : try{
1297 0 : std::map<v3s16, MapBlock*> modified_blocks;
1298 0 : addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1299 :
1300 : // Copy modified_blocks to event
1301 0 : for(std::map<v3s16, MapBlock*>::iterator
1302 0 : i = modified_blocks.begin();
1303 0 : i != modified_blocks.end(); ++i)
1304 : {
1305 0 : event.modified_blocks.insert(i->first);
1306 : }
1307 : }
1308 0 : catch(InvalidPositionException &e){
1309 0 : succeeded = false;
1310 : }
1311 :
1312 0 : dispatchEvent(&event);
1313 :
1314 0 : return succeeded;
1315 : }
1316 :
1317 0 : bool Map::removeNodeWithEvent(v3s16 p)
1318 : {
1319 0 : MapEditEvent event;
1320 0 : event.type = MEET_REMOVENODE;
1321 0 : event.p = p;
1322 :
1323 0 : bool succeeded = true;
1324 : try{
1325 0 : std::map<v3s16, MapBlock*> modified_blocks;
1326 0 : removeNodeAndUpdate(p, modified_blocks);
1327 :
1328 : // Copy modified_blocks to event
1329 0 : for(std::map<v3s16, MapBlock*>::iterator
1330 0 : i = modified_blocks.begin();
1331 0 : i != modified_blocks.end(); ++i)
1332 : {
1333 0 : event.modified_blocks.insert(i->first);
1334 : }
1335 : }
1336 0 : catch(InvalidPositionException &e){
1337 0 : succeeded = false;
1338 : }
1339 :
1340 0 : dispatchEvent(&event);
1341 :
1342 0 : return succeeded;
1343 : }
1344 :
1345 0 : bool Map::getDayNightDiff(v3s16 blockpos)
1346 : {
1347 : try{
1348 0 : v3s16 p = blockpos + v3s16(0,0,0);
1349 0 : MapBlock *b = getBlockNoCreate(p);
1350 0 : if(b->getDayNightDiff())
1351 0 : return true;
1352 : }
1353 0 : catch(InvalidPositionException &e){}
1354 : // Leading edges
1355 : try{
1356 0 : v3s16 p = blockpos + v3s16(-1,0,0);
1357 0 : MapBlock *b = getBlockNoCreate(p);
1358 0 : if(b->getDayNightDiff())
1359 0 : return true;
1360 : }
1361 0 : catch(InvalidPositionException &e){}
1362 : try{
1363 0 : v3s16 p = blockpos + v3s16(0,-1,0);
1364 0 : MapBlock *b = getBlockNoCreate(p);
1365 0 : if(b->getDayNightDiff())
1366 0 : return true;
1367 : }
1368 0 : catch(InvalidPositionException &e){}
1369 : try{
1370 0 : v3s16 p = blockpos + v3s16(0,0,-1);
1371 0 : MapBlock *b = getBlockNoCreate(p);
1372 0 : if(b->getDayNightDiff())
1373 0 : return true;
1374 : }
1375 0 : catch(InvalidPositionException &e){}
1376 : // Trailing edges
1377 : try{
1378 0 : v3s16 p = blockpos + v3s16(1,0,0);
1379 0 : MapBlock *b = getBlockNoCreate(p);
1380 0 : if(b->getDayNightDiff())
1381 0 : return true;
1382 : }
1383 0 : catch(InvalidPositionException &e){}
1384 : try{
1385 0 : v3s16 p = blockpos + v3s16(0,1,0);
1386 0 : MapBlock *b = getBlockNoCreate(p);
1387 0 : if(b->getDayNightDiff())
1388 0 : return true;
1389 : }
1390 0 : catch(InvalidPositionException &e){}
1391 : try{
1392 0 : v3s16 p = blockpos + v3s16(0,0,1);
1393 0 : MapBlock *b = getBlockNoCreate(p);
1394 0 : if(b->getDayNightDiff())
1395 0 : return true;
1396 : }
1397 0 : catch(InvalidPositionException &e){}
1398 :
1399 0 : return false;
1400 : }
1401 :
1402 : /*
1403 : Updates usage timers
1404 : */
1405 6 : void Map::timerUpdate(float dtime, float unload_timeout,
1406 : std::vector<v3s16> *unloaded_blocks)
1407 : {
1408 6 : bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1409 :
1410 : // Profile modified reasons
1411 12 : Profiler modprofiler;
1412 :
1413 12 : std::vector<v2s16> sector_deletion_queue;
1414 6 : u32 deleted_blocks_count = 0;
1415 6 : u32 saved_blocks_count = 0;
1416 6 : u32 block_count_all = 0;
1417 :
1418 6 : beginSave();
1419 2211 : for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1420 1474 : si != m_sectors.end(); ++si) {
1421 731 : MapSector *sector = si->second;
1422 :
1423 731 : bool all_blocks_deleted = true;
1424 :
1425 1462 : MapBlockVect blocks;
1426 731 : sector->getBlocks(blocks);
1427 :
1428 8766 : for(MapBlockVect::iterator i = blocks.begin();
1429 5844 : i != blocks.end(); ++i) {
1430 2191 : MapBlock *block = (*i);
1431 :
1432 2191 : block->incrementUsageTimer(dtime);
1433 :
1434 2191 : if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout) {
1435 0 : v3s16 p = block->getPos();
1436 :
1437 : // Save if modified
1438 0 : if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
1439 0 : modprofiler.add(block->getModifiedReasonString(), 1);
1440 0 : if (!saveBlock(block))
1441 0 : continue;
1442 0 : saved_blocks_count++;
1443 : }
1444 :
1445 : // Delete from memory
1446 0 : sector->deleteBlock(block);
1447 :
1448 0 : if(unloaded_blocks)
1449 0 : unloaded_blocks->push_back(p);
1450 :
1451 0 : deleted_blocks_count++;
1452 : }
1453 : else {
1454 2191 : all_blocks_deleted = false;
1455 2191 : block_count_all++;
1456 : }
1457 : }
1458 :
1459 731 : if(all_blocks_deleted) {
1460 0 : sector_deletion_queue.push_back(si->first);
1461 : }
1462 : }
1463 6 : endSave();
1464 :
1465 : // Finally delete the empty sectors
1466 6 : deleteSectors(sector_deletion_queue);
1467 :
1468 6 : if(deleted_blocks_count != 0)
1469 : {
1470 0 : PrintInfo(infostream); // ServerMap/ClientMap:
1471 0 : infostream<<"Unloaded "<<deleted_blocks_count
1472 0 : <<" blocks from memory";
1473 0 : if(save_before_unloading)
1474 0 : infostream<<", of which "<<saved_blocks_count<<" were written";
1475 0 : infostream<<", "<<block_count_all<<" blocks in memory";
1476 0 : infostream<<"."<<std::endl;
1477 0 : if(saved_blocks_count != 0){
1478 0 : PrintInfo(infostream); // ServerMap/ClientMap:
1479 0 : infostream<<"Blocks modified by: "<<std::endl;
1480 0 : modprofiler.print(infostream);
1481 : }
1482 : }
1483 6 : }
1484 :
1485 0 : void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
1486 : {
1487 0 : timerUpdate(0.0, -1.0, unloaded_blocks);
1488 0 : }
1489 :
1490 6 : void Map::deleteSectors(std::vector<v2s16> §orList)
1491 : {
1492 18 : for(std::vector<v2s16>::iterator j = sectorList.begin();
1493 12 : j != sectorList.end(); ++j) {
1494 0 : MapSector *sector = m_sectors[*j];
1495 : // If sector is in sector cache, remove it from there
1496 0 : if(m_sector_cache == sector)
1497 0 : m_sector_cache = NULL;
1498 : // Remove from map and delete
1499 0 : m_sectors.erase(*j);
1500 0 : delete sector;
1501 : }
1502 6 : }
1503 :
1504 0 : void Map::PrintInfo(std::ostream &out)
1505 : {
1506 0 : out<<"Map: ";
1507 0 : }
1508 :
1509 : #define WATER_DROP_BOOST 4
1510 :
1511 : enum NeighborType {
1512 : NEIGHBOR_UPPER,
1513 : NEIGHBOR_SAME_LEVEL,
1514 : NEIGHBOR_LOWER
1515 : };
1516 0 : struct NodeNeighbor {
1517 : MapNode n;
1518 : NeighborType t;
1519 : v3s16 p;
1520 : bool l; //can liquid
1521 :
1522 0 : NodeNeighbor()
1523 0 : : n(CONTENT_AIR)
1524 0 : { }
1525 :
1526 0 : NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
1527 : : n(node),
1528 : t(n_type),
1529 0 : p(pos)
1530 0 : { }
1531 : };
1532 :
1533 0 : void Map::transforming_liquid_add(v3s16 p) {
1534 0 : m_transforming_liquid.push_back(p);
1535 0 : }
1536 :
1537 0 : s32 Map::transforming_liquid_size() {
1538 0 : return m_transforming_liquid.size();
1539 : }
1540 :
1541 0 : void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1542 : {
1543 :
1544 0 : INodeDefManager *nodemgr = m_gamedef->ndef();
1545 :
1546 0 : DSTACK(__FUNCTION_NAME);
1547 : //TimeTaker timer("transformLiquids()");
1548 :
1549 0 : u32 loopcount = 0;
1550 0 : u32 initial_size = m_transforming_liquid.size();
1551 :
1552 : /*if(initial_size != 0)
1553 : infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1554 :
1555 : // list of nodes that due to viscosity have not reached their max level height
1556 0 : std::deque<v3s16> must_reflow;
1557 :
1558 : // List of MapBlocks that will require a lighting update (due to lava)
1559 0 : std::map<v3s16, MapBlock*> lighting_modified_blocks;
1560 :
1561 0 : u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1562 0 : u32 loop_max = liquid_loop_max;
1563 :
1564 : #if 0
1565 :
1566 : /* If liquid_loop_max is not keeping up with the queue size increase
1567 : * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1568 : */
1569 : if (m_transforming_liquid.size() > loop_max * 2) {
1570 : // "Burst" mode
1571 : float server_step = g_settings->getFloat("dedicated_server_step");
1572 : if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1573 : m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1574 : } else {
1575 : m_transforming_liquid_loop_count_multiplier = 1.0;
1576 : }
1577 :
1578 : loop_max *= m_transforming_liquid_loop_count_multiplier;
1579 : #endif
1580 :
1581 0 : while(m_transforming_liquid.size() != 0)
1582 : {
1583 : // This should be done here so that it is done when continue is used
1584 0 : if(loopcount >= initial_size || loopcount >= loop_max)
1585 : break;
1586 0 : loopcount++;
1587 :
1588 : /*
1589 : Get a queued transforming liquid node
1590 : */
1591 0 : v3s16 p0 = m_transforming_liquid.front();
1592 0 : m_transforming_liquid.pop_front();
1593 :
1594 0 : MapNode n0 = getNodeNoEx(p0);
1595 :
1596 : /*
1597 : Collect information about current node
1598 : */
1599 0 : s8 liquid_level = -1;
1600 0 : content_t liquid_kind = CONTENT_IGNORE;
1601 0 : LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1602 0 : switch (liquid_type) {
1603 : case LIQUID_SOURCE:
1604 0 : liquid_level = LIQUID_LEVEL_SOURCE;
1605 0 : liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1606 0 : break;
1607 : case LIQUID_FLOWING:
1608 0 : liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1609 0 : liquid_kind = n0.getContent();
1610 0 : break;
1611 : case LIQUID_NONE:
1612 : // if this is an air node, it *could* be transformed into a liquid. otherwise,
1613 : // continue with the next node.
1614 0 : if (n0.getContent() != CONTENT_AIR)
1615 0 : continue;
1616 0 : liquid_kind = CONTENT_AIR;
1617 0 : break;
1618 : }
1619 :
1620 : /*
1621 : Collect information about the environment
1622 : */
1623 0 : const v3s16 *dirs = g_6dirs;
1624 0 : NodeNeighbor sources[6]; // surrounding sources
1625 0 : int num_sources = 0;
1626 0 : NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1627 0 : int num_flows = 0;
1628 0 : NodeNeighbor airs[6]; // surrounding air
1629 0 : int num_airs = 0;
1630 0 : NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1631 0 : int num_neutrals = 0;
1632 0 : bool flowing_down = false;
1633 0 : for (u16 i = 0; i < 6; i++) {
1634 0 : NeighborType nt = NEIGHBOR_SAME_LEVEL;
1635 0 : switch (i) {
1636 : case 1:
1637 0 : nt = NEIGHBOR_UPPER;
1638 0 : break;
1639 : case 4:
1640 0 : nt = NEIGHBOR_LOWER;
1641 0 : break;
1642 : }
1643 0 : v3s16 npos = p0 + dirs[i];
1644 0 : NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1645 0 : switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1646 : case LIQUID_NONE:
1647 0 : if (nb.n.getContent() == CONTENT_AIR) {
1648 0 : airs[num_airs++] = nb;
1649 : // if the current node is a water source the neighbor
1650 : // should be enqueded for transformation regardless of whether the
1651 : // current node changes or not.
1652 0 : if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1653 0 : m_transforming_liquid.push_back(npos);
1654 : // if the current node happens to be a flowing node, it will start to flow down here.
1655 0 : if (nb.t == NEIGHBOR_LOWER) {
1656 0 : flowing_down = true;
1657 : }
1658 : } else {
1659 0 : neutrals[num_neutrals++] = nb;
1660 : }
1661 0 : break;
1662 : case LIQUID_SOURCE:
1663 : // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1664 0 : if (liquid_kind == CONTENT_AIR)
1665 0 : liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1666 0 : if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1667 0 : neutrals[num_neutrals++] = nb;
1668 : } else {
1669 : // Do not count bottom source, it will screw things up
1670 0 : if(dirs[i].Y != -1)
1671 0 : sources[num_sources++] = nb;
1672 : }
1673 0 : break;
1674 : case LIQUID_FLOWING:
1675 : // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1676 0 : if (liquid_kind == CONTENT_AIR)
1677 0 : liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1678 0 : if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1679 0 : neutrals[num_neutrals++] = nb;
1680 : } else {
1681 0 : flows[num_flows++] = nb;
1682 0 : if (nb.t == NEIGHBOR_LOWER)
1683 0 : flowing_down = true;
1684 : }
1685 0 : break;
1686 : }
1687 : }
1688 :
1689 : /*
1690 : decide on the type (and possibly level) of the current node
1691 : */
1692 : content_t new_node_content;
1693 0 : s8 new_node_level = -1;
1694 0 : s8 max_node_level = -1;
1695 :
1696 0 : u8 range = nodemgr->get(liquid_kind).liquid_range;
1697 0 : if (range > LIQUID_LEVEL_MAX+1)
1698 0 : range = LIQUID_LEVEL_MAX+1;
1699 :
1700 0 : if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1701 : // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1702 : // or the flowing alternative of the first of the surrounding sources (if it's air), so
1703 : // it's perfectly safe to use liquid_kind here to determine the new node content.
1704 0 : new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1705 0 : } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1706 : // liquid_kind is set properly, see above
1707 0 : new_node_content = liquid_kind;
1708 0 : max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1709 0 : if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
1710 0 : new_node_content = CONTENT_AIR;
1711 : } else {
1712 : // no surrounding sources, so get the maximum level that can flow into this node
1713 0 : for (u16 i = 0; i < num_flows; i++) {
1714 0 : u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1715 0 : switch (flows[i].t) {
1716 : case NEIGHBOR_UPPER:
1717 0 : if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1718 0 : max_node_level = LIQUID_LEVEL_MAX;
1719 0 : if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1720 0 : max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1721 0 : } else if (nb_liquid_level > max_node_level)
1722 0 : max_node_level = nb_liquid_level;
1723 0 : break;
1724 : case NEIGHBOR_LOWER:
1725 0 : break;
1726 : case NEIGHBOR_SAME_LEVEL:
1727 0 : if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1728 0 : nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1729 0 : max_node_level = nb_liquid_level - 1;
1730 : }
1731 0 : break;
1732 : }
1733 : }
1734 :
1735 0 : u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1736 0 : if (viscosity > 1 && max_node_level != liquid_level) {
1737 : // amount to gain, limited by viscosity
1738 : // must be at least 1 in absolute value
1739 0 : s8 level_inc = max_node_level - liquid_level;
1740 0 : if (level_inc < -viscosity || level_inc > viscosity)
1741 0 : new_node_level = liquid_level + level_inc/viscosity;
1742 0 : else if (level_inc < 0)
1743 0 : new_node_level = liquid_level - 1;
1744 0 : else if (level_inc > 0)
1745 0 : new_node_level = liquid_level + 1;
1746 0 : if (new_node_level != max_node_level)
1747 0 : must_reflow.push_back(p0);
1748 : } else
1749 0 : new_node_level = max_node_level;
1750 :
1751 0 : if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
1752 0 : new_node_content = liquid_kind;
1753 : else
1754 0 : new_node_content = CONTENT_AIR;
1755 :
1756 : }
1757 :
1758 : /*
1759 : check if anything has changed. if not, just continue with the next node.
1760 : */
1761 0 : if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1762 0 : ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1763 0 : ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1764 0 : == flowing_down)))
1765 0 : continue;
1766 :
1767 :
1768 : /*
1769 : update the current node
1770 : */
1771 0 : MapNode n00 = n0;
1772 : //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1773 0 : if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1774 : // set level to last 3 bits, flowing down bit to 4th bit
1775 0 : n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1776 : } else {
1777 : // set the liquid level and flow bit to 0
1778 0 : n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1779 : }
1780 0 : n0.setContent(new_node_content);
1781 :
1782 : // Find out whether there is a suspect for this action
1783 0 : std::string suspect;
1784 0 : if(m_gamedef->rollback()) {
1785 0 : suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1786 : }
1787 :
1788 0 : if(m_gamedef->rollback() && !suspect.empty()){
1789 : // Blame suspect
1790 0 : RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1791 : // Get old node for rollback
1792 0 : RollbackNode rollback_oldnode(this, p0, m_gamedef);
1793 : // Set node
1794 0 : setNode(p0, n0);
1795 : // Report
1796 0 : RollbackNode rollback_newnode(this, p0, m_gamedef);
1797 0 : RollbackAction action;
1798 0 : action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1799 0 : m_gamedef->rollback()->reportAction(action);
1800 : } else {
1801 : // Set node
1802 0 : setNode(p0, n0);
1803 : }
1804 :
1805 0 : v3s16 blockpos = getNodeBlockPos(p0);
1806 0 : MapBlock *block = getBlockNoCreateNoEx(blockpos);
1807 0 : if(block != NULL) {
1808 0 : modified_blocks[blockpos] = block;
1809 : // If new or old node emits light, MapBlock requires lighting update
1810 0 : if(nodemgr->get(n0).light_source != 0 ||
1811 0 : nodemgr->get(n00).light_source != 0)
1812 0 : lighting_modified_blocks[block->getPos()] = block;
1813 : }
1814 :
1815 : /*
1816 : enqueue neighbors for update if neccessary
1817 : */
1818 0 : switch (nodemgr->get(n0.getContent()).liquid_type) {
1819 : case LIQUID_SOURCE:
1820 : case LIQUID_FLOWING:
1821 : // make sure source flows into all neighboring nodes
1822 0 : for (u16 i = 0; i < num_flows; i++)
1823 0 : if (flows[i].t != NEIGHBOR_UPPER)
1824 0 : m_transforming_liquid.push_back(flows[i].p);
1825 0 : for (u16 i = 0; i < num_airs; i++)
1826 0 : if (airs[i].t != NEIGHBOR_UPPER)
1827 0 : m_transforming_liquid.push_back(airs[i].p);
1828 0 : break;
1829 : case LIQUID_NONE:
1830 : // this flow has turned to air; neighboring flows might need to do the same
1831 0 : for (u16 i = 0; i < num_flows; i++)
1832 0 : m_transforming_liquid.push_back(flows[i].p);
1833 0 : break;
1834 : }
1835 : }
1836 : //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1837 :
1838 0 : for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
1839 0 : m_transforming_liquid.push_back(*iter);
1840 :
1841 0 : updateLighting(lighting_modified_blocks, modified_blocks);
1842 :
1843 :
1844 : /* ----------------------------------------------------------------------
1845 : * Manage the queue so that it does not grow indefinately
1846 : */
1847 0 : u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1848 :
1849 0 : if (time_until_purge == 0)
1850 0 : return; // Feature disabled
1851 :
1852 0 : time_until_purge *= 1000; // seconds -> milliseconds
1853 :
1854 0 : u32 curr_time = getTime(PRECISION_MILLI);
1855 0 : u32 prev_unprocessed = m_unprocessed_count;
1856 0 : m_unprocessed_count = m_transforming_liquid.size();
1857 :
1858 : // if unprocessed block count is decreasing or stable
1859 0 : if (m_unprocessed_count <= prev_unprocessed) {
1860 0 : m_queue_size_timer_started = false;
1861 : } else {
1862 0 : if (!m_queue_size_timer_started)
1863 0 : m_inc_trending_up_start_time = curr_time;
1864 0 : m_queue_size_timer_started = true;
1865 : }
1866 :
1867 : // Account for curr_time overflowing
1868 0 : if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1869 0 : m_queue_size_timer_started = false;
1870 :
1871 : /* If the queue has been growing for more than liquid_queue_purge_time seconds
1872 : * and the number of unprocessed blocks is still > liquid_loop_max then we
1873 : * cannot keep up; dump the oldest blocks from the queue so that the queue
1874 : * has liquid_loop_max items in it
1875 : */
1876 0 : if (m_queue_size_timer_started
1877 0 : && curr_time - m_inc_trending_up_start_time > time_until_purge
1878 0 : && m_unprocessed_count > liquid_loop_max) {
1879 :
1880 0 : size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1881 :
1882 0 : infostream << "transformLiquids(): DUMPING " << dump_qty
1883 0 : << " blocks from the queue" << std::endl;
1884 :
1885 0 : while (dump_qty--)
1886 0 : m_transforming_liquid.pop_front();
1887 :
1888 0 : m_queue_size_timer_started = false; // optimistically assume we can keep up now
1889 0 : m_unprocessed_count = m_transforming_liquid.size();
1890 : }
1891 : }
1892 :
1893 0 : std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
1894 : {
1895 0 : std::vector<v3s16> positions_with_meta;
1896 :
1897 0 : sortBoxVerticies(p1, p2);
1898 0 : v3s16 bpmin = getNodeBlockPos(p1);
1899 0 : v3s16 bpmax = getNodeBlockPos(p2);
1900 :
1901 0 : VoxelArea area(p1, p2);
1902 :
1903 0 : for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1904 0 : for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
1905 0 : for (s16 x = bpmin.X; x <= bpmax.X; x++) {
1906 0 : v3s16 blockpos(x, y, z);
1907 :
1908 0 : MapBlock *block = getBlockNoCreateNoEx(blockpos);
1909 0 : if (!block) {
1910 0 : verbosestream << "Map::getNodeMetadata(): Need to emerge "
1911 0 : << PP(blockpos) << std::endl;
1912 0 : block = emergeBlock(blockpos, false);
1913 : }
1914 0 : if (!block) {
1915 0 : infostream << "WARNING: Map::getNodeMetadata(): Block not found"
1916 0 : << std::endl;
1917 0 : continue;
1918 : }
1919 :
1920 0 : v3s16 p_base = blockpos * MAP_BLOCKSIZE;
1921 0 : std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
1922 0 : for (size_t i = 0; i != keys.size(); i++) {
1923 0 : v3s16 p(keys[i] + p_base);
1924 0 : if (!area.contains(p))
1925 0 : continue;
1926 :
1927 0 : positions_with_meta.push_back(p);
1928 : }
1929 : }
1930 :
1931 0 : return positions_with_meta;
1932 : }
1933 :
1934 340 : NodeMetadata *Map::getNodeMetadata(v3s16 p)
1935 : {
1936 340 : v3s16 blockpos = getNodeBlockPos(p);
1937 340 : v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1938 340 : MapBlock *block = getBlockNoCreateNoEx(blockpos);
1939 340 : if(!block){
1940 0 : infostream<<"Map::getNodeMetadata(): Need to emerge "
1941 0 : <<PP(blockpos)<<std::endl;
1942 0 : block = emergeBlock(blockpos, false);
1943 : }
1944 340 : if(!block){
1945 0 : infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1946 0 : <<std::endl;
1947 0 : return NULL;
1948 : }
1949 340 : NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1950 340 : return meta;
1951 : }
1952 :
1953 0 : bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1954 : {
1955 0 : v3s16 blockpos = getNodeBlockPos(p);
1956 0 : v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1957 0 : MapBlock *block = getBlockNoCreateNoEx(blockpos);
1958 0 : if(!block){
1959 0 : infostream<<"Map::setNodeMetadata(): Need to emerge "
1960 0 : <<PP(blockpos)<<std::endl;
1961 0 : block = emergeBlock(blockpos, false);
1962 : }
1963 0 : if(!block){
1964 0 : infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1965 0 : <<std::endl;
1966 0 : return false;
1967 : }
1968 0 : block->m_node_metadata.set(p_rel, meta);
1969 0 : return true;
1970 : }
1971 :
1972 13 : void Map::removeNodeMetadata(v3s16 p)
1973 : {
1974 13 : v3s16 blockpos = getNodeBlockPos(p);
1975 13 : v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1976 13 : MapBlock *block = getBlockNoCreateNoEx(blockpos);
1977 13 : if(block == NULL)
1978 : {
1979 0 : infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1980 0 : <<std::endl;
1981 0 : return;
1982 : }
1983 13 : block->m_node_metadata.remove(p_rel);
1984 : }
1985 :
1986 0 : NodeTimer Map::getNodeTimer(v3s16 p)
1987 : {
1988 0 : v3s16 blockpos = getNodeBlockPos(p);
1989 0 : v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1990 0 : MapBlock *block = getBlockNoCreateNoEx(blockpos);
1991 0 : if(!block){
1992 0 : infostream<<"Map::getNodeTimer(): Need to emerge "
1993 0 : <<PP(blockpos)<<std::endl;
1994 0 : block = emergeBlock(blockpos, false);
1995 : }
1996 0 : if(!block){
1997 0 : infostream<<"WARNING: Map::getNodeTimer(): Block not found"
1998 0 : <<std::endl;
1999 0 : return NodeTimer();
2000 : }
2001 0 : NodeTimer t = block->m_node_timers.get(p_rel);
2002 0 : return t;
2003 : }
2004 :
2005 0 : void Map::setNodeTimer(v3s16 p, NodeTimer t)
2006 : {
2007 0 : v3s16 blockpos = getNodeBlockPos(p);
2008 0 : v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2009 0 : MapBlock *block = getBlockNoCreateNoEx(blockpos);
2010 0 : if(!block){
2011 0 : infostream<<"Map::setNodeTimer(): Need to emerge "
2012 0 : <<PP(blockpos)<<std::endl;
2013 0 : block = emergeBlock(blockpos, false);
2014 : }
2015 0 : if(!block){
2016 0 : infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2017 0 : <<std::endl;
2018 0 : return;
2019 : }
2020 0 : block->m_node_timers.set(p_rel, t);
2021 : }
2022 :
2023 0 : void Map::removeNodeTimer(v3s16 p)
2024 : {
2025 0 : v3s16 blockpos = getNodeBlockPos(p);
2026 0 : v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2027 0 : MapBlock *block = getBlockNoCreateNoEx(blockpos);
2028 0 : if(block == NULL)
2029 : {
2030 0 : infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2031 0 : <<std::endl;
2032 0 : return;
2033 : }
2034 0 : block->m_node_timers.remove(p_rel);
2035 : }
2036 :
2037 : /*
2038 : ServerMap
2039 : */
2040 0 : ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2041 : Map(dout_server, gamedef),
2042 : m_emerge(emerge),
2043 0 : m_map_metadata_changed(true)
2044 : {
2045 0 : verbosestream<<__FUNCTION_NAME<<std::endl;
2046 :
2047 : /*
2048 : Try to load map; if not found, create a new one.
2049 : */
2050 :
2051 : // Determine which database backend to use
2052 0 : std::string conf_path = savedir + DIR_DELIM + "world.mt";
2053 0 : Settings conf;
2054 0 : bool succeeded = conf.readConfigFile(conf_path.c_str());
2055 0 : if (!succeeded || !conf.exists("backend")) {
2056 : // fall back to sqlite3
2057 0 : conf.set("backend", "sqlite3");
2058 : }
2059 0 : std::string backend = conf.get("backend");
2060 0 : dbase = createDatabase(backend, savedir, conf);
2061 :
2062 0 : if (!conf.updateConfigFile(conf_path.c_str()))
2063 0 : errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
2064 :
2065 0 : m_savedir = savedir;
2066 0 : m_map_saving_enabled = false;
2067 :
2068 : try
2069 : {
2070 : // If directory exists, check contents and load if possible
2071 0 : if(fs::PathExists(m_savedir))
2072 : {
2073 : // If directory is empty, it is safe to save into it.
2074 0 : if(fs::GetDirListing(m_savedir).size() == 0)
2075 : {
2076 0 : infostream<<"ServerMap: Empty save directory is valid."
2077 0 : <<std::endl;
2078 0 : m_map_saving_enabled = true;
2079 : }
2080 : else
2081 : {
2082 : try{
2083 : // Load map metadata (seed, chunksize)
2084 0 : loadMapMeta();
2085 : }
2086 0 : catch(SettingNotFoundException &e){
2087 0 : infostream<<"ServerMap: Some metadata not found."
2088 0 : <<" Using default settings."<<std::endl;
2089 : }
2090 0 : catch(FileNotGoodException &e){
2091 0 : infostream<<"WARNING: Could not load map metadata"
2092 : //<<" Disabling chunk-based generator."
2093 0 : <<std::endl;
2094 : //m_chunksize = 0;
2095 : }
2096 :
2097 0 : infostream<<"ServerMap: Successfully loaded map "
2098 0 : <<"metadata from "<<savedir
2099 0 : <<", assuming valid save directory."
2100 0 : <<" seed="<< m_emerge->params.seed <<"."
2101 0 : <<std::endl;
2102 :
2103 0 : m_map_saving_enabled = true;
2104 : // Map loaded, not creating new one
2105 0 : return;
2106 : }
2107 : }
2108 : // If directory doesn't exist, it is safe to save to it
2109 : else{
2110 0 : m_map_saving_enabled = true;
2111 : }
2112 : }
2113 0 : catch(std::exception &e)
2114 : {
2115 0 : infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2116 0 : <<", exception: "<<e.what()<<std::endl;
2117 0 : infostream<<"Please remove the map or fix it."<<std::endl;
2118 0 : infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2119 : }
2120 :
2121 0 : infostream<<"Initializing new map."<<std::endl;
2122 :
2123 : // Create zero sector
2124 0 : emergeSector(v2s16(0,0));
2125 :
2126 : // Initially write whole map
2127 0 : save(MOD_STATE_CLEAN);
2128 : }
2129 :
2130 0 : ServerMap::~ServerMap()
2131 : {
2132 0 : verbosestream<<__FUNCTION_NAME<<std::endl;
2133 :
2134 : try
2135 : {
2136 0 : if(m_map_saving_enabled)
2137 : {
2138 : // Save only changed parts
2139 0 : save(MOD_STATE_WRITE_AT_UNLOAD);
2140 0 : infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2141 : }
2142 : else
2143 : {
2144 0 : infostream<<"ServerMap: Map not saved"<<std::endl;
2145 : }
2146 : }
2147 0 : catch(std::exception &e)
2148 : {
2149 0 : infostream<<"ServerMap: Failed to save map to "<<m_savedir
2150 0 : <<", exception: "<<e.what()<<std::endl;
2151 : }
2152 :
2153 : /*
2154 : Close database if it was opened
2155 : */
2156 0 : delete dbase;
2157 :
2158 : #if 0
2159 : /*
2160 : Free all MapChunks
2161 : */
2162 : core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2163 : for(; i.atEnd() == false; i++)
2164 : {
2165 : MapChunk *chunk = i.getNode()->getValue();
2166 : delete chunk;
2167 : }
2168 : #endif
2169 0 : }
2170 :
2171 0 : u64 ServerMap::getSeed()
2172 : {
2173 0 : return m_emerge->params.seed;
2174 : }
2175 :
2176 0 : s16 ServerMap::getWaterLevel()
2177 : {
2178 0 : return m_emerge->params.water_level;
2179 : }
2180 :
2181 0 : bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2182 : {
2183 0 : bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2184 0 : EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2185 :
2186 0 : s16 chunksize = m_emerge->params.chunksize;
2187 0 : s16 coffset = -chunksize / 2;
2188 0 : v3s16 chunk_offset(coffset, coffset, coffset);
2189 0 : v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2190 0 : v3s16 blockpos_min = blockpos_div * chunksize;
2191 0 : v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2192 0 : blockpos_min += chunk_offset;
2193 0 : blockpos_max += chunk_offset;
2194 :
2195 0 : v3s16 extra_borders(1,1,1);
2196 :
2197 : // Do nothing if not inside limits (+-1 because of neighbors)
2198 0 : if(blockpos_over_limit(blockpos_min - extra_borders) ||
2199 0 : blockpos_over_limit(blockpos_max + extra_borders))
2200 0 : return false;
2201 :
2202 0 : data->seed = m_emerge->params.seed;
2203 0 : data->blockpos_min = blockpos_min;
2204 0 : data->blockpos_max = blockpos_max;
2205 0 : data->blockpos_requested = blockpos;
2206 0 : data->nodedef = m_gamedef->ndef();
2207 :
2208 : /*
2209 : Create the whole area of this and the neighboring blocks
2210 : */
2211 : {
2212 : //TimeTaker timer("initBlockMake() create area");
2213 :
2214 0 : for(s16 x=blockpos_min.X-extra_borders.X;
2215 0 : x<=blockpos_max.X+extra_borders.X; x++)
2216 0 : for(s16 z=blockpos_min.Z-extra_borders.Z;
2217 0 : z<=blockpos_max.Z+extra_borders.Z; z++)
2218 : {
2219 0 : v2s16 sectorpos(x, z);
2220 : // Sector metadata is loaded from disk if not already loaded.
2221 0 : ServerMapSector *sector = createSector(sectorpos);
2222 0 : FATAL_ERROR_IF(sector == NULL, "createSector() failed");
2223 : (void) sector;
2224 :
2225 0 : for(s16 y=blockpos_min.Y-extra_borders.Y;
2226 0 : y<=blockpos_max.Y+extra_borders.Y; y++)
2227 : {
2228 0 : v3s16 p(x,y,z);
2229 : //MapBlock *block = createBlock(p);
2230 : // 1) get from memory, 2) load from disk
2231 0 : MapBlock *block = emergeBlock(p, false);
2232 : // 3) create a blank one
2233 0 : if(block == NULL)
2234 : {
2235 0 : block = createBlock(p);
2236 :
2237 : /*
2238 : Block gets sunlight if this is true.
2239 :
2240 : Refer to the map generator heuristics.
2241 : */
2242 0 : bool ug = m_emerge->isBlockUnderground(p);
2243 0 : block->setIsUnderground(ug);
2244 : }
2245 :
2246 : // Lighting will not be valid after make_chunk is called
2247 0 : block->setLightingExpired(true);
2248 : // Lighting will be calculated
2249 : //block->setLightingExpired(false);
2250 : }
2251 : }
2252 : }
2253 :
2254 : /*
2255 : Now we have a big empty area.
2256 :
2257 : Make a ManualMapVoxelManipulator that contains this and the
2258 : neighboring blocks
2259 : */
2260 :
2261 : // The area that contains this block and it's neighbors
2262 0 : v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2263 0 : v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2264 :
2265 0 : data->vmanip = new MMVManip(this);
2266 : //data->vmanip->setMap(this);
2267 :
2268 : // Add the area
2269 : {
2270 : //TimeTaker timer("initBlockMake() initialEmerge");
2271 0 : data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2272 : }
2273 :
2274 : // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2275 : /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2276 : for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2277 : for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2278 : core::map<v3s16, u8>::Node *n;
2279 : n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2280 : if (n == NULL)
2281 : continue;
2282 : u8 flags = n->getValue();
2283 : flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2284 : n->setValue(flags);
2285 : }
2286 : }
2287 : }*/
2288 :
2289 : // Data is ready now.
2290 0 : return true;
2291 : }
2292 :
2293 0 : void ServerMap::finishBlockMake(BlockMakeData *data,
2294 : std::map<v3s16, MapBlock*> &changed_blocks)
2295 : {
2296 0 : v3s16 blockpos_min = data->blockpos_min;
2297 0 : v3s16 blockpos_max = data->blockpos_max;
2298 0 : v3s16 blockpos_requested = data->blockpos_requested;
2299 : /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2300 : <<blockpos_requested.Y<<","
2301 : <<blockpos_requested.Z<<")"<<std::endl;*/
2302 :
2303 0 : v3s16 extra_borders(1,1,1);
2304 :
2305 0 : bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2306 :
2307 : /*infostream<<"Resulting vmanip:"<<std::endl;
2308 : data->vmanip.print(infostream);*/
2309 :
2310 : // Make sure affected blocks are loaded
2311 0 : for(s16 x=blockpos_min.X-extra_borders.X;
2312 0 : x<=blockpos_max.X+extra_borders.X; x++)
2313 0 : for(s16 z=blockpos_min.Z-extra_borders.Z;
2314 0 : z<=blockpos_max.Z+extra_borders.Z; z++)
2315 0 : for(s16 y=blockpos_min.Y-extra_borders.Y;
2316 0 : y<=blockpos_max.Y+extra_borders.Y; y++)
2317 : {
2318 0 : v3s16 p(x, y, z);
2319 : // Load from disk if not already in memory
2320 0 : emergeBlock(p, false);
2321 : }
2322 :
2323 : /*
2324 : Blit generated stuff to map
2325 : NOTE: blitBackAll adds nearly everything to changed_blocks
2326 : */
2327 : {
2328 : // 70ms @cs=8
2329 : //TimeTaker timer("finishBlockMake() blitBackAll");
2330 0 : data->vmanip->blitBackAll(&changed_blocks);
2331 : }
2332 :
2333 0 : EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2334 :
2335 : /*
2336 : Copy transforming liquid information
2337 : */
2338 0 : while(data->transforming_liquid.size() > 0)
2339 : {
2340 0 : m_transforming_liquid.push_back(data->transforming_liquid.front());
2341 0 : data->transforming_liquid.pop_front();
2342 : }
2343 :
2344 : /*
2345 : Do stuff in central blocks
2346 : */
2347 :
2348 : /*
2349 : Update lighting
2350 : */
2351 : {
2352 : #if 0
2353 : TimeTaker t("finishBlockMake lighting update");
2354 :
2355 : core::map<v3s16, MapBlock*> lighting_update_blocks;
2356 :
2357 : // Center blocks
2358 : for(s16 x=blockpos_min.X-extra_borders.X;
2359 : x<=blockpos_max.X+extra_borders.X; x++)
2360 : for(s16 z=blockpos_min.Z-extra_borders.Z;
2361 : z<=blockpos_max.Z+extra_borders.Z; z++)
2362 : for(s16 y=blockpos_min.Y-extra_borders.Y;
2363 : y<=blockpos_max.Y+extra_borders.Y; y++)
2364 : {
2365 : v3s16 p(x, y, z);
2366 : MapBlock *block = getBlockNoCreateNoEx(p);
2367 : assert(block);
2368 : lighting_update_blocks.insert(block->getPos(), block);
2369 : }
2370 :
2371 : updateLighting(lighting_update_blocks, changed_blocks);
2372 : #endif
2373 :
2374 : /*
2375 : Set lighting to non-expired state in all of them.
2376 : This is cheating, but it is not fast enough if all of them
2377 : would actually be updated.
2378 : */
2379 0 : for(s16 x=blockpos_min.X-extra_borders.X;
2380 0 : x<=blockpos_max.X+extra_borders.X; x++)
2381 0 : for(s16 z=blockpos_min.Z-extra_borders.Z;
2382 0 : z<=blockpos_max.Z+extra_borders.Z; z++)
2383 0 : for(s16 y=blockpos_min.Y-extra_borders.Y;
2384 0 : y<=blockpos_max.Y+extra_borders.Y; y++)
2385 : {
2386 0 : v3s16 p(x, y, z);
2387 0 : MapBlock * block = getBlockNoCreateNoEx(p);
2388 0 : if (block != NULL)
2389 0 : block->setLightingExpired(false);
2390 : }
2391 :
2392 : #if 0
2393 : if(enable_mapgen_debug_info == false)
2394 : t.stop(true); // Hide output
2395 : #endif
2396 : }
2397 :
2398 : /*
2399 : Go through changed blocks
2400 : */
2401 0 : for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2402 0 : i != changed_blocks.end(); ++i)
2403 : {
2404 0 : MapBlock *block = i->second;
2405 0 : if (!block)
2406 0 : continue;
2407 : /*
2408 : Update day/night difference cache of the MapBlocks
2409 : */
2410 0 : block->expireDayNightDiff();
2411 : /*
2412 : Set block as modified
2413 : */
2414 : block->raiseModified(MOD_STATE_WRITE_NEEDED,
2415 0 : MOD_REASON_EXPIRE_DAYNIGHTDIFF);
2416 : }
2417 :
2418 : /*
2419 : Set central blocks as generated
2420 : */
2421 0 : for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2422 0 : for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2423 0 : for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2424 : {
2425 0 : v3s16 p(x, y, z);
2426 0 : MapBlock *block = getBlockNoCreateNoEx(p);
2427 0 : if (!block)
2428 0 : continue;
2429 0 : block->setGenerated(true);
2430 : }
2431 :
2432 : /*
2433 : Save changed parts of map
2434 : NOTE: Will be saved later.
2435 : */
2436 : //save(MOD_STATE_WRITE_AT_UNLOAD);
2437 :
2438 : /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2439 : <<","<<blockpos_requested.Y<<","
2440 : <<blockpos_requested.Z<<")"<<std::endl;*/
2441 :
2442 :
2443 : #if 0
2444 : if(enable_mapgen_debug_info)
2445 : {
2446 : /*
2447 : Analyze resulting blocks
2448 : */
2449 : /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2450 : for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2451 : for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2452 : for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2453 : for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2454 : for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2455 : {
2456 : v3s16 p = v3s16(x,y,z);
2457 : MapBlock *block = getBlockNoCreateNoEx(p);
2458 : char spos[20];
2459 : snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2460 : infostream<<"Generated "<<spos<<": "
2461 : <<analyze_block(block)<<std::endl;
2462 : }
2463 : }
2464 : #endif
2465 :
2466 0 : getBlockNoCreateNoEx(blockpos_requested);
2467 0 : }
2468 :
2469 0 : ServerMapSector * ServerMap::createSector(v2s16 p2d)
2470 : {
2471 0 : DSTACKF("%s: p2d=(%d,%d)",
2472 : __FUNCTION_NAME,
2473 : p2d.X, p2d.Y);
2474 :
2475 : /*
2476 : Check if it exists already in memory
2477 : */
2478 0 : ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2479 0 : if(sector != NULL)
2480 0 : return sector;
2481 :
2482 : /*
2483 : Try to load it from disk (with blocks)
2484 : */
2485 : //if(loadSectorFull(p2d) == true)
2486 :
2487 : /*
2488 : Try to load metadata from disk
2489 : */
2490 : #if 0
2491 : if(loadSectorMeta(p2d) == true)
2492 : {
2493 : ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2494 : if(sector == NULL)
2495 : {
2496 : infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2497 : throw InvalidPositionException("");
2498 : }
2499 : return sector;
2500 : }
2501 : #endif
2502 : /*
2503 : Do not create over-limit
2504 : */
2505 0 : if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2506 0 : || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2507 0 : || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2508 0 : || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2509 0 : throw InvalidPositionException("createSector(): pos. over limit");
2510 :
2511 : /*
2512 : Generate blank sector
2513 : */
2514 :
2515 0 : sector = new ServerMapSector(this, p2d, m_gamedef);
2516 :
2517 : // Sector position on map in nodes
2518 : //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2519 :
2520 : /*
2521 : Insert to container
2522 : */
2523 0 : m_sectors[p2d] = sector;
2524 :
2525 0 : return sector;
2526 : }
2527 :
2528 : #if 0
2529 : /*
2530 : This is a quick-hand function for calling makeBlock().
2531 : */
2532 : MapBlock * ServerMap::generateBlock(
2533 : v3s16 p,
2534 : std::map<v3s16, MapBlock*> &modified_blocks
2535 : )
2536 : {
2537 : DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2538 :
2539 : /*infostream<<"generateBlock(): "
2540 : <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2541 : <<std::endl;*/
2542 :
2543 : bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2544 :
2545 : TimeTaker timer("generateBlock");
2546 :
2547 : //MapBlock *block = original_dummy;
2548 :
2549 : v2s16 p2d(p.X, p.Z);
2550 : v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2551 :
2552 : /*
2553 : Do not generate over-limit
2554 : */
2555 : if(blockpos_over_limit(p))
2556 : {
2557 : infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2558 : throw InvalidPositionException("generateBlock(): pos. over limit");
2559 : }
2560 :
2561 : /*
2562 : Create block make data
2563 : */
2564 : BlockMakeData data;
2565 : initBlockMake(&data, p);
2566 :
2567 : /*
2568 : Generate block
2569 : */
2570 : {
2571 : TimeTaker t("mapgen::make_block()");
2572 : mapgen->makeChunk(&data);
2573 : //mapgen::make_block(&data);
2574 :
2575 : if(enable_mapgen_debug_info == false)
2576 : t.stop(true); // Hide output
2577 : }
2578 :
2579 : /*
2580 : Blit data back on map, update lighting, add mobs and whatever this does
2581 : */
2582 : finishBlockMake(&data, modified_blocks);
2583 :
2584 : /*
2585 : Get central block
2586 : */
2587 : MapBlock *block = getBlockNoCreateNoEx(p);
2588 :
2589 : #if 0
2590 : /*
2591 : Check result
2592 : */
2593 : if(block)
2594 : {
2595 : bool erroneus_content = false;
2596 : for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2597 : for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2598 : for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2599 : {
2600 : v3s16 p(x0,y0,z0);
2601 : MapNode n = block->getNode(p);
2602 : if(n.getContent() == CONTENT_IGNORE)
2603 : {
2604 : infostream<<"CONTENT_IGNORE at "
2605 : <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2606 : <<std::endl;
2607 : erroneus_content = true;
2608 : assert(0);
2609 : }
2610 : }
2611 : if(erroneus_content)
2612 : {
2613 : assert(0);
2614 : }
2615 : }
2616 : #endif
2617 :
2618 : #if 0
2619 : /*
2620 : Generate a completely empty block
2621 : */
2622 : if(block)
2623 : {
2624 : for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2625 : for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2626 : {
2627 : for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2628 : {
2629 : MapNode n;
2630 : n.setContent(CONTENT_AIR);
2631 : block->setNode(v3s16(x0,y0,z0), n);
2632 : }
2633 : }
2634 : }
2635 : #endif
2636 :
2637 : if(enable_mapgen_debug_info == false)
2638 : timer.stop(true); // Hide output
2639 :
2640 : return block;
2641 : }
2642 : #endif
2643 :
2644 0 : MapBlock * ServerMap::createBlock(v3s16 p)
2645 : {
2646 0 : DSTACKF("%s: p=(%d,%d,%d)",
2647 : __FUNCTION_NAME, p.X, p.Y, p.Z);
2648 :
2649 : /*
2650 : Do not create over-limit
2651 : */
2652 0 : if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2653 0 : || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2654 0 : || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2655 0 : || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2656 0 : || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2657 0 : || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2658 0 : throw InvalidPositionException("createBlock(): pos. over limit");
2659 :
2660 0 : v2s16 p2d(p.X, p.Z);
2661 0 : s16 block_y = p.Y;
2662 : /*
2663 : This will create or load a sector if not found in memory.
2664 : If block exists on disk, it will be loaded.
2665 :
2666 : NOTE: On old save formats, this will be slow, as it generates
2667 : lighting on blocks for them.
2668 : */
2669 : ServerMapSector *sector;
2670 : try {
2671 0 : sector = (ServerMapSector*)createSector(p2d);
2672 : assert(sector->getId() == MAPSECTOR_SERVER);
2673 : }
2674 0 : catch(InvalidPositionException &e)
2675 : {
2676 0 : infostream<<"createBlock: createSector() failed"<<std::endl;
2677 0 : throw e;
2678 : }
2679 : /*
2680 : NOTE: This should not be done, or at least the exception
2681 : should not be passed on as std::exception, because it
2682 : won't be catched at all.
2683 : */
2684 : /*catch(std::exception &e)
2685 : {
2686 : infostream<<"createBlock: createSector() failed: "
2687 : <<e.what()<<std::endl;
2688 : throw e;
2689 : }*/
2690 :
2691 : /*
2692 : Try to get a block from the sector
2693 : */
2694 :
2695 0 : MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2696 0 : if(block)
2697 : {
2698 0 : if(block->isDummy())
2699 0 : block->unDummify();
2700 0 : return block;
2701 : }
2702 : // Create blank
2703 0 : block = sector->createBlankBlock(block_y);
2704 :
2705 0 : return block;
2706 : }
2707 :
2708 0 : MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2709 : {
2710 0 : DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2711 : __FUNCTION_NAME,
2712 : p.X, p.Y, p.Z, create_blank);
2713 :
2714 : {
2715 0 : MapBlock *block = getBlockNoCreateNoEx(p);
2716 0 : if(block && block->isDummy() == false)
2717 0 : return block;
2718 : }
2719 :
2720 : {
2721 0 : MapBlock *block = loadBlock(p);
2722 0 : if(block)
2723 0 : return block;
2724 : }
2725 :
2726 0 : if (create_blank) {
2727 0 : ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2728 0 : MapBlock *block = sector->createBlankBlock(p.Y);
2729 :
2730 0 : return block;
2731 : }
2732 :
2733 : #if 0
2734 : if(allow_generate)
2735 : {
2736 : std::map<v3s16, MapBlock*> modified_blocks;
2737 : MapBlock *block = generateBlock(p, modified_blocks);
2738 : if(block)
2739 : {
2740 : MapEditEvent event;
2741 : event.type = MEET_OTHER;
2742 : event.p = p;
2743 :
2744 : // Copy modified_blocks to event
2745 : for(std::map<v3s16, MapBlock*>::iterator
2746 : i = modified_blocks.begin();
2747 : i != modified_blocks.end(); ++i)
2748 : {
2749 : event.modified_blocks.insert(i->first);
2750 : }
2751 :
2752 : // Queue event
2753 : dispatchEvent(&event);
2754 :
2755 : return block;
2756 : }
2757 : }
2758 : #endif
2759 :
2760 0 : return NULL;
2761 : }
2762 :
2763 0 : MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2764 : {
2765 0 : MapBlock *block = getBlockNoCreateNoEx(p3d);
2766 0 : if (block == NULL)
2767 0 : m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2768 :
2769 0 : return block;
2770 : }
2771 :
2772 0 : void ServerMap::prepareBlock(MapBlock *block) {
2773 0 : }
2774 :
2775 : // N.B. This requires no synchronization, since data will not be modified unless
2776 : // the VoxelManipulator being updated belongs to the same thread.
2777 0 : void ServerMap::updateVManip(v3s16 pos)
2778 : {
2779 0 : Mapgen *mg = m_emerge->getCurrentMapgen();
2780 0 : if (!mg)
2781 0 : return;
2782 :
2783 0 : MMVManip *vm = mg->vm;
2784 0 : if (!vm)
2785 0 : return;
2786 :
2787 0 : if (!vm->m_area.contains(pos))
2788 0 : return;
2789 :
2790 0 : s32 idx = vm->m_area.index(pos);
2791 0 : vm->m_data[idx] = getNodeNoEx(pos);
2792 0 : vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2793 :
2794 0 : vm->m_is_dirty = true;
2795 : }
2796 :
2797 0 : s16 ServerMap::findGroundLevel(v2s16 p2d)
2798 : {
2799 : #if 0
2800 : /*
2801 : Uh, just do something random...
2802 : */
2803 : // Find existing map from top to down
2804 : s16 max=63;
2805 : s16 min=-64;
2806 : v3s16 p(p2d.X, max, p2d.Y);
2807 : for(; p.Y>min; p.Y--)
2808 : {
2809 : MapNode n = getNodeNoEx(p);
2810 : if(n.getContent() != CONTENT_IGNORE)
2811 : break;
2812 : }
2813 : if(p.Y == min)
2814 : goto plan_b;
2815 : // If this node is not air, go to plan b
2816 : if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2817 : goto plan_b;
2818 : // Search existing walkable and return it
2819 : for(; p.Y>min; p.Y--)
2820 : {
2821 : MapNode n = getNodeNoEx(p);
2822 : if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2823 : return p.Y;
2824 : }
2825 :
2826 : // Move to plan b
2827 : plan_b:
2828 : #endif
2829 :
2830 : /*
2831 : Determine from map generator noise functions
2832 : */
2833 :
2834 0 : s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2835 0 : return level;
2836 :
2837 : //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2838 : //return (s16)level;
2839 : }
2840 :
2841 0 : bool ServerMap::loadFromFolders() {
2842 0 : if (!dbase->initialized() &&
2843 0 : !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2844 0 : return true;
2845 0 : return false;
2846 : }
2847 :
2848 0 : void ServerMap::createDirs(std::string path)
2849 : {
2850 0 : if(fs::CreateAllDirs(path) == false)
2851 : {
2852 0 : m_dout<<DTIME<<"ServerMap: Failed to create directory "
2853 0 : <<"\""<<path<<"\""<<std::endl;
2854 0 : throw BaseException("ServerMap failed to create directory");
2855 : }
2856 0 : }
2857 :
2858 0 : std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2859 : {
2860 : char cc[9];
2861 0 : switch(layout)
2862 : {
2863 : case 1:
2864 0 : snprintf(cc, 9, "%.4x%.4x",
2865 0 : (unsigned int) pos.X & 0xffff,
2866 0 : (unsigned int) pos.Y & 0xffff);
2867 :
2868 0 : return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2869 : case 2:
2870 0 : snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
2871 0 : (unsigned int) pos.X & 0xfff,
2872 0 : (unsigned int) pos.Y & 0xfff);
2873 :
2874 0 : return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2875 : default:
2876 : assert(false);
2877 0 : return "";
2878 : }
2879 : }
2880 :
2881 0 : v2s16 ServerMap::getSectorPos(std::string dirname)
2882 : {
2883 0 : unsigned int x = 0, y = 0;
2884 : int r;
2885 0 : std::string component;
2886 0 : fs::RemoveLastPathComponent(dirname, &component, 1);
2887 0 : if(component.size() == 8)
2888 : {
2889 : // Old layout
2890 0 : r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2891 : }
2892 0 : else if(component.size() == 3)
2893 : {
2894 : // New layout
2895 0 : fs::RemoveLastPathComponent(dirname, &component, 2);
2896 0 : r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
2897 : // Sign-extend the 12 bit values up to 16 bits...
2898 0 : if(x & 0x800) x |= 0xF000;
2899 0 : if(y & 0x800) y |= 0xF000;
2900 : }
2901 : else
2902 : {
2903 0 : r = -1;
2904 : }
2905 :
2906 0 : FATAL_ERROR_IF(r != 2, "getSectorPos()");
2907 0 : v2s16 pos((s16)x, (s16)y);
2908 0 : return pos;
2909 : }
2910 :
2911 0 : v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2912 : {
2913 0 : v2s16 p2d = getSectorPos(sectordir);
2914 :
2915 0 : if(blockfile.size() != 4){
2916 0 : throw InvalidFilenameException("Invalid block filename");
2917 : }
2918 : unsigned int y;
2919 0 : int r = sscanf(blockfile.c_str(), "%4x", &y);
2920 0 : if(r != 1)
2921 0 : throw InvalidFilenameException("Invalid block filename");
2922 0 : return v3s16(p2d.X, y, p2d.Y);
2923 : }
2924 :
2925 0 : std::string ServerMap::getBlockFilename(v3s16 p)
2926 : {
2927 : char cc[5];
2928 0 : snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2929 0 : return cc;
2930 : }
2931 :
2932 0 : void ServerMap::save(ModifiedState save_level)
2933 : {
2934 0 : DSTACK(__FUNCTION_NAME);
2935 0 : if(m_map_saving_enabled == false) {
2936 0 : infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2937 0 : return;
2938 : }
2939 :
2940 0 : if(save_level == MOD_STATE_CLEAN)
2941 0 : infostream<<"ServerMap: Saving whole map, this can take time."
2942 0 : <<std::endl;
2943 :
2944 0 : if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
2945 0 : saveMapMeta();
2946 : }
2947 :
2948 : // Profile modified reasons
2949 0 : Profiler modprofiler;
2950 :
2951 0 : u32 sector_meta_count = 0;
2952 0 : u32 block_count = 0;
2953 0 : u32 block_count_all = 0; // Number of blocks in memory
2954 :
2955 : // Don't do anything with sqlite unless something is really saved
2956 0 : bool save_started = false;
2957 :
2958 0 : for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2959 0 : i != m_sectors.end(); ++i) {
2960 0 : ServerMapSector *sector = (ServerMapSector*)i->second;
2961 : assert(sector->getId() == MAPSECTOR_SERVER);
2962 :
2963 0 : if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
2964 0 : saveSectorMeta(sector);
2965 0 : sector_meta_count++;
2966 : }
2967 :
2968 0 : MapBlockVect blocks;
2969 0 : sector->getBlocks(blocks);
2970 :
2971 0 : for(MapBlockVect::iterator j = blocks.begin();
2972 0 : j != blocks.end(); ++j) {
2973 0 : MapBlock *block = *j;
2974 :
2975 0 : block_count_all++;
2976 :
2977 0 : if(block->getModified() >= (u32)save_level) {
2978 : // Lazy beginSave()
2979 0 : if(!save_started) {
2980 0 : beginSave();
2981 0 : save_started = true;
2982 : }
2983 :
2984 0 : modprofiler.add(block->getModifiedReasonString(), 1);
2985 :
2986 0 : saveBlock(block);
2987 0 : block_count++;
2988 :
2989 : /*infostream<<"ServerMap: Written block ("
2990 : <<block->getPos().X<<","
2991 : <<block->getPos().Y<<","
2992 : <<block->getPos().Z<<")"
2993 : <<std::endl;*/
2994 : }
2995 : }
2996 : }
2997 :
2998 0 : if(save_started)
2999 0 : endSave();
3000 :
3001 : /*
3002 : Only print if something happened or saved whole map
3003 : */
3004 0 : if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3005 0 : || block_count != 0) {
3006 0 : infostream<<"ServerMap: Written: "
3007 0 : <<sector_meta_count<<" sector metadata files, "
3008 0 : <<block_count<<" block files"
3009 0 : <<", "<<block_count_all<<" blocks in memory."
3010 0 : <<std::endl;
3011 0 : PrintInfo(infostream); // ServerMap/ClientMap:
3012 0 : infostream<<"Blocks modified by: "<<std::endl;
3013 0 : modprofiler.print(infostream);
3014 : }
3015 : }
3016 :
3017 0 : void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
3018 : {
3019 0 : if (loadFromFolders()) {
3020 0 : errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
3021 0 : << "all blocks that are stored in flat files." << std::endl;
3022 : }
3023 0 : dbase->listAllLoadableBlocks(dst);
3024 0 : }
3025 :
3026 0 : void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
3027 : {
3028 0 : for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3029 0 : si != m_sectors.end(); ++si)
3030 : {
3031 0 : MapSector *sector = si->second;
3032 :
3033 0 : MapBlockVect blocks;
3034 0 : sector->getBlocks(blocks);
3035 :
3036 0 : for(MapBlockVect::iterator i = blocks.begin();
3037 0 : i != blocks.end(); ++i) {
3038 0 : v3s16 p = (*i)->getPos();
3039 0 : dst.push_back(p);
3040 : }
3041 : }
3042 0 : }
3043 :
3044 0 : void ServerMap::saveMapMeta()
3045 : {
3046 0 : DSTACK(__FUNCTION_NAME);
3047 :
3048 0 : createDirs(m_savedir);
3049 :
3050 0 : std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3051 0 : std::ostringstream oss(std::ios_base::binary);
3052 0 : Settings conf;
3053 :
3054 0 : m_emerge->params.save(conf);
3055 0 : conf.writeLines(oss);
3056 :
3057 0 : oss << "[end_of_params]\n";
3058 :
3059 0 : if(!fs::safeWriteToFile(fullpath, oss.str())) {
3060 0 : errorstream << "ServerMap::saveMapMeta(): "
3061 0 : << "could not write " << fullpath << std::endl;
3062 0 : throw FileNotGoodException("Cannot save chunk metadata");
3063 : }
3064 :
3065 0 : m_map_metadata_changed = false;
3066 0 : }
3067 :
3068 0 : void ServerMap::loadMapMeta()
3069 : {
3070 0 : DSTACK(__FUNCTION_NAME);
3071 :
3072 0 : Settings conf;
3073 0 : std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3074 :
3075 0 : std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3076 0 : if (!is.good()) {
3077 : errorstream << "ServerMap::loadMapMeta(): "
3078 0 : "could not open " << fullpath << std::endl;
3079 0 : throw FileNotGoodException("Cannot open map metadata");
3080 : }
3081 :
3082 0 : if (!conf.parseConfigLines(is, "[end_of_params]")) {
3083 : throw SerializationError("ServerMap::loadMapMeta(): "
3084 0 : "[end_of_params] not found!");
3085 : }
3086 :
3087 0 : m_emerge->params.load(conf);
3088 :
3089 0 : verbosestream << "ServerMap::loadMapMeta(): seed="
3090 0 : << m_emerge->params.seed << std::endl;
3091 0 : }
3092 :
3093 0 : void ServerMap::saveSectorMeta(ServerMapSector *sector)
3094 : {
3095 0 : DSTACK(__FUNCTION_NAME);
3096 : // Format used for writing
3097 0 : u8 version = SER_FMT_VER_HIGHEST_WRITE;
3098 : // Get destination
3099 0 : v2s16 pos = sector->getPos();
3100 0 : std::string dir = getSectorDir(pos);
3101 0 : createDirs(dir);
3102 :
3103 0 : std::string fullpath = dir + DIR_DELIM + "meta";
3104 0 : std::ostringstream ss(std::ios_base::binary);
3105 :
3106 0 : sector->serialize(ss, version);
3107 :
3108 0 : if(!fs::safeWriteToFile(fullpath, ss.str()))
3109 0 : throw FileNotGoodException("Cannot write sector metafile");
3110 :
3111 0 : sector->differs_from_disk = false;
3112 0 : }
3113 :
3114 0 : MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3115 : {
3116 0 : DSTACK(__FUNCTION_NAME);
3117 : // Get destination
3118 0 : v2s16 p2d = getSectorPos(sectordir);
3119 :
3120 0 : ServerMapSector *sector = NULL;
3121 :
3122 0 : std::string fullpath = sectordir + DIR_DELIM + "meta";
3123 0 : std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3124 0 : if(is.good() == false)
3125 : {
3126 : // If the directory exists anyway, it probably is in some old
3127 : // format. Just go ahead and create the sector.
3128 0 : if(fs::PathExists(sectordir))
3129 : {
3130 : /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3131 : <<fullpath<<" doesn't exist but directory does."
3132 : <<" Continuing with a sector with no metadata."
3133 : <<std::endl;*/
3134 0 : sector = new ServerMapSector(this, p2d, m_gamedef);
3135 0 : m_sectors[p2d] = sector;
3136 : }
3137 : else
3138 : {
3139 0 : throw FileNotGoodException("Cannot open sector metafile");
3140 : }
3141 : }
3142 : else
3143 : {
3144 : sector = ServerMapSector::deSerialize
3145 0 : (is, this, p2d, m_sectors, m_gamedef);
3146 0 : if(save_after_load)
3147 0 : saveSectorMeta(sector);
3148 : }
3149 :
3150 0 : sector->differs_from_disk = false;
3151 :
3152 0 : return sector;
3153 : }
3154 :
3155 0 : bool ServerMap::loadSectorMeta(v2s16 p2d)
3156 : {
3157 0 : DSTACK(__FUNCTION_NAME);
3158 :
3159 : // The directory layout we're going to load from.
3160 : // 1 - original sectors/xxxxzzzz/
3161 : // 2 - new sectors2/xxx/zzz/
3162 : // If we load from anything but the latest structure, we will
3163 : // immediately save to the new one, and remove the old.
3164 0 : int loadlayout = 1;
3165 0 : std::string sectordir1 = getSectorDir(p2d, 1);
3166 0 : std::string sectordir;
3167 0 : if(fs::PathExists(sectordir1))
3168 : {
3169 0 : sectordir = sectordir1;
3170 : }
3171 : else
3172 : {
3173 0 : loadlayout = 2;
3174 0 : sectordir = getSectorDir(p2d, 2);
3175 : }
3176 :
3177 : try{
3178 0 : loadSectorMeta(sectordir, loadlayout != 2);
3179 : }
3180 0 : catch(InvalidFilenameException &e)
3181 : {
3182 0 : return false;
3183 : }
3184 0 : catch(FileNotGoodException &e)
3185 : {
3186 0 : return false;
3187 : }
3188 0 : catch(std::exception &e)
3189 : {
3190 0 : return false;
3191 : }
3192 :
3193 0 : return true;
3194 : }
3195 :
3196 : #if 0
3197 : bool ServerMap::loadSectorFull(v2s16 p2d)
3198 : {
3199 : DSTACK(__FUNCTION_NAME);
3200 :
3201 : MapSector *sector = NULL;
3202 :
3203 : // The directory layout we're going to load from.
3204 : // 1 - original sectors/xxxxzzzz/
3205 : // 2 - new sectors2/xxx/zzz/
3206 : // If we load from anything but the latest structure, we will
3207 : // immediately save to the new one, and remove the old.
3208 : int loadlayout = 1;
3209 : std::string sectordir1 = getSectorDir(p2d, 1);
3210 : std::string sectordir;
3211 : if(fs::PathExists(sectordir1))
3212 : {
3213 : sectordir = sectordir1;
3214 : }
3215 : else
3216 : {
3217 : loadlayout = 2;
3218 : sectordir = getSectorDir(p2d, 2);
3219 : }
3220 :
3221 : try{
3222 : sector = loadSectorMeta(sectordir, loadlayout != 2);
3223 : }
3224 : catch(InvalidFilenameException &e)
3225 : {
3226 : return false;
3227 : }
3228 : catch(FileNotGoodException &e)
3229 : {
3230 : return false;
3231 : }
3232 : catch(std::exception &e)
3233 : {
3234 : return false;
3235 : }
3236 :
3237 : /*
3238 : Load blocks
3239 : */
3240 : std::vector<fs::DirListNode> list2 = fs::GetDirListing
3241 : (sectordir);
3242 : std::vector<fs::DirListNode>::iterator i2;
3243 : for(i2=list2.begin(); i2!=list2.end(); i2++)
3244 : {
3245 : // We want files
3246 : if(i2->dir)
3247 : continue;
3248 : try{
3249 : loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3250 : }
3251 : catch(InvalidFilenameException &e)
3252 : {
3253 : // This catches unknown crap in directory
3254 : }
3255 : }
3256 :
3257 : if(loadlayout != 2)
3258 : {
3259 : infostream<<"Sector converted to new layout - deleting "<<
3260 : sectordir1<<std::endl;
3261 : fs::RecursiveDelete(sectordir1);
3262 : }
3263 :
3264 : return true;
3265 : }
3266 : #endif
3267 :
3268 0 : Database *ServerMap::createDatabase(const std::string &name, const std::string &savedir, Settings &conf)
3269 : {
3270 0 : if (name == "sqlite3")
3271 0 : return new Database_SQLite3(savedir);
3272 0 : if (name == "dummy")
3273 0 : return new Database_Dummy();
3274 : #if USE_LEVELDB
3275 : else if (name == "leveldb")
3276 : return new Database_LevelDB(savedir);
3277 : #endif
3278 : #if USE_REDIS
3279 : else if (name == "redis")
3280 : return new Database_Redis(conf);
3281 : #endif
3282 : else
3283 0 : throw BaseException(std::string("Database backend ") + name + " not supported.");
3284 : }
3285 :
3286 0 : void ServerMap::beginSave()
3287 : {
3288 0 : dbase->beginSave();
3289 0 : }
3290 :
3291 0 : void ServerMap::endSave()
3292 : {
3293 0 : dbase->endSave();
3294 0 : }
3295 :
3296 0 : bool ServerMap::saveBlock(MapBlock *block)
3297 : {
3298 0 : return saveBlock(block, dbase);
3299 : }
3300 :
3301 0 : bool ServerMap::saveBlock(MapBlock *block, Database *db)
3302 : {
3303 0 : v3s16 p3d = block->getPos();
3304 :
3305 : // Dummy blocks are not written
3306 0 : if (block->isDummy()) {
3307 0 : errorstream << "WARNING: saveBlock: Not writing dummy block "
3308 0 : << PP(p3d) << std::endl;
3309 0 : return true;
3310 : }
3311 :
3312 : // Format used for writing
3313 0 : u8 version = SER_FMT_VER_HIGHEST_WRITE;
3314 :
3315 : /*
3316 : [0] u8 serialization version
3317 : [1] data
3318 : */
3319 0 : std::ostringstream o(std::ios_base::binary);
3320 0 : o.write((char*) &version, 1);
3321 0 : block->serialize(o, version, true);
3322 :
3323 0 : std::string data = o.str();
3324 0 : bool ret = db->saveBlock(p3d, data);
3325 0 : if (ret) {
3326 : // We just wrote it to the disk so clear modified flag
3327 0 : block->resetModified();
3328 : }
3329 0 : return ret;
3330 : }
3331 :
3332 0 : void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
3333 : MapSector *sector, bool save_after_load)
3334 : {
3335 0 : DSTACK(__FUNCTION_NAME);
3336 :
3337 0 : std::string fullpath = sectordir + DIR_DELIM + blockfile;
3338 : try {
3339 :
3340 0 : std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3341 0 : if(is.good() == false)
3342 0 : throw FileNotGoodException("Cannot open block file");
3343 :
3344 0 : v3s16 p3d = getBlockPos(sectordir, blockfile);
3345 0 : v2s16 p2d(p3d.X, p3d.Z);
3346 :
3347 : assert(sector->getPos() == p2d);
3348 :
3349 0 : u8 version = SER_FMT_VER_INVALID;
3350 0 : is.read((char*)&version, 1);
3351 :
3352 0 : if(is.fail())
3353 : throw SerializationError("ServerMap::loadBlock(): Failed"
3354 0 : " to read MapBlock version");
3355 :
3356 : /*u32 block_size = MapBlock::serializedLength(version);
3357 : SharedBuffer<u8> data(block_size);
3358 : is.read((char*)*data, block_size);*/
3359 :
3360 : // This will always return a sector because we're the server
3361 : //MapSector *sector = emergeSector(p2d);
3362 :
3363 0 : MapBlock *block = NULL;
3364 0 : bool created_new = false;
3365 0 : block = sector->getBlockNoCreateNoEx(p3d.Y);
3366 0 : if(block == NULL)
3367 : {
3368 0 : block = sector->createBlankBlockNoInsert(p3d.Y);
3369 0 : created_new = true;
3370 : }
3371 :
3372 : // Read basic data
3373 0 : block->deSerialize(is, version, true);
3374 :
3375 : // If it's a new block, insert it to the map
3376 0 : if(created_new)
3377 0 : sector->insertBlock(block);
3378 :
3379 : /*
3380 : Save blocks loaded in old format in new format
3381 : */
3382 :
3383 0 : if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3384 : {
3385 0 : saveBlock(block);
3386 :
3387 : // Should be in database now, so delete the old file
3388 0 : fs::RecursiveDelete(fullpath);
3389 : }
3390 :
3391 : // We just loaded it from the disk, so it's up-to-date.
3392 0 : block->resetModified();
3393 :
3394 : }
3395 0 : catch(SerializationError &e)
3396 : {
3397 0 : infostream<<"WARNING: Invalid block data on disk "
3398 0 : <<"fullpath="<<fullpath
3399 0 : <<" (SerializationError). "
3400 0 : <<"what()="<<e.what()
3401 0 : <<std::endl;
3402 : // Ignoring. A new one will be generated.
3403 0 : abort();
3404 :
3405 : // TODO: Backup file; name is in fullpath.
3406 : }
3407 0 : }
3408 :
3409 0 : void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3410 : {
3411 0 : DSTACK(__FUNCTION_NAME);
3412 :
3413 : try {
3414 0 : std::istringstream is(*blob, std::ios_base::binary);
3415 :
3416 0 : u8 version = SER_FMT_VER_INVALID;
3417 0 : is.read((char*)&version, 1);
3418 :
3419 0 : if(is.fail())
3420 : throw SerializationError("ServerMap::loadBlock(): Failed"
3421 0 : " to read MapBlock version");
3422 :
3423 : /*u32 block_size = MapBlock::serializedLength(version);
3424 : SharedBuffer<u8> data(block_size);
3425 : is.read((char*)*data, block_size);*/
3426 :
3427 : // This will always return a sector because we're the server
3428 : //MapSector *sector = emergeSector(p2d);
3429 :
3430 0 : MapBlock *block = NULL;
3431 0 : bool created_new = false;
3432 0 : block = sector->getBlockNoCreateNoEx(p3d.Y);
3433 0 : if(block == NULL)
3434 : {
3435 0 : block = sector->createBlankBlockNoInsert(p3d.Y);
3436 0 : created_new = true;
3437 : }
3438 :
3439 : // Read basic data
3440 0 : block->deSerialize(is, version, true);
3441 :
3442 : // If it's a new block, insert it to the map
3443 0 : if(created_new)
3444 0 : sector->insertBlock(block);
3445 :
3446 : /*
3447 : Save blocks loaded in old format in new format
3448 : */
3449 :
3450 : //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3451 : // Only save if asked to; no need to update version
3452 0 : if(save_after_load)
3453 0 : saveBlock(block);
3454 :
3455 : // We just loaded it from, so it's up-to-date.
3456 0 : block->resetModified();
3457 :
3458 : }
3459 0 : catch(SerializationError &e)
3460 : {
3461 0 : errorstream<<"Invalid block data in database"
3462 0 : <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3463 0 : <<" (SerializationError): "<<e.what()<<std::endl;
3464 :
3465 : // TODO: Block should be marked as invalid in memory so that it is
3466 : // not touched but the game can run
3467 :
3468 0 : if(g_settings->getBool("ignore_world_load_errors")){
3469 0 : errorstream<<"Ignoring block load error. Duck and cover! "
3470 0 : <<"(ignore_world_load_errors)"<<std::endl;
3471 : } else {
3472 0 : throw SerializationError("Invalid block data in database");
3473 : }
3474 : }
3475 0 : }
3476 :
3477 0 : MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3478 : {
3479 0 : DSTACK(__FUNCTION_NAME);
3480 :
3481 0 : v2s16 p2d(blockpos.X, blockpos.Z);
3482 :
3483 0 : std::string ret;
3484 :
3485 0 : ret = dbase->loadBlock(blockpos);
3486 0 : if (ret != "") {
3487 0 : loadBlock(&ret, blockpos, createSector(p2d), false);
3488 0 : return getBlockNoCreateNoEx(blockpos);
3489 : }
3490 : // Not found in database, try the files
3491 :
3492 : // The directory layout we're going to load from.
3493 : // 1 - original sectors/xxxxzzzz/
3494 : // 2 - new sectors2/xxx/zzz/
3495 : // If we load from anything but the latest structure, we will
3496 : // immediately save to the new one, and remove the old.
3497 0 : int loadlayout = 1;
3498 0 : std::string sectordir1 = getSectorDir(p2d, 1);
3499 0 : std::string sectordir;
3500 0 : if(fs::PathExists(sectordir1))
3501 : {
3502 0 : sectordir = sectordir1;
3503 : }
3504 : else
3505 : {
3506 0 : loadlayout = 2;
3507 0 : sectordir = getSectorDir(p2d, 2);
3508 : }
3509 :
3510 : /*
3511 : Make sure sector is loaded
3512 : */
3513 0 : MapSector *sector = getSectorNoGenerateNoEx(p2d);
3514 0 : if(sector == NULL)
3515 : {
3516 : try{
3517 0 : sector = loadSectorMeta(sectordir, loadlayout != 2);
3518 : }
3519 0 : catch(InvalidFilenameException &e)
3520 : {
3521 0 : return NULL;
3522 : }
3523 0 : catch(FileNotGoodException &e)
3524 : {
3525 0 : return NULL;
3526 : }
3527 0 : catch(std::exception &e)
3528 : {
3529 0 : return NULL;
3530 : }
3531 : }
3532 :
3533 : /*
3534 : Make sure file exists
3535 : */
3536 :
3537 0 : std::string blockfilename = getBlockFilename(blockpos);
3538 0 : if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
3539 0 : return NULL;
3540 :
3541 : /*
3542 : Load block and save it to the database
3543 : */
3544 0 : loadBlock(sectordir, blockfilename, sector, true);
3545 0 : return getBlockNoCreateNoEx(blockpos);
3546 : }
3547 :
3548 0 : bool ServerMap::deleteBlock(v3s16 blockpos)
3549 : {
3550 0 : if (!dbase->deleteBlock(blockpos))
3551 0 : return false;
3552 :
3553 0 : MapBlock *block = getBlockNoCreateNoEx(blockpos);
3554 0 : if (block) {
3555 0 : v2s16 p2d(blockpos.X, blockpos.Z);
3556 0 : MapSector *sector = getSectorNoGenerateNoEx(p2d);
3557 0 : if (!sector)
3558 0 : return false;
3559 0 : sector->deleteBlock(block);
3560 : }
3561 :
3562 0 : return true;
3563 : }
3564 :
3565 0 : void ServerMap::PrintInfo(std::ostream &out)
3566 : {
3567 0 : out<<"ServerMap: ";
3568 0 : }
3569 :
3570 0 : MMVManip::MMVManip(Map *map):
3571 : VoxelManipulator(),
3572 : m_is_dirty(false),
3573 : m_create_area(false),
3574 0 : m_map(map)
3575 : {
3576 0 : }
3577 :
3578 0 : MMVManip::~MMVManip()
3579 : {
3580 0 : }
3581 :
3582 0 : void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3583 : bool load_if_inexistent)
3584 : {
3585 0 : TimeTaker timer1("initialEmerge", &emerge_time);
3586 :
3587 : // Units of these are MapBlocks
3588 0 : v3s16 p_min = blockpos_min;
3589 0 : v3s16 p_max = blockpos_max;
3590 :
3591 : VoxelArea block_area_nodes
3592 0 : (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3593 :
3594 0 : u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3595 0 : if(size_MB >= 1)
3596 : {
3597 0 : infostream<<"initialEmerge: area: ";
3598 0 : block_area_nodes.print(infostream);
3599 0 : infostream<<" ("<<size_MB<<"MB)";
3600 0 : infostream<<std::endl;
3601 : }
3602 :
3603 0 : addArea(block_area_nodes);
3604 :
3605 0 : for(s32 z=p_min.Z; z<=p_max.Z; z++)
3606 0 : for(s32 y=p_min.Y; y<=p_max.Y; y++)
3607 0 : for(s32 x=p_min.X; x<=p_max.X; x++)
3608 : {
3609 0 : u8 flags = 0;
3610 : MapBlock *block;
3611 0 : v3s16 p(x,y,z);
3612 0 : std::map<v3s16, u8>::iterator n;
3613 0 : n = m_loaded_blocks.find(p);
3614 0 : if(n != m_loaded_blocks.end())
3615 0 : continue;
3616 :
3617 0 : bool block_data_inexistent = false;
3618 : try
3619 : {
3620 0 : TimeTaker timer1("emerge load", &emerge_load_time);
3621 :
3622 0 : block = m_map->getBlockNoCreate(p);
3623 0 : if(block->isDummy())
3624 0 : block_data_inexistent = true;
3625 : else
3626 0 : block->copyTo(*this);
3627 : }
3628 0 : catch(InvalidPositionException &e)
3629 : {
3630 0 : block_data_inexistent = true;
3631 : }
3632 :
3633 0 : if(block_data_inexistent)
3634 : {
3635 :
3636 0 : if (load_if_inexistent) {
3637 0 : ServerMap *svrmap = (ServerMap *)m_map;
3638 0 : block = svrmap->emergeBlock(p, false);
3639 0 : if (block == NULL)
3640 0 : block = svrmap->createBlock(p);
3641 0 : block->copyTo(*this);
3642 : } else {
3643 0 : flags |= VMANIP_BLOCK_DATA_INEXIST;
3644 :
3645 : /*
3646 : Mark area inexistent
3647 : */
3648 0 : VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3649 : // Fill with VOXELFLAG_NO_DATA
3650 0 : for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3651 0 : for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3652 : {
3653 0 : s32 i = m_area.index(a.MinEdge.X,y,z);
3654 0 : memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3655 : }
3656 : }
3657 : }
3658 : /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3659 : {
3660 : // Mark that block was loaded as blank
3661 : flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3662 : }*/
3663 :
3664 0 : m_loaded_blocks[p] = flags;
3665 : }
3666 :
3667 0 : m_is_dirty = false;
3668 0 : }
3669 :
3670 0 : void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3671 : bool overwrite_generated)
3672 : {
3673 0 : if(m_area.getExtent() == v3s16(0,0,0))
3674 0 : return;
3675 :
3676 : /*
3677 : Copy data of all blocks
3678 : */
3679 0 : for(std::map<v3s16, u8>::iterator
3680 0 : i = m_loaded_blocks.begin();
3681 0 : i != m_loaded_blocks.end(); ++i)
3682 : {
3683 0 : v3s16 p = i->first;
3684 0 : MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3685 0 : bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3686 0 : if ((existed == false) || (block == NULL) ||
3687 0 : (overwrite_generated == false && block->isGenerated() == true))
3688 0 : continue;
3689 :
3690 0 : block->copyFrom(*this);
3691 :
3692 0 : if(modified_blocks)
3693 0 : (*modified_blocks)[p] = block;
3694 : }
3695 3 : }
3696 :
3697 : //END
|