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