]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapblock.cpp
Made the initial loading screen slightly more alive
[dragonfireclient.git] / src / mapblock.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "mapblock.h"
21 #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(BLOCK_TIMESTAMP_UNDEFINED)
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                 SharedBuffer<u8> databuf(nodecount*3);
611
612                 // Get contents
613                 for(u32 i=0; i<nodecount; i++)
614                 {
615                         databuf[i] = data[i].d;
616                 }
617
618                 // Get params
619                 for(u32 i=0; i<nodecount; i++)
620                 {
621                         databuf[i+nodecount] = data[i].param;
622                 }
623
624                 // Get param2
625                 for(u32 i=0; i<nodecount; i++)
626                 {
627                         databuf[i+nodecount*2] = data[i].param2;
628                 }
629
630                 /*
631                         Compress data to output stream
632                 */
633
634                 compress(databuf, os, version);
635                 
636                 /*
637                         NodeMetadata
638                 */
639                 if(version >= 14)
640                 {
641                         if(version <= 15)
642                         {
643                                 try{
644                                         std::ostringstream oss(std::ios_base::binary);
645                                         m_node_metadata.serialize(oss);
646                                         os<<serializeString(oss.str());
647                                 }
648                                 // This will happen if the string is longer than 65535
649                                 catch(SerializationError &e)
650                                 {
651                                         // Use an empty string
652                                         os<<serializeString("");
653                                 }
654                         }
655                         else
656                         {
657                                 std::ostringstream oss(std::ios_base::binary);
658                                 m_node_metadata.serialize(oss);
659                                 compressZlib(oss.str(), os);
660                                 //os<<serializeLongString(oss.str());
661                         }
662                 }
663         }
664 }
665
666 void MapBlock::deSerialize(std::istream &is, u8 version)
667 {
668         if(!ser_ver_supported(version))
669                 throw VersionMismatchException("ERROR: MapBlock format not supported");
670
671         // These have no lighting info
672         if(version <= 1)
673         {
674                 setLightingExpired(true);
675         }
676
677         // These have no "generated" field
678         if(version < 18)
679         {
680                 m_generated = true;
681         }
682
683         // These have no compression
684         if(version <= 3 || version == 5 || version == 6)
685         {
686                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
687                 char tmp;
688                 is.read(&tmp, 1);
689                 if(is.gcount() != 1)
690                         throw SerializationError
691                                         ("MapBlock::deSerialize: no enough input data");
692                 is_underground = tmp;
693                 for(u32 i=0; i<nodecount; i++)
694                 {
695                         s32 len = MapNode::serializedLength(version);
696                         SharedBuffer<u8> d(len);
697                         is.read((char*)*d, len);
698                         if(is.gcount() != len)
699                                 throw SerializationError
700                                                 ("MapBlock::deSerialize: no enough input data");
701                         data[i].deSerialize(*d, version);
702                 }
703         }
704         else if(version <= 10)
705         {
706                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
707
708                 u8 t8;
709                 is.read((char*)&t8, 1);
710                 is_underground = t8;
711
712                 {
713                         // Uncompress and set material data
714                         std::ostringstream os(std::ios_base::binary);
715                         decompress(is, os, version);
716                         std::string s = os.str();
717                         if(s.size() != nodecount)
718                                 throw SerializationError
719                                                 ("MapBlock::deSerialize: invalid format");
720                         for(u32 i=0; i<s.size(); i++)
721                         {
722                                 data[i].d = s[i];
723                         }
724                 }
725                 {
726                         // Uncompress and set param data
727                         std::ostringstream os(std::ios_base::binary);
728                         decompress(is, os, version);
729                         std::string s = os.str();
730                         if(s.size() != nodecount)
731                                 throw SerializationError
732                                                 ("MapBlock::deSerialize: invalid format");
733                         for(u32 i=0; i<s.size(); i++)
734                         {
735                                 data[i].param = s[i];
736                         }
737                 }
738         
739                 if(version >= 10)
740                 {
741                         // Uncompress and set param2 data
742                         std::ostringstream os(std::ios_base::binary);
743                         decompress(is, os, version);
744                         std::string s = os.str();
745                         if(s.size() != nodecount)
746                                 throw SerializationError
747                                                 ("MapBlock::deSerialize: invalid format");
748                         for(u32 i=0; i<s.size(); i++)
749                         {
750                                 data[i].param2 = s[i];
751                         }
752                 }
753         }
754         // All other versions (newest)
755         else
756         {
757                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
758
759                 u8 flags;
760                 is.read((char*)&flags, 1);
761                 is_underground = (flags & 0x01) ? true : false;
762                 m_day_night_differs = (flags & 0x02) ? true : false;
763                 m_lighting_expired = (flags & 0x04) ? true : false;
764                 if(version >= 18)
765                         m_generated = (flags & 0x08) ? false : true;
766
767                 // Uncompress data
768                 std::ostringstream os(std::ios_base::binary);
769                 decompress(is, os, version);
770                 std::string s = os.str();
771                 if(s.size() != nodecount*3)
772                         throw SerializationError
773                                         ("MapBlock::deSerialize: decompress resulted in size"
774                                         " other than nodecount*3");
775
776                 // Set contents
777                 for(u32 i=0; i<nodecount; i++)
778                 {
779                         data[i].d = s[i];
780                 }
781                 // Set params
782                 for(u32 i=0; i<nodecount; i++)
783                 {
784                         data[i].param = s[i+nodecount];
785                 }
786                 // Set param2
787                 for(u32 i=0; i<nodecount; i++)
788                 {
789                         data[i].param2 = s[i+nodecount*2];
790                 }
791                 
792                 /*
793                         NodeMetadata
794                 */
795                 if(version >= 14)
796                 {
797                         // Ignore errors
798                         try{
799                                 if(version <= 15)
800                                 {
801                                         std::string data = deSerializeString(is);
802                                         std::istringstream iss(data, std::ios_base::binary);
803                                         m_node_metadata.deSerialize(iss);
804                                 }
805                                 else
806                                 {
807                                         //std::string data = deSerializeLongString(is);
808                                         std::ostringstream oss(std::ios_base::binary);
809                                         decompressZlib(is, oss);
810                                         std::istringstream iss(oss.str(), std::ios_base::binary);
811                                         m_node_metadata.deSerialize(iss);
812                                 }
813                         }
814                         catch(SerializationError &e)
815                         {
816                                 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
817                                                 <<" while deserializing node metadata"<<std::endl;
818                         }
819                 }
820         }
821         
822         /*
823                 Translate nodes as specified in the translate_to fields of
824                 node features
825
826                 NOTE: This isn't really used. Should it be removed?
827         */
828         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
829         {
830                 MapNode &n = data[i];
831
832                 MapNode *translate_to = content_features(n.d).translate_to;
833                 if(translate_to)
834                 {
835                         dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
836                                         <<translate_to->d<<std::endl;
837                         n = *translate_to;
838                 }
839         }
840 }
841
842 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
843 {
844         // Versions up from 9 have block objects.
845         if(version >= 9)
846         {
847                 //serializeObjects(os, version); // DEPRECATED
848                 // count=0
849                 writeU16(os, 0);
850         }
851         
852         // Versions up from 15 have static objects.
853         if(version >= 15)
854         {
855                 m_static_objects.serialize(os);
856         }
857
858         // Timestamp
859         if(version >= 17)
860         {
861                 writeU32(os, getTimestamp());
862         }
863 }
864
865 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
866 {
867         /*
868                 Versions up from 9 have block objects.
869         */
870         if(version >= 9)
871         {
872                 updateObjects(is, version, NULL, 0);
873         }
874
875         /*
876                 Versions up from 15 have static objects.
877         */
878         if(version >= 15)
879         {
880                 m_static_objects.deSerialize(is);
881         }
882                 
883         // Timestamp
884         if(version >= 17)
885         {
886                 setTimestamp(readU32(is));
887         }
888         else
889         {
890                 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
891         }
892 }
893
894
895 //END