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