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