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