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