initial steps in doing content type extension
[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_objects(this),
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.d == CONTENT_IGNORE || n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
246                                 {
247                                         no_sunlight = true;
248                                 }
249                         }
250                         catch(InvalidPositionException &e)
251                         {
252                                 no_top_block = true;
253                                 
254                                 // NOTE: This makes over-ground roofed places sunlighted
255                                 // Assume sunlight, unless is_underground==true
256                                 if(is_underground)
257                                 {
258                                         no_sunlight = true;
259                                 }
260                                 else
261                                 {
262                                         MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
263                                         //if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
264                                         if(content_features(n.d).sunlight_propagates == false)
265                                         {
266                                                 no_sunlight = true;
267                                         }
268                                 }
269                                 // NOTE: As of now, this just would make everything dark.
270                                 // No sunlight here
271                                 //no_sunlight = true;
272                         }
273 #endif
274 #if 0 // Doesn't work; nothing gets light.
275                         bool no_sunlight = true;
276                         bool no_top_block = false;
277                         // Check if node above block has sunlight
278                         try{
279                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
280                                 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
281                                 {
282                                         no_sunlight = false;
283                                 }
284                         }
285                         catch(InvalidPositionException &e)
286                         {
287                                 no_top_block = true;
288                         }
289 #endif
290
291                         /*std::cout<<"("<<x<<","<<z<<"): "
292                                         <<"no_top_block="<<no_top_block
293                                         <<", is_underground="<<is_underground
294                                         <<", no_sunlight="<<no_sunlight
295                                         <<std::endl;*/
296                 
297                         s16 y = MAP_BLOCKSIZE-1;
298                         
299                         // This makes difference to diminishing in water.
300                         bool stopped_to_solid_object = false;
301                         
302                         u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
303
304                         for(; y >= 0; y--)
305                         {
306                                 v3s16 pos(x, y, z);
307                                 MapNode &n = getNodeRef(pos);
308                                 
309                                 if(current_light == 0)
310                                 {
311                                         // Do nothing
312                                 }
313                                 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
314                                 {
315                                         // Do nothing: Sunlight is continued
316                                 }
317                                 else if(n.light_propagates() == false)
318                                 {
319                                         /*// DEPRECATED TODO: REMOVE
320                                         if(grow_grass)
321                                         {
322                                                 bool upper_is_air = false;
323                                                 try
324                                                 {
325                                                         if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
326                                                                 upper_is_air = true;
327                                                 }
328                                                 catch(InvalidPositionException &e)
329                                                 {
330                                                 }
331                                                 // Turn mud into grass
332                                                 if(upper_is_air && n.d == CONTENT_MUD
333                                                                 && current_light == LIGHT_SUN)
334                                                 {
335                                                         n.d = CONTENT_GRASS;
336                                                 }
337                                         }*/
338
339                                         // A solid object is on the way.
340                                         stopped_to_solid_object = true;
341                                         
342                                         // Light stops.
343                                         current_light = 0;
344                                 }
345                                 else
346                                 {
347                                         // Diminish light
348                                         current_light = diminish_light(current_light);
349                                 }
350
351                                 u8 old_light = n.getLight(LIGHTBANK_DAY);
352
353                                 if(current_light > old_light || remove_light)
354                                 {
355                                         n.setLight(LIGHTBANK_DAY, current_light);
356                                 }
357                                 
358                                 if(diminish_light(current_light) != 0)
359                                 {
360                                         light_sources.insert(pos_relative + pos, true);
361                                 }
362
363                                 if(current_light == 0 && stopped_to_solid_object)
364                                 {
365                                         if(black_air_left)
366                                         {
367                                                 *black_air_left = true;
368                                         }
369                                 }
370                         }
371
372                         // Whether or not the block below should see LIGHT_SUN
373                         bool sunlight_should_go_down = (current_light == LIGHT_SUN);
374
375                         /*
376                                 If the block below hasn't already been marked invalid:
377
378                                 Check if the node below the block has proper sunlight at top.
379                                 If not, the block below is invalid.
380                                 
381                                 Ignore non-transparent nodes as they always have no light
382                         */
383                         try
384                         {
385                         if(block_below_is_valid)
386                         {
387                                 MapNode n = getNodeParent(v3s16(x, -1, z));
388                                 if(n.light_propagates())
389                                 {
390                                         if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
391                                                         && sunlight_should_go_down == false)
392                                                 block_below_is_valid = false;
393                                         else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
394                                                         && sunlight_should_go_down == true)
395                                                 block_below_is_valid = false;
396                                 }
397                         }//if
398                         }//try
399                         catch(InvalidPositionException &e)
400                         {
401                                 /*std::cout<<"InvalidBlockException for bottom block node"
402                                                 <<std::endl;*/
403                                 // Just no block below, no need to panic.
404                         }
405                 }
406         }
407
408         return block_below_is_valid;
409 }
410
411
412 void MapBlock::copyTo(VoxelManipulator &dst)
413 {
414         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
415         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
416         
417         // Copy from data to VoxelManipulator
418         dst.copyFrom(data, data_area, v3s16(0,0,0),
419                         getPosRelative(), data_size);
420 }
421
422 void MapBlock::copyFrom(VoxelManipulator &dst)
423 {
424         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
425         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
426         
427         // Copy from VoxelManipulator to data
428         dst.copyTo(data, data_area, v3s16(0,0,0),
429                         getPosRelative(), data_size);
430 }
431
432 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
433 {
434         /*
435                 Step objects
436         */
437         m_objects.step(dtime, server, daynight_ratio);
438
439         setChangedFlag();
440 }
441
442
443 void MapBlock::updateDayNightDiff()
444 {
445         if(data == NULL)
446         {
447                 m_day_night_differs = false;
448                 return;
449         }
450
451         bool differs = false;
452
453         /*
454                 Check if any lighting value differs
455         */
456         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
457         {
458                 MapNode &n = data[i];
459                 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
460                 {
461                         differs = true;
462                         break;
463                 }
464         }
465
466         /*
467                 If some lighting values differ, check if the whole thing is
468                 just air. If it is, differ = false
469         */
470         if(differs)
471         {
472                 bool only_air = true;
473                 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
474                 {
475                         MapNode &n = data[i];
476                         if(n.d != CONTENT_AIR)
477                         {
478                                 only_air = false;
479                                 break;
480                         }
481                 }
482                 if(only_air)
483                         differs = false;
484         }
485
486         // Set member variable
487         m_day_night_differs = differs;
488 }
489
490 s16 MapBlock::getGroundLevel(v2s16 p2d)
491 {
492         if(isDummy())
493                 return -3;
494         try
495         {
496                 s16 y = MAP_BLOCKSIZE-1;
497                 for(; y>=0; y--)
498                 {
499                         //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
500                         if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
501                         {
502                                 if(y == MAP_BLOCKSIZE-1)
503                                         return -2;
504                                 else
505                                         return y;
506                         }
507                 }
508                 return -1;
509         }
510         catch(InvalidPositionException &e)
511         {
512                 return -3;
513         }
514 }
515
516 /*
517         Serialization
518 */
519
520 void MapBlock::serialize(std::ostream &os, u8 version)
521 {
522         if(!ser_ver_supported(version))
523                 throw VersionMismatchException("ERROR: MapBlock format not supported");
524         
525         if(data == NULL)
526         {
527                 throw SerializationError("ERROR: Not writing dummy block.");
528         }
529         
530         // These have no compression
531         if(version <= 3 || version == 5 || version == 6)
532         {
533                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
534                 
535                 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
536                 SharedBuffer<u8> dest(buflen);
537
538                 dest[0] = is_underground;
539                 for(u32 i=0; i<nodecount; i++)
540                 {
541                         u32 s = 1 + i * MapNode::serializedLength(version);
542                         data[i].serialize(&dest[s], version);
543                 }
544                 
545                 os.write((char*)*dest, dest.getSize());
546         }
547         else if(version <= 10)
548         {
549                 /*
550                         With compression.
551                         Compress the materials and the params separately.
552                 */
553                 
554                 // First byte
555                 os.write((char*)&is_underground, 1);
556
557                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
558
559                 // Get and compress materials
560                 SharedBuffer<u8> materialdata(nodecount);
561                 for(u32 i=0; i<nodecount; i++)
562                 {
563                         materialdata[i] = data[i].d;
564                 }
565                 compress(materialdata, os, version);
566
567                 // Get and compress lights
568                 SharedBuffer<u8> lightdata(nodecount);
569                 for(u32 i=0; i<nodecount; i++)
570                 {
571                         lightdata[i] = data[i].param;
572                 }
573                 compress(lightdata, os, version);
574                 
575                 if(version >= 10)
576                 {
577                         // Get and compress param2
578                         SharedBuffer<u8> param2data(nodecount);
579                         for(u32 i=0; i<nodecount; i++)
580                         {
581                                 param2data[i] = data[i].param2;
582                         }
583                         compress(param2data, os, version);
584                 }
585         }
586         // All other versions (newest)
587         else
588         {
589                 // First byte
590                 u8 flags = 0;
591                 if(is_underground)
592                         flags |= 0x01;
593                 if(m_day_night_differs)
594                         flags |= 0x02;
595                 if(m_lighting_expired)
596                         flags |= 0x04;
597                 if(version >= 18)
598                 {
599                         if(m_generated == false)
600                                 flags |= 0x08;
601                 }
602                 os.write((char*)&flags, 1);
603
604                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
605
606                 /*
607                         Get data
608                 */
609
610                 // Serialize nodes
611                 SharedBuffer<u8> databuf_nodelist(nodecount*3);
612                 for(u32 i=0; i<nodecount; i++)
613                 {
614                         data[i].serialize(&databuf_nodelist[i*3], version);
615                 }
616                 
617                 // Create buffer with different parameters sorted
618                 SharedBuffer<u8> databuf(nodecount*3);
619                 for(u32 i=0; i<nodecount; i++)
620                 {
621                         databuf[i] = databuf_nodelist[i*3];
622                         databuf[i+nodecount] = databuf_nodelist[i*3+1];
623                         databuf[i+nodecount*2] = databuf_nodelist[i*3+2];
624                 }
625
626                 /*
627                         Compress data to output stream
628                 */
629
630                 compress(databuf, os, version);
631                 
632                 /*
633                         NodeMetadata
634                 */
635                 if(version >= 14)
636                 {
637                         if(version <= 15)
638                         {
639                                 try{
640                                         std::ostringstream oss(std::ios_base::binary);
641                                         m_node_metadata.serialize(oss);
642                                         os<<serializeString(oss.str());
643                                 }
644                                 // This will happen if the string is longer than 65535
645                                 catch(SerializationError &e)
646                                 {
647                                         // Use an empty string
648                                         os<<serializeString("");
649                                 }
650                         }
651                         else
652                         {
653                                 std::ostringstream oss(std::ios_base::binary);
654                                 m_node_metadata.serialize(oss);
655                                 compressZlib(oss.str(), os);
656                                 //os<<serializeLongString(oss.str());
657                         }
658                 }
659         }
660 }
661
662 void MapBlock::deSerialize(std::istream &is, u8 version)
663 {
664         if(!ser_ver_supported(version))
665                 throw VersionMismatchException("ERROR: MapBlock format not supported");
666
667         // These have no lighting info
668         if(version <= 1)
669         {
670                 setLightingExpired(true);
671         }
672
673         // These have no "generated" field
674         if(version < 18)
675         {
676                 m_generated = true;
677         }
678
679         // These have no compression
680         if(version <= 3 || version == 5 || version == 6)
681         {
682                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
683                 char tmp;
684                 is.read(&tmp, 1);
685                 if(is.gcount() != 1)
686                         throw SerializationError
687                                         ("MapBlock::deSerialize: no enough input data");
688                 is_underground = tmp;
689                 for(u32 i=0; i<nodecount; i++)
690                 {
691                         s32 len = MapNode::serializedLength(version);
692                         SharedBuffer<u8> d(len);
693                         is.read((char*)*d, len);
694                         if(is.gcount() != len)
695                                 throw SerializationError
696                                                 ("MapBlock::deSerialize: no enough input data");
697                         data[i].deSerialize(*d, version);
698                 }
699         }
700         else if(version <= 10)
701         {
702                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
703
704                 u8 t8;
705                 is.read((char*)&t8, 1);
706                 is_underground = t8;
707
708                 {
709                         // Uncompress and set material data
710                         std::ostringstream os(std::ios_base::binary);
711                         decompress(is, os, version);
712                         std::string s = os.str();
713                         if(s.size() != nodecount)
714                                 throw SerializationError
715                                                 ("MapBlock::deSerialize: invalid format");
716                         for(u32 i=0; i<s.size(); i++)
717                         {
718                                 data[i].d = s[i];
719                         }
720                 }
721                 {
722                         // Uncompress and set param data
723                         std::ostringstream os(std::ios_base::binary);
724                         decompress(is, os, version);
725                         std::string s = os.str();
726                         if(s.size() != nodecount)
727                                 throw SerializationError
728                                                 ("MapBlock::deSerialize: invalid format");
729                         for(u32 i=0; i<s.size(); i++)
730                         {
731                                 data[i].param = s[i];
732                         }
733                 }
734         
735                 if(version >= 10)
736                 {
737                         // Uncompress and set param2 data
738                         std::ostringstream os(std::ios_base::binary);
739                         decompress(is, os, version);
740                         std::string s = os.str();
741                         if(s.size() != nodecount)
742                                 throw SerializationError
743                                                 ("MapBlock::deSerialize: invalid format");
744                         for(u32 i=0; i<s.size(); i++)
745                         {
746                                 data[i].param2 = s[i];
747                         }
748                 }
749         }
750         // All other versions (newest)
751         else
752         {
753                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
754
755                 u8 flags;
756                 is.read((char*)&flags, 1);
757                 is_underground = (flags & 0x01) ? true : false;
758                 m_day_night_differs = (flags & 0x02) ? true : false;
759                 m_lighting_expired = (flags & 0x04) ? true : false;
760                 if(version >= 18)
761                         m_generated = (flags & 0x08) ? false : true;
762
763                 // Uncompress data
764                 std::ostringstream os(std::ios_base::binary);
765                 decompress(is, os, version);
766                 std::string s = os.str();
767                 if(s.size() != nodecount*3)
768                         throw SerializationError
769                                         ("MapBlock::deSerialize: decompress resulted in size"
770                                         " other than nodecount*3");
771
772                 // deserialize nodes from buffer
773                 for(u32 i=0; i<nodecount; i++)
774                 {
775                         u8 buf[3];
776                         buf[0] = s[i];
777                         buf[1] = s[i+nodecount];
778                         buf[2] = s[i+nodecount*2];
779                         data[i].deSerialize(buf, version);
780                 }
781                 
782                 /*
783                         NodeMetadata
784                 */
785                 if(version >= 14)
786                 {
787                         // Ignore errors
788                         try{
789                                 if(version <= 15)
790                                 {
791                                         std::string data = deSerializeString(is);
792                                         std::istringstream iss(data, std::ios_base::binary);
793                                         m_node_metadata.deSerialize(iss);
794                                 }
795                                 else
796                                 {
797                                         //std::string data = deSerializeLongString(is);
798                                         std::ostringstream oss(std::ios_base::binary);
799                                         decompressZlib(is, oss);
800                                         std::istringstream iss(oss.str(), std::ios_base::binary);
801                                         m_node_metadata.deSerialize(iss);
802                                 }
803                         }
804                         catch(SerializationError &e)
805                         {
806                                 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
807                                                 <<" while deserializing node metadata"<<std::endl;
808                         }
809                 }
810         }
811 }
812
813 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
814 {
815         // Versions up from 9 have block objects.
816         if(version >= 9)
817         {
818                 //serializeObjects(os, version); // DEPRECATED
819                 // count=0
820                 writeU16(os, 0);
821         }
822         
823         // Versions up from 15 have static objects.
824         if(version >= 15)
825         {
826                 m_static_objects.serialize(os);
827         }
828
829         // Timestamp
830         if(version >= 17)
831         {
832                 writeU32(os, getTimestamp());
833         }
834 }
835
836 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
837 {
838         /*
839                 Versions up from 9 have block objects.
840         */
841         if(version >= 9)
842         {
843                 updateObjects(is, version, NULL, 0);
844         }
845
846         /*
847                 Versions up from 15 have static objects.
848         */
849         if(version >= 15)
850         {
851                 m_static_objects.deSerialize(is);
852         }
853                 
854         // Timestamp
855         if(version >= 17)
856         {
857                 setTimestamp(readU32(is));
858         }
859         else
860         {
861                 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
862         }
863 }
864
865
866 //END