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