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