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