]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapblock.cpp
Weather: Clean up getHeat/getHumidity somewhat
[dragonfireclient.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                 heat(0),
47                 humidity(0),
48                 weather_update_time(0),
49                 m_parent(parent),
50                 m_pos(pos),
51                 m_gamedef(gamedef),
52                 m_modified(MOD_STATE_WRITE_NEEDED),
53                 m_modified_reason("initial"),
54                 m_modified_reason_too_long(false),
55                 is_underground(false),
56                 m_lighting_expired(true),
57                 m_day_night_differs(false),
58                 m_day_night_differs_expired(true),
59                 m_generated(false),
60                 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
61                 m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
62                 m_usage_timer(0),
63                 m_refcount(0)
64 {
65         data = NULL;
66         if(dummy == false)
67                 reallocate();
68         
69 #ifndef SERVER
70         //mesh_mutex.Init();
71         mesh = NULL;
72 #endif
73 }
74
75 MapBlock::~MapBlock()
76 {
77 #ifndef SERVER
78         {
79                 //JMutexAutoLock lock(mesh_mutex);
80                 
81                 if(mesh)
82                 {
83                         delete mesh;
84                         mesh = NULL;
85                 }
86         }
87 #endif
88
89         if(data)
90                 delete[] data;
91 }
92
93 bool MapBlock::isValidPositionParent(v3s16 p)
94 {
95         if(isValidPosition(p))
96         {
97                 return true;
98         }
99         else{
100                 return m_parent->isValidPosition(getPosRelative() + p);
101         }
102 }
103
104 MapNode MapBlock::getNodeParent(v3s16 p)
105 {
106         if(isValidPosition(p) == false)
107         {
108                 return m_parent->getNode(getPosRelative() + p);
109         }
110         else
111         {
112                 if(data == NULL)
113                         throw InvalidPositionException();
114                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
115         }
116 }
117
118 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
119 {
120         if(isValidPosition(p) == false)
121         {
122                 m_parent->setNode(getPosRelative() + p, n);
123         }
124         else
125         {
126                 if(data == NULL)
127                         throw InvalidPositionException();
128                 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
129         }
130 }
131
132 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
133 {
134         if(isValidPosition(p) == false)
135         {
136                 try{
137                         return m_parent->getNode(getPosRelative() + p);
138                 }
139                 catch(InvalidPositionException &e)
140                 {
141                         return MapNode(CONTENT_IGNORE);
142                 }
143         }
144         else
145         {
146                 if(data == NULL)
147                 {
148                         return MapNode(CONTENT_IGNORE);
149                 }
150                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
151         }
152 }
153
154 /*
155         Propagates sunlight down through the block.
156         Doesn't modify nodes that are not affected by sunlight.
157         
158         Returns false if sunlight at bottom block is invalid.
159         Returns true if sunlight at bottom block is valid.
160         Returns true if bottom block doesn't exist.
161
162         If there is a block above, continues from it.
163         If there is no block above, assumes there is sunlight, unless
164         is_underground is set or highest node is water.
165
166         All sunlighted nodes are added to light_sources.
167
168         if remove_light==true, sets non-sunlighted nodes black.
169
170         if black_air_left!=NULL, it is set to true if non-sunlighted
171         air is left in block.
172 */
173 bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources,
174                 bool remove_light, bool *black_air_left)
175 {
176         INodeDefManager *nodemgr = m_gamedef->ndef();
177
178         // Whether the sunlight at the top of the bottom block is valid
179         bool block_below_is_valid = true;
180         
181         v3s16 pos_relative = getPosRelative();
182         
183         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
184         {
185                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
186                 {
187 #if 1
188                         bool no_sunlight = false;
189                         bool no_top_block = false;
190                         // Check if node above block has sunlight
191                         try{
192                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
193                                 if(n.getContent() == CONTENT_IGNORE)
194                                 {
195                                         // Trust heuristics
196                                         no_sunlight = is_underground;
197                                 }
198                                 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
199                                 {
200                                         no_sunlight = true;
201                                 }
202                         }
203                         catch(InvalidPositionException &e)
204                         {
205                                 no_top_block = true;
206                                 
207                                 // NOTE: This makes over-ground roofed places sunlighted
208                                 // Assume sunlight, unless is_underground==true
209                                 if(is_underground)
210                                 {
211                                         no_sunlight = true;
212                                 }
213                                 else
214                                 {
215                                         MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
216                                         if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
217                                         {
218                                                 no_sunlight = true;
219                                         }
220                                 }
221                                 // NOTE: As of now, this just would make everything dark.
222                                 // No sunlight here
223                                 //no_sunlight = true;
224                         }
225 #endif
226 #if 0 // Doesn't work; nothing gets light.
227                         bool no_sunlight = true;
228                         bool no_top_block = false;
229                         // Check if node above block has sunlight
230                         try{
231                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
232                                 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
233                                 {
234                                         no_sunlight = false;
235                                 }
236                         }
237                         catch(InvalidPositionException &e)
238                         {
239                                 no_top_block = true;
240                         }
241 #endif
242
243                         /*std::cout<<"("<<x<<","<<z<<"): "
244                                         <<"no_top_block="<<no_top_block
245                                         <<", is_underground="<<is_underground
246                                         <<", no_sunlight="<<no_sunlight
247                                         <<std::endl;*/
248                 
249                         s16 y = MAP_BLOCKSIZE-1;
250                         
251                         // This makes difference to diminishing in water.
252                         bool stopped_to_solid_object = false;
253                         
254                         u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
255
256                         for(; y >= 0; y--)
257                         {
258                                 v3s16 pos(x, y, z);
259                                 MapNode &n = getNodeRef(pos);
260                                 
261                                 if(current_light == 0)
262                                 {
263                                         // Do nothing
264                                 }
265                                 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
266                                 {
267                                         // Do nothing: Sunlight is continued
268                                 }
269                                 else if(nodemgr->get(n).light_propagates == false)
270                                 {
271                                         // A solid object is on the way.
272                                         stopped_to_solid_object = true;
273                                         
274                                         // Light stops.
275                                         current_light = 0;
276                                 }
277                                 else
278                                 {
279                                         // Diminish light
280                                         current_light = diminish_light(current_light);
281                                 }
282
283                                 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
284
285                                 if(current_light > old_light || remove_light)
286                                 {
287                                         n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
288                                 }
289                                 
290                                 if(diminish_light(current_light) != 0)
291                                 {
292                                         light_sources.insert(pos_relative + pos);
293                                 }
294
295                                 if(current_light == 0 && stopped_to_solid_object)
296                                 {
297                                         if(black_air_left)
298                                         {
299                                                 *black_air_left = true;
300                                         }
301                                 }
302                         }
303
304                         // Whether or not the block below should see LIGHT_SUN
305                         bool sunlight_should_go_down = (current_light == LIGHT_SUN);
306
307                         /*
308                                 If the block below hasn't already been marked invalid:
309
310                                 Check if the node below the block has proper sunlight at top.
311                                 If not, the block below is invalid.
312                                 
313                                 Ignore non-transparent nodes as they always have no light
314                         */
315                         try
316                         {
317                         if(block_below_is_valid)
318                         {
319                                 MapNode n = getNodeParent(v3s16(x, -1, z));
320                                 if(nodemgr->get(n).light_propagates)
321                                 {
322                                         if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
323                                                         && sunlight_should_go_down == false)
324                                                 block_below_is_valid = false;
325                                         else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
326                                                         && sunlight_should_go_down == true)
327                                                 block_below_is_valid = false;
328                                 }
329                         }//if
330                         }//try
331                         catch(InvalidPositionException &e)
332                         {
333                                 /*std::cout<<"InvalidBlockException for bottom block node"
334                                                 <<std::endl;*/
335                                 // Just no block below, no need to panic.
336                         }
337                 }
338         }
339
340         return block_below_is_valid;
341 }
342
343
344 void MapBlock::copyTo(VoxelManipulator &dst)
345 {
346         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
347         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
348         
349         // Copy from data to VoxelManipulator
350         dst.copyFrom(data, data_area, v3s16(0,0,0),
351                         getPosRelative(), data_size);
352 }
353
354 void MapBlock::copyFrom(VoxelManipulator &dst)
355 {
356         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
357         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
358         
359         // Copy from VoxelManipulator to data
360         dst.copyTo(data, data_area, v3s16(0,0,0),
361                         getPosRelative(), data_size);
362 }
363
364 void MapBlock::actuallyUpdateDayNightDiff()
365 {
366         INodeDefManager *nodemgr = m_gamedef->ndef();
367         // Running this function un-expires m_day_night_differs
368         m_day_night_differs_expired = false;
369
370         if(data == NULL)
371         {
372                 m_day_night_differs = false;
373                 return;
374         }
375
376         bool differs = false;
377
378         /*
379                 Check if any lighting value differs
380         */
381         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
382         {
383                 MapNode &n = data[i];
384                 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
385                 {
386                         differs = true;
387                         break;
388                 }
389         }
390
391         /*
392                 If some lighting values differ, check if the whole thing is
393                 just air. If it is, differ = false
394         */
395         if(differs)
396         {
397                 bool only_air = true;
398                 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
399                 {
400                         MapNode &n = data[i];
401                         if(n.getContent() != CONTENT_AIR)
402                         {
403                                 only_air = false;
404                                 break;
405                         }
406                 }
407                 if(only_air)
408                         differs = false;
409         }
410
411         // Set member variable
412         m_day_night_differs = differs;
413 }
414
415 void MapBlock::expireDayNightDiff()
416 {
417         //INodeDefManager *nodemgr = m_gamedef->ndef();
418
419         if(data == NULL){
420                 m_day_night_differs = false;
421                 m_day_night_differs_expired = false;
422                 return;
423         }
424
425         m_day_night_differs_expired = true;
426 }
427
428 s16 MapBlock::getGroundLevel(v2s16 p2d)
429 {
430         if(isDummy())
431                 return -3;
432         try
433         {
434                 s16 y = MAP_BLOCKSIZE-1;
435                 for(; y>=0; y--)
436                 {
437                         MapNode n = getNodeRef(p2d.X, y, p2d.Y);
438                         if(m_gamedef->ndef()->get(n).walkable)
439                         {
440                                 if(y == MAP_BLOCKSIZE-1)
441                                         return -2;
442                                 else
443                                         return y;
444                         }
445                 }
446                 return -1;
447         }
448         catch(InvalidPositionException &e)
449         {
450                 return -3;
451         }
452 }
453
454 /*
455         Serialization
456 */
457 // List relevant id-name pairs for ids in the block using nodedef
458 // Renumbers the content IDs (starting at 0 and incrementing
459 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
460                 INodeDefManager *nodedef)
461 {
462         std::map<content_t, content_t> mapping;
463         std::set<content_t> unknown_contents;
464         content_t id_counter = 0;
465         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
466         {
467                 content_t global_id = nodes[i].getContent();
468                 content_t id = CONTENT_IGNORE;
469
470                 // Try to find an existing mapping
471                 std::map<content_t, content_t>::iterator j = mapping.find(global_id);
472                 if(j != mapping.end())
473                 {
474                         id = j->second;
475                 }
476                 else
477                 {
478                         // We have to assign a new mapping
479                         id = id_counter++;
480                         mapping.insert(std::make_pair(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, heat);
651                 writeF1000(os, 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                         heat = readF1000(is);
768                         humidity = readF1000(is);
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