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