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