Line data Source code
1 : /*
2 : Minetest
3 : Copyright (C) 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 "mapblock.h"
21 :
22 : #include <sstream>
23 : #include "map.h"
24 : #include "light.h"
25 : #include "nodedef.h"
26 : #include "nodemetadata.h"
27 : #include "gamedef.h"
28 : #include "log.h"
29 : #include "nameidmapping.h"
30 : #include "content_mapnode.h" // For legacy name-id mapping
31 : #include "content_nodemeta.h" // For legacy deserialization
32 : #include "serialization.h"
33 : #ifndef SERVER
34 : #include "mapblock_mesh.h"
35 : #endif
36 : #include "util/string.h"
37 : #include "util/serialize.h"
38 :
39 : #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
40 :
41 : static const char *modified_reason_strings[] = {
42 : "initial",
43 : "reallocate",
44 : "setIsUnderground",
45 : "setLightingExpired",
46 : "setGenerated",
47 : "setNode",
48 : "setNodeNoCheck",
49 : "setTimestamp",
50 : "NodeMetaRef::reportMetadataChange",
51 : "clearAllObjects",
52 : "Timestamp expired (step)",
53 : "addActiveObjectRaw",
54 : "removeRemovedObjects/remove",
55 : "removeRemovedObjects/deactivate",
56 : "Stored list cleared in activateObjects due to overflow",
57 : "deactivateFarObjects: Static data moved in",
58 : "deactivateFarObjects: Static data moved out",
59 : "deactivateFarObjects: Static data changed considerably",
60 : "finishBlockMake: expireDayNightDiff"
61 : "unknown",
62 : };
63 :
64 :
65 : /*
66 : MapBlock
67 : */
68 :
69 751 : MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
70 : m_parent(parent),
71 : m_pos(pos),
72 : m_gamedef(gamedef),
73 : m_modified(MOD_STATE_WRITE_NEEDED),
74 : m_modified_reason(MOD_REASON_INITIAL),
75 : is_underground(false),
76 : m_lighting_expired(true),
77 : m_day_night_differs(false),
78 : m_day_night_differs_expired(true),
79 : m_generated(false),
80 : m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
81 : m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
82 : m_usage_timer(0),
83 751 : m_refcount(0)
84 : {
85 751 : data = NULL;
86 751 : if(dummy == false)
87 751 : reallocate();
88 :
89 : #ifndef SERVER
90 751 : mesh = NULL;
91 : #endif
92 751 : }
93 :
94 1502 : MapBlock::~MapBlock()
95 : {
96 : #ifndef SERVER
97 : {
98 : //JMutexAutoLock lock(mesh_mutex);
99 :
100 751 : if(mesh)
101 : {
102 654 : delete mesh;
103 654 : mesh = NULL;
104 : }
105 : }
106 : #endif
107 :
108 751 : if(data)
109 751 : delete[] data;
110 751 : }
111 :
112 0 : bool MapBlock::isValidPositionParent(v3s16 p)
113 : {
114 0 : if(isValidPosition(p))
115 : {
116 0 : return true;
117 : }
118 : else{
119 0 : return m_parent->isValidPosition(getPosRelative() + p);
120 : }
121 : }
122 :
123 0 : MapNode MapBlock::getNodeParent(v3s16 p, bool *is_valid_position)
124 : {
125 0 : if (isValidPosition(p) == false)
126 0 : return m_parent->getNodeNoEx(getPosRelative() + p, is_valid_position);
127 :
128 0 : if (data == NULL) {
129 0 : if (is_valid_position)
130 0 : *is_valid_position = false;
131 0 : return MapNode(CONTENT_IGNORE);
132 : }
133 0 : if (is_valid_position)
134 0 : *is_valid_position = true;
135 0 : return data[p.Z * zstride + p.Y * ystride + p.X];
136 : }
137 :
138 0 : std::string MapBlock::getModifiedReasonString()
139 : {
140 0 : std::string reason;
141 :
142 0 : const u32 ubound = MYMIN(sizeof(m_modified_reason) * CHAR_BIT,
143 : ARRLEN(modified_reason_strings));
144 :
145 0 : for (u32 i = 0; i != ubound; i++) {
146 0 : if ((m_modified_reason & (1 << i)) == 0)
147 0 : continue;
148 :
149 0 : reason += modified_reason_strings[i];
150 0 : reason += ", ";
151 : }
152 :
153 0 : if (reason.length() > 2)
154 0 : reason.resize(reason.length() - 2);
155 :
156 0 : return reason;
157 : }
158 :
159 : /*
160 : Propagates sunlight down through the block.
161 : Doesn't modify nodes that are not affected by sunlight.
162 :
163 : Returns false if sunlight at bottom block is invalid.
164 : Returns true if sunlight at bottom block is valid.
165 : Returns true if bottom block doesn't exist.
166 :
167 : If there is a block above, continues from it.
168 : If there is no block above, assumes there is sunlight, unless
169 : is_underground is set or highest node is water.
170 :
171 : All sunlighted nodes are added to light_sources.
172 :
173 : if remove_light==true, sets non-sunlighted nodes black.
174 :
175 : if black_air_left!=NULL, it is set to true if non-sunlighted
176 : air is left in block.
177 : */
178 0 : bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources,
179 : bool remove_light, bool *black_air_left)
180 : {
181 0 : INodeDefManager *nodemgr = m_gamedef->ndef();
182 :
183 : // Whether the sunlight at the top of the bottom block is valid
184 0 : bool block_below_is_valid = true;
185 :
186 0 : v3s16 pos_relative = getPosRelative();
187 :
188 0 : for(s16 x=0; x<MAP_BLOCKSIZE; x++)
189 : {
190 0 : for(s16 z=0; z<MAP_BLOCKSIZE; z++)
191 : {
192 : #if 1
193 0 : bool no_sunlight = false;
194 : //bool no_top_block = false;
195 :
196 : // Check if node above block has sunlight
197 :
198 : bool is_valid_position;
199 : MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z),
200 0 : &is_valid_position);
201 0 : if (is_valid_position)
202 : {
203 0 : if(n.getContent() == CONTENT_IGNORE)
204 : {
205 : // Trust heuristics
206 0 : no_sunlight = is_underground;
207 : }
208 0 : else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
209 : {
210 0 : no_sunlight = true;
211 : }
212 : }
213 : else
214 : {
215 : //no_top_block = true;
216 :
217 : // NOTE: This makes over-ground roofed places sunlighted
218 : // Assume sunlight, unless is_underground==true
219 0 : if(is_underground)
220 : {
221 0 : no_sunlight = true;
222 : }
223 : else
224 : {
225 0 : MapNode n = getNodeNoEx(v3s16(x, MAP_BLOCKSIZE-1, z));
226 0 : if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
227 : {
228 0 : no_sunlight = true;
229 : }
230 : }
231 : // NOTE: As of now, this just would make everything dark.
232 : // No sunlight here
233 : //no_sunlight = true;
234 : }
235 : #endif
236 : #if 0 // Doesn't work; nothing gets light.
237 : bool no_sunlight = true;
238 : bool no_top_block = false;
239 : // Check if node above block has sunlight
240 : try{
241 : MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
242 : if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
243 : {
244 : no_sunlight = false;
245 : }
246 : }
247 : catch(InvalidPositionException &e)
248 : {
249 : no_top_block = true;
250 : }
251 : #endif
252 :
253 : /*std::cout<<"("<<x<<","<<z<<"): "
254 : <<"no_top_block="<<no_top_block
255 : <<", is_underground="<<is_underground
256 : <<", no_sunlight="<<no_sunlight
257 : <<std::endl;*/
258 :
259 0 : s16 y = MAP_BLOCKSIZE-1;
260 :
261 : // This makes difference to diminishing in water.
262 0 : bool stopped_to_solid_object = false;
263 :
264 0 : u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
265 :
266 0 : for(; y >= 0; y--)
267 : {
268 0 : v3s16 pos(x, y, z);
269 0 : MapNode &n = getNodeRef(pos);
270 :
271 0 : if(current_light == 0)
272 : {
273 : // Do nothing
274 : }
275 0 : else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
276 : {
277 : // Do nothing: Sunlight is continued
278 : }
279 0 : else if(nodemgr->get(n).light_propagates == false)
280 : {
281 : // A solid object is on the way.
282 0 : stopped_to_solid_object = true;
283 :
284 : // Light stops.
285 0 : current_light = 0;
286 : }
287 : else
288 : {
289 : // Diminish light
290 0 : current_light = diminish_light(current_light);
291 : }
292 :
293 0 : u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
294 :
295 0 : if(current_light > old_light || remove_light)
296 : {
297 0 : n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
298 : }
299 :
300 0 : if(diminish_light(current_light) != 0)
301 : {
302 0 : light_sources.insert(pos_relative + pos);
303 : }
304 :
305 0 : if(current_light == 0 && stopped_to_solid_object)
306 : {
307 0 : if(black_air_left)
308 : {
309 0 : *black_air_left = true;
310 : }
311 : }
312 : }
313 :
314 : // Whether or not the block below should see LIGHT_SUN
315 0 : bool sunlight_should_go_down = (current_light == LIGHT_SUN);
316 :
317 : /*
318 : If the block below hasn't already been marked invalid:
319 :
320 : Check if the node below the block has proper sunlight at top.
321 : If not, the block below is invalid.
322 :
323 : Ignore non-transparent nodes as they always have no light
324 : */
325 :
326 0 : if(block_below_is_valid)
327 : {
328 0 : MapNode n = getNodeParent(v3s16(x, -1, z), &is_valid_position);
329 0 : if (is_valid_position) {
330 0 : if(nodemgr->get(n).light_propagates)
331 : {
332 0 : if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
333 0 : && sunlight_should_go_down == false)
334 0 : block_below_is_valid = false;
335 0 : else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
336 0 : && sunlight_should_go_down == true)
337 0 : block_below_is_valid = false;
338 : }
339 : }
340 : else
341 : {
342 : /*std::cout<<"InvalidBlockException for bottom block node"
343 : <<std::endl;*/
344 : // Just no block below, no need to panic.
345 : }
346 : }
347 : }
348 : }
349 :
350 0 : return block_below_is_valid;
351 : }
352 :
353 :
354 56320 : void MapBlock::copyTo(VoxelManipulator &dst)
355 : {
356 56320 : v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
357 56320 : VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
358 :
359 : // Copy from data to VoxelManipulator
360 225280 : dst.copyFrom(data, data_area, v3s16(0,0,0),
361 168960 : getPosRelative(), data_size);
362 56320 : }
363 :
364 0 : void MapBlock::copyFrom(VoxelManipulator &dst)
365 : {
366 0 : v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
367 0 : VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
368 :
369 : // Copy from VoxelManipulator to data
370 0 : dst.copyTo(data, data_area, v3s16(0,0,0),
371 0 : getPosRelative(), data_size);
372 0 : }
373 :
374 0 : void MapBlock::actuallyUpdateDayNightDiff()
375 : {
376 0 : INodeDefManager *nodemgr = m_gamedef->ndef();
377 :
378 : // Running this function un-expires m_day_night_differs
379 0 : m_day_night_differs_expired = false;
380 :
381 0 : if (data == NULL) {
382 0 : m_day_night_differs = false;
383 0 : return;
384 : }
385 :
386 : bool differs;
387 :
388 : /*
389 : Check if any lighting value differs
390 : */
391 0 : for (u32 i = 0; i < nodecount; i++) {
392 0 : MapNode &n = data[i];
393 :
394 0 : differs = !n.isLightDayNightEq(nodemgr);
395 0 : if (differs)
396 0 : break;
397 : }
398 :
399 : /*
400 : If some lighting values differ, check if the whole thing is
401 : just air. If it is just air, differs = false
402 : */
403 0 : if (differs) {
404 0 : bool only_air = true;
405 0 : for (u32 i = 0; i < nodecount; i++) {
406 0 : MapNode &n = data[i];
407 0 : if (n.getContent() != CONTENT_AIR) {
408 0 : only_air = false;
409 0 : break;
410 : }
411 : }
412 0 : if (only_air)
413 0 : differs = false;
414 : }
415 :
416 : // Set member variable
417 0 : m_day_night_differs = differs;
418 : }
419 :
420 108 : void MapBlock::expireDayNightDiff()
421 : {
422 : //INodeDefManager *nodemgr = m_gamedef->ndef();
423 :
424 108 : if(data == NULL){
425 0 : m_day_night_differs = false;
426 0 : m_day_night_differs_expired = false;
427 0 : return;
428 : }
429 :
430 108 : m_day_night_differs_expired = true;
431 : }
432 :
433 0 : s16 MapBlock::getGroundLevel(v2s16 p2d)
434 : {
435 0 : if(isDummy())
436 0 : return -3;
437 : try
438 : {
439 0 : s16 y = MAP_BLOCKSIZE-1;
440 0 : for(; y>=0; y--)
441 : {
442 0 : MapNode n = getNodeRef(p2d.X, y, p2d.Y);
443 0 : if(m_gamedef->ndef()->get(n).walkable)
444 : {
445 0 : if(y == MAP_BLOCKSIZE-1)
446 0 : return -2;
447 : else
448 0 : return y;
449 : }
450 : }
451 0 : return -1;
452 : }
453 0 : catch(InvalidPositionException &e)
454 : {
455 0 : return -3;
456 : }
457 : }
458 :
459 : /*
460 : Serialization
461 : */
462 : // List relevant id-name pairs for ids in the block using nodedef
463 : // Renumbers the content IDs (starting at 0 and incrementing
464 : // use static memory requires about 65535 * sizeof(int) ram in order to be
465 : // sure we can handle all content ids. But it's absolutely worth it as it's
466 : // a speedup of 4 for one of the major time consuming functions on storing
467 : // mapblocks.
468 : static content_t getBlockNodeIdMapping_mapping[USHRT_MAX + 1];
469 0 : static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
470 : INodeDefManager *nodedef)
471 : {
472 0 : memset(getBlockNodeIdMapping_mapping, 0xFF, (USHRT_MAX + 1) * sizeof(content_t));
473 :
474 0 : std::set<content_t> unknown_contents;
475 0 : content_t id_counter = 0;
476 0 : for (u32 i = 0; i < MapBlock::nodecount; i++) {
477 0 : content_t global_id = nodes[i].getContent();
478 0 : content_t id = CONTENT_IGNORE;
479 :
480 : // Try to find an existing mapping
481 0 : if (getBlockNodeIdMapping_mapping[global_id] != 0xFFFF) {
482 0 : id = getBlockNodeIdMapping_mapping[global_id];
483 : }
484 : else
485 : {
486 : // We have to assign a new mapping
487 0 : id = id_counter++;
488 0 : getBlockNodeIdMapping_mapping[global_id] = id;
489 :
490 0 : const ContentFeatures &f = nodedef->get(global_id);
491 0 : const std::string &name = f.name;
492 0 : if(name == "")
493 0 : unknown_contents.insert(global_id);
494 : else
495 0 : nimap->set(id, name);
496 : }
497 :
498 : // Update the MapNode
499 0 : nodes[i].setContent(id);
500 : }
501 0 : for(std::set<content_t>::const_iterator
502 0 : i = unknown_contents.begin();
503 0 : i != unknown_contents.end(); i++){
504 0 : errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
505 0 : <<"Name for node id "<<(*i)<<" not known"<<std::endl;
506 : }
507 0 : }
508 : // Correct ids in the block to match nodedef based on names.
509 : // Unknown ones are added to nodedef.
510 : // Will not update itself to match id-name pairs in nodedef.
511 0 : static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
512 : IGameDef *gamedef)
513 : {
514 0 : INodeDefManager *nodedef = gamedef->ndef();
515 : // This means the block contains incorrect ids, and we contain
516 : // the information to convert those to names.
517 : // nodedef contains information to convert our names to globally
518 : // correct ids.
519 0 : std::set<content_t> unnamed_contents;
520 0 : std::set<std::string> unallocatable_contents;
521 0 : for (u32 i = 0; i < MapBlock::nodecount; i++) {
522 0 : content_t local_id = nodes[i].getContent();
523 0 : std::string name;
524 0 : bool found = nimap->getName(local_id, name);
525 0 : if(!found){
526 0 : unnamed_contents.insert(local_id);
527 0 : continue;
528 : }
529 : content_t global_id;
530 0 : found = nodedef->getId(name, global_id);
531 0 : if(!found){
532 0 : global_id = gamedef->allocateUnknownNodeId(name);
533 0 : if(global_id == CONTENT_IGNORE){
534 0 : unallocatable_contents.insert(name);
535 0 : continue;
536 : }
537 : }
538 0 : nodes[i].setContent(global_id);
539 : }
540 0 : for(std::set<content_t>::const_iterator
541 0 : i = unnamed_contents.begin();
542 0 : i != unnamed_contents.end(); i++){
543 0 : errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
544 0 : <<"Block contains id "<<(*i)
545 0 : <<" with no name mapping"<<std::endl;
546 : }
547 0 : for(std::set<std::string>::const_iterator
548 0 : i = unallocatable_contents.begin();
549 0 : i != unallocatable_contents.end(); i++){
550 0 : errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
551 0 : <<"Could not allocate global id for node name \""
552 0 : <<(*i)<<"\""<<std::endl;
553 : }
554 0 : }
555 :
556 0 : void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
557 : {
558 0 : if(!ser_ver_supported(version))
559 0 : throw VersionMismatchException("ERROR: MapBlock format not supported");
560 :
561 0 : if(data == NULL)
562 : {
563 0 : throw SerializationError("ERROR: Not writing dummy block.");
564 : }
565 :
566 0 : FATAL_ERROR_IF(version < SER_FMT_CLIENT_VER_LOWEST, "Serialize version error");
567 :
568 : // First byte
569 0 : u8 flags = 0;
570 0 : if(is_underground)
571 0 : flags |= 0x01;
572 0 : if(getDayNightDiff())
573 0 : flags |= 0x02;
574 0 : if(m_lighting_expired)
575 0 : flags |= 0x04;
576 0 : if(m_generated == false)
577 0 : flags |= 0x08;
578 0 : writeU8(os, flags);
579 :
580 : /*
581 : Bulk node data
582 : */
583 0 : NameIdMapping nimap;
584 0 : if(disk)
585 : {
586 0 : MapNode *tmp_nodes = new MapNode[nodecount];
587 0 : for(u32 i=0; i<nodecount; i++)
588 0 : tmp_nodes[i] = data[i];
589 0 : getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
590 :
591 0 : u8 content_width = 2;
592 0 : u8 params_width = 2;
593 0 : writeU8(os, content_width);
594 0 : writeU8(os, params_width);
595 0 : MapNode::serializeBulk(os, version, tmp_nodes, nodecount,
596 0 : content_width, params_width, true);
597 0 : delete[] tmp_nodes;
598 : }
599 : else
600 : {
601 0 : u8 content_width = 2;
602 0 : u8 params_width = 2;
603 0 : writeU8(os, content_width);
604 0 : writeU8(os, params_width);
605 0 : MapNode::serializeBulk(os, version, data, nodecount,
606 0 : content_width, params_width, true);
607 : }
608 :
609 : /*
610 : Node metadata
611 : */
612 0 : std::ostringstream oss(std::ios_base::binary);
613 0 : m_node_metadata.serialize(oss);
614 0 : compressZlib(oss.str(), os);
615 :
616 : /*
617 : Data that goes to disk, but not the network
618 : */
619 0 : if(disk)
620 : {
621 0 : if(version <= 24){
622 : // Node timers
623 0 : m_node_timers.serialize(os, version);
624 : }
625 :
626 : // Static objects
627 0 : m_static_objects.serialize(os);
628 :
629 : // Timestamp
630 0 : writeU32(os, getTimestamp());
631 :
632 : // Write block-specific node definition id mapping
633 0 : nimap.serialize(os);
634 :
635 0 : if(version >= 25){
636 : // Node timers
637 0 : m_node_timers.serialize(os, version);
638 : }
639 : }
640 0 : }
641 :
642 0 : void MapBlock::serializeNetworkSpecific(std::ostream &os, u16 net_proto_version)
643 : {
644 0 : if(data == NULL)
645 : {
646 0 : throw SerializationError("ERROR: Not writing dummy block.");
647 : }
648 :
649 0 : if(net_proto_version >= 21){
650 0 : int version = 1;
651 0 : writeU8(os, version);
652 0 : writeF1000(os, 0); // deprecated heat
653 0 : writeF1000(os, 0); // deprecated humidity
654 : }
655 0 : }
656 :
657 786 : void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
658 : {
659 786 : if(!ser_ver_supported(version))
660 0 : throw VersionMismatchException("ERROR: MapBlock format not supported");
661 :
662 786 : TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())<<std::endl);
663 :
664 786 : m_day_night_differs_expired = false;
665 :
666 786 : if(version <= 21)
667 : {
668 0 : deSerialize_pre22(is, version, disk);
669 0 : return;
670 : }
671 :
672 786 : u8 flags = readU8(is);
673 786 : is_underground = (flags & 0x01) ? true : false;
674 786 : m_day_night_differs = (flags & 0x02) ? true : false;
675 786 : m_lighting_expired = (flags & 0x04) ? true : false;
676 786 : m_generated = (flags & 0x08) ? false : true;
677 :
678 : /*
679 : Bulk node data
680 : */
681 786 : TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
682 : <<": Bulk node data"<<std::endl);
683 786 : u8 content_width = readU8(is);
684 786 : u8 params_width = readU8(is);
685 786 : if(content_width != 1 && content_width != 2)
686 0 : throw SerializationError("MapBlock::deSerialize(): invalid content_width");
687 786 : if(params_width != 2)
688 0 : throw SerializationError("MapBlock::deSerialize(): invalid params_width");
689 786 : MapNode::deSerializeBulk(is, version, data, nodecount,
690 786 : content_width, params_width, true);
691 :
692 : /*
693 : NodeMetadata
694 : */
695 786 : TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
696 : <<": Node metadata"<<std::endl);
697 : // Ignore errors
698 : try{
699 1572 : std::ostringstream oss(std::ios_base::binary);
700 786 : decompressZlib(is, oss);
701 1572 : std::istringstream iss(oss.str(), std::ios_base::binary);
702 786 : if(version >= 23)
703 786 : m_node_metadata.deSerialize(iss, m_gamedef);
704 : else
705 0 : content_nodemeta_deserialize_legacy(iss,
706 : &m_node_metadata, &m_node_timers,
707 0 : m_gamedef);
708 : }
709 0 : catch(SerializationError &e)
710 : {
711 0 : errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
712 0 : <<" while deserializing node metadata at ("
713 0 : <<PP(getPos())<<": "<<e.what()<<std::endl;
714 : }
715 :
716 : /*
717 : Data that is only on disk
718 : */
719 786 : if(disk)
720 : {
721 : // Node timers
722 0 : if(version == 23){
723 : // Read unused zero
724 0 : readU8(is);
725 : }
726 0 : if(version == 24){
727 0 : TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
728 : <<": Node timers (ver==24)"<<std::endl);
729 0 : m_node_timers.deSerialize(is, version);
730 : }
731 :
732 : // Static objects
733 0 : TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
734 : <<": Static objects"<<std::endl);
735 0 : m_static_objects.deSerialize(is);
736 :
737 : // Timestamp
738 0 : TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
739 : <<": Timestamp"<<std::endl);
740 0 : setTimestamp(readU32(is));
741 0 : m_disk_timestamp = m_timestamp;
742 :
743 : // Dynamically re-set ids based on node names
744 0 : TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
745 : <<": NameIdMapping"<<std::endl);
746 0 : NameIdMapping nimap;
747 0 : nimap.deSerialize(is);
748 0 : correctBlockNodeIds(&nimap, data, m_gamedef);
749 :
750 0 : if(version >= 25){
751 0 : TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
752 : <<": Node timers (ver>=25)"<<std::endl);
753 0 : m_node_timers.deSerialize(is, version);
754 : }
755 : }
756 :
757 786 : TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
758 : <<": Done."<<std::endl);
759 : }
760 :
761 786 : void MapBlock::deSerializeNetworkSpecific(std::istream &is)
762 : {
763 : try {
764 786 : int version = readU8(is);
765 : //if(version != 1)
766 : // throw SerializationError("unsupported MapBlock version");
767 786 : if(version >= 1) {
768 786 : readF1000(is); // deprecated heat
769 786 : readF1000(is); // deprecated humidity
770 : }
771 : }
772 0 : catch(SerializationError &e)
773 : {
774 0 : errorstream<<"WARNING: MapBlock::deSerializeNetworkSpecific(): Ignoring an error"
775 0 : <<": "<<e.what()<<std::endl;
776 : }
777 786 : }
778 :
779 : /*
780 : Legacy serialization
781 : */
782 :
783 0 : void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
784 : {
785 : // Initialize default flags
786 0 : is_underground = false;
787 0 : m_day_night_differs = false;
788 0 : m_lighting_expired = false;
789 0 : m_generated = true;
790 :
791 : // Make a temporary buffer
792 0 : u32 ser_length = MapNode::serializedLength(version);
793 0 : SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
794 :
795 : // These have no compression
796 0 : if(version <= 3 || version == 5 || version == 6)
797 : {
798 : char tmp;
799 0 : is.read(&tmp, 1);
800 0 : if(is.gcount() != 1)
801 : throw SerializationError
802 0 : ("MapBlock::deSerialize: no enough input data");
803 0 : is_underground = tmp;
804 0 : is.read((char*)*databuf_nodelist, nodecount * ser_length);
805 0 : if((u32)is.gcount() != nodecount * ser_length)
806 : throw SerializationError
807 0 : ("MapBlock::deSerialize: no enough input data");
808 : }
809 0 : else if(version <= 10)
810 : {
811 : u8 t8;
812 0 : is.read((char*)&t8, 1);
813 0 : is_underground = t8;
814 :
815 : {
816 : // Uncompress and set material data
817 0 : std::ostringstream os(std::ios_base::binary);
818 0 : decompress(is, os, version);
819 0 : std::string s = os.str();
820 0 : if(s.size() != nodecount)
821 : throw SerializationError
822 0 : ("MapBlock::deSerialize: invalid format");
823 0 : for(u32 i=0; i<s.size(); i++)
824 : {
825 0 : databuf_nodelist[i*ser_length] = s[i];
826 : }
827 : }
828 : {
829 : // Uncompress and set param data
830 0 : std::ostringstream os(std::ios_base::binary);
831 0 : decompress(is, os, version);
832 0 : std::string s = os.str();
833 0 : if(s.size() != nodecount)
834 : throw SerializationError
835 0 : ("MapBlock::deSerialize: invalid format");
836 0 : for(u32 i=0; i<s.size(); i++)
837 : {
838 0 : databuf_nodelist[i*ser_length + 1] = s[i];
839 : }
840 : }
841 :
842 0 : if(version >= 10)
843 : {
844 : // Uncompress and set param2 data
845 0 : std::ostringstream os(std::ios_base::binary);
846 0 : decompress(is, os, version);
847 0 : std::string s = os.str();
848 0 : if(s.size() != nodecount)
849 : throw SerializationError
850 0 : ("MapBlock::deSerialize: invalid format");
851 0 : for(u32 i=0; i<s.size(); i++)
852 : {
853 0 : databuf_nodelist[i*ser_length + 2] = s[i];
854 : }
855 : }
856 : }
857 : // All other versions (newest)
858 : else
859 : {
860 : u8 flags;
861 0 : is.read((char*)&flags, 1);
862 0 : is_underground = (flags & 0x01) ? true : false;
863 0 : m_day_night_differs = (flags & 0x02) ? true : false;
864 0 : m_lighting_expired = (flags & 0x04) ? true : false;
865 0 : if(version >= 18)
866 0 : m_generated = (flags & 0x08) ? false : true;
867 :
868 : // Uncompress data
869 0 : std::ostringstream os(std::ios_base::binary);
870 0 : decompress(is, os, version);
871 0 : std::string s = os.str();
872 0 : if(s.size() != nodecount*3)
873 : throw SerializationError
874 : ("MapBlock::deSerialize: decompress resulted in size"
875 0 : " other than nodecount*3");
876 :
877 : // deserialize nodes from buffer
878 0 : for(u32 i=0; i<nodecount; i++)
879 : {
880 0 : databuf_nodelist[i*ser_length] = s[i];
881 0 : databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
882 0 : databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
883 : }
884 :
885 : /*
886 : NodeMetadata
887 : */
888 0 : if(version >= 14)
889 : {
890 : // Ignore errors
891 : try{
892 0 : if(version <= 15)
893 : {
894 0 : std::string data = deSerializeString(is);
895 0 : std::istringstream iss(data, std::ios_base::binary);
896 0 : content_nodemeta_deserialize_legacy(iss,
897 : &m_node_metadata, &m_node_timers,
898 0 : m_gamedef);
899 : }
900 : else
901 : {
902 : //std::string data = deSerializeLongString(is);
903 0 : std::ostringstream oss(std::ios_base::binary);
904 0 : decompressZlib(is, oss);
905 0 : std::istringstream iss(oss.str(), std::ios_base::binary);
906 0 : content_nodemeta_deserialize_legacy(iss,
907 : &m_node_metadata, &m_node_timers,
908 0 : m_gamedef);
909 : }
910 : }
911 0 : catch(SerializationError &e)
912 : {
913 0 : errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
914 0 : <<" while deserializing node metadata"<<std::endl;
915 : }
916 : }
917 : }
918 :
919 : // Deserialize node data
920 0 : for(u32 i=0; i<nodecount; i++)
921 : {
922 0 : data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
923 : }
924 :
925 0 : if(disk)
926 : {
927 : /*
928 : Versions up from 9 have block objects. (DEPRECATED)
929 : */
930 0 : if(version >= 9){
931 0 : u16 count = readU16(is);
932 : // Not supported and length not known if count is not 0
933 0 : if(count != 0){
934 0 : errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
935 0 : <<"Ignoring stuff coming at and after MBOs"<<std::endl;
936 0 : return;
937 : }
938 : }
939 :
940 : /*
941 : Versions up from 15 have static objects.
942 : */
943 0 : if(version >= 15)
944 0 : m_static_objects.deSerialize(is);
945 :
946 : // Timestamp
947 0 : if(version >= 17){
948 0 : setTimestamp(readU32(is));
949 0 : m_disk_timestamp = m_timestamp;
950 : } else {
951 0 : setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
952 : }
953 :
954 : // Dynamically re-set ids based on node names
955 0 : NameIdMapping nimap;
956 : // If supported, read node definition id mapping
957 0 : if(version >= 21){
958 0 : nimap.deSerialize(is);
959 : // Else set the legacy mapping
960 : } else {
961 0 : content_mapnode_get_name_id_mapping(&nimap);
962 : }
963 0 : correctBlockNodeIds(&nimap, data, m_gamedef);
964 : }
965 :
966 :
967 : // Legacy data changes
968 : // This code has to convert from pre-22 to post-22 format.
969 0 : INodeDefManager *nodedef = m_gamedef->ndef();
970 0 : for(u32 i=0; i<nodecount; i++)
971 : {
972 0 : const ContentFeatures &f = nodedef->get(data[i].getContent());
973 : // Mineral
974 0 : if(nodedef->getId("default:stone") == data[i].getContent()
975 0 : && data[i].getParam1() == 1)
976 : {
977 0 : data[i].setContent(nodedef->getId("default:stone_with_coal"));
978 0 : data[i].setParam1(0);
979 : }
980 0 : else if(nodedef->getId("default:stone") == data[i].getContent()
981 0 : && data[i].getParam1() == 2)
982 : {
983 0 : data[i].setContent(nodedef->getId("default:stone_with_iron"));
984 0 : data[i].setParam1(0);
985 : }
986 : // facedir_simple
987 0 : if(f.legacy_facedir_simple)
988 : {
989 0 : data[i].setParam2(data[i].getParam1());
990 0 : data[i].setParam1(0);
991 : }
992 : // wall_mounted
993 0 : if(f.legacy_wallmounted)
994 : {
995 0 : u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
996 0 : u8 dir_old_format = data[i].getParam2();
997 0 : u8 dir_new_format = 0;
998 0 : for(u8 j=0; j<8; j++)
999 : {
1000 0 : if((dir_old_format & wallmounted_new_to_old[j]) != 0)
1001 : {
1002 0 : dir_new_format = j;
1003 0 : break;
1004 : }
1005 : }
1006 0 : data[i].setParam2(dir_new_format);
1007 : }
1008 : }
1009 :
1010 : }
1011 :
1012 : /*
1013 : Get a quick string to describe what a block actually contains
1014 : */
1015 0 : std::string analyze_block(MapBlock *block)
1016 : {
1017 0 : if(block == NULL)
1018 0 : return "NULL";
1019 :
1020 0 : std::ostringstream desc;
1021 :
1022 0 : v3s16 p = block->getPos();
1023 : char spos[20];
1024 0 : snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1025 0 : desc<<spos;
1026 :
1027 0 : switch(block->getModified())
1028 : {
1029 : case MOD_STATE_CLEAN:
1030 0 : desc<<"CLEAN, ";
1031 0 : break;
1032 : case MOD_STATE_WRITE_AT_UNLOAD:
1033 0 : desc<<"WRITE_AT_UNLOAD, ";
1034 0 : break;
1035 : case MOD_STATE_WRITE_NEEDED:
1036 0 : desc<<"WRITE_NEEDED, ";
1037 0 : break;
1038 : default:
1039 0 : desc<<"unknown getModified()="+itos(block->getModified())+", ";
1040 : }
1041 :
1042 0 : if(block->isGenerated())
1043 0 : desc<<"is_gen [X], ";
1044 : else
1045 0 : desc<<"is_gen [ ], ";
1046 :
1047 0 : if(block->getIsUnderground())
1048 0 : desc<<"is_ug [X], ";
1049 : else
1050 0 : desc<<"is_ug [ ], ";
1051 :
1052 0 : if(block->getLightingExpired())
1053 0 : desc<<"lighting_exp [X], ";
1054 : else
1055 0 : desc<<"lighting_exp [ ], ";
1056 :
1057 0 : if(block->isDummy())
1058 : {
1059 0 : desc<<"Dummy, ";
1060 : }
1061 : else
1062 : {
1063 0 : bool full_ignore = true;
1064 0 : bool some_ignore = false;
1065 0 : bool full_air = true;
1066 0 : bool some_air = false;
1067 0 : for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1068 0 : for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1069 0 : for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1070 : {
1071 0 : v3s16 p(x0,y0,z0);
1072 0 : MapNode n = block->getNodeNoEx(p);
1073 0 : content_t c = n.getContent();
1074 0 : if(c == CONTENT_IGNORE)
1075 0 : some_ignore = true;
1076 : else
1077 0 : full_ignore = false;
1078 0 : if(c == CONTENT_AIR)
1079 0 : some_air = true;
1080 : else
1081 0 : full_air = false;
1082 : }
1083 :
1084 0 : desc<<"content {";
1085 :
1086 0 : std::ostringstream ss;
1087 :
1088 0 : if(full_ignore)
1089 0 : ss<<"IGNORE (full), ";
1090 0 : else if(some_ignore)
1091 0 : ss<<"IGNORE, ";
1092 :
1093 0 : if(full_air)
1094 0 : ss<<"AIR (full), ";
1095 0 : else if(some_air)
1096 0 : ss<<"AIR, ";
1097 :
1098 0 : if(ss.str().size()>=2)
1099 0 : desc<<ss.str().substr(0, ss.str().size()-2);
1100 :
1101 0 : desc<<"}, ";
1102 : }
1103 :
1104 0 : return desc.str().substr(0, desc.str().size()-2);
1105 3 : }
1106 :
1107 :
1108 : //END
|