]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapblock.cpp
Add disable_jump and fall_damage_add_percent node groups
[dragonfireclient.git] / src / mapblock.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU 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 // 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 #include "content_nodemeta.h" // For legacy deserialization
34 #ifndef SERVER
35 #include "mapblock_mesh.h"
36 #endif
37 #include "util/string.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 {
61         data = NULL;
62         if(dummy == false)
63                 reallocate();
64         
65 #ifndef SERVER
66         //mesh_mutex.Init();
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(core::map<v3s16, bool> & 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, true);
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 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
456                 INodeDefManager *nodedef)
457 {
458         std::map<content_t, content_t> mapping;
459         std::set<content_t> unknown_contents;
460         content_t id_counter = 0;
461         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
462         {
463                 content_t global_id = nodes[i].getContent();
464                 content_t id = CONTENT_IGNORE;
465
466                 // Try to find an existing mapping
467                 std::map<content_t, content_t>::iterator j = mapping.find(global_id);
468                 if(j != mapping.end())
469                 {
470                         id = j->second;
471                 }
472                 else
473                 {
474                         // We have to assign a new mapping
475                         id = id_counter++;
476                         mapping.insert(std::make_pair(global_id, id));
477
478                         const ContentFeatures &f = nodedef->get(global_id);
479                         const std::string &name = f.name;
480                         if(name == "")
481                                 unknown_contents.insert(global_id);
482                         else
483                                 nimap->set(id, name);
484                 }
485
486                 // Update the MapNode
487                 nodes[i].setContent(id);
488         }
489         for(std::set<content_t>::const_iterator
490                         i = unknown_contents.begin();
491                         i != unknown_contents.end(); i++){
492                 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
493                                 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
494         }
495 }
496 // Correct ids in the block to match nodedef based on names.
497 // Unknown ones are added to nodedef.
498 // Will not update itself to match id-name pairs in nodedef.
499 static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
500                 IGameDef *gamedef)
501 {
502         INodeDefManager *nodedef = gamedef->ndef();
503         // This means the block contains incorrect ids, and we contain
504         // the information to convert those to names.
505         // nodedef contains information to convert our names to globally
506         // correct ids.
507         std::set<content_t> unnamed_contents;
508         std::set<std::string> unallocatable_contents;
509         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
510         {
511                 content_t local_id = nodes[i].getContent();
512                 std::string name;
513                 bool found = nimap->getName(local_id, name);
514                 if(!found){
515                         unnamed_contents.insert(local_id);
516                         continue;
517                 }
518                 content_t global_id;
519                 found = nodedef->getId(name, global_id);
520                 if(!found){
521                         global_id = gamedef->allocateUnknownNodeId(name);
522                         if(global_id == CONTENT_IGNORE){
523                                 unallocatable_contents.insert(name);
524                                 continue;
525                         }
526                 }
527                 nodes[i].setContent(global_id);
528         }
529         for(std::set<content_t>::const_iterator
530                         i = unnamed_contents.begin();
531                         i != unnamed_contents.end(); i++){
532                 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
533                                 <<"Block contains id "<<(*i)
534                                 <<" with no name mapping"<<std::endl;
535         }
536         for(std::set<std::string>::const_iterator
537                         i = unallocatable_contents.begin();
538                         i != unallocatable_contents.end(); i++){
539                 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
540                                 <<"Could not allocate global id for node name \""
541                                 <<(*i)<<"\""<<std::endl;
542         }
543 }
544
545 void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
546 {
547         if(!ser_ver_supported(version))
548                 throw VersionMismatchException("ERROR: MapBlock format not supported");
549         
550         if(data == NULL)
551         {
552                 throw SerializationError("ERROR: Not writing dummy block.");
553         }
554         
555         // Can't do this anymore; we have 16-bit dynamically allocated node IDs
556         // in memory; conversion just won't work in this direction.
557         if(version < 24)
558                 throw SerializationError("MapBlock::serialize: serialization to "
559                                 "version < 24 not possible");
560                 
561         // First byte
562         u8 flags = 0;
563         if(is_underground)
564                 flags |= 0x01;
565         if(getDayNightDiff())
566                 flags |= 0x02;
567         if(m_lighting_expired)
568                 flags |= 0x04;
569         if(m_generated == false)
570                 flags |= 0x08;
571         writeU8(os, flags);
572         
573         /*
574                 Bulk node data
575         */
576         NameIdMapping nimap;
577         u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
578         if(disk)
579         {
580                 MapNode *tmp_nodes = new MapNode[nodecount];
581                 for(u32 i=0; i<nodecount; i++)
582                         tmp_nodes[i] = data[i];
583                 getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
584
585                 u8 content_width = 2;
586                 u8 params_width = 2;
587                 writeU8(os, content_width);
588                 writeU8(os, params_width);
589                 MapNode::serializeBulk(os, version, tmp_nodes, nodecount,
590                                 content_width, params_width, true);
591                 delete[] tmp_nodes;
592         }
593         else
594         {
595                 u8 content_width = 2;
596                 u8 params_width = 2;
597                 writeU8(os, content_width);
598                 writeU8(os, params_width);
599                 MapNode::serializeBulk(os, version, data, nodecount,
600                                 content_width, params_width, true);
601         }
602         
603         /*
604                 Node metadata
605         */
606         std::ostringstream oss(std::ios_base::binary);
607         m_node_metadata.serialize(oss);
608         compressZlib(oss.str(), os);
609
610         /*
611                 Data that goes to disk, but not the network
612         */
613         if(disk)
614         {
615                 if(version <= 24){
616                         // Node timers
617                         m_node_timers.serialize(os, version);
618                 }
619
620                 // Static objects
621                 m_static_objects.serialize(os);
622
623                 // Timestamp
624                 writeU32(os, getTimestamp());
625
626                 // Write block-specific node definition id mapping
627                 nimap.serialize(os);
628                 
629                 if(version >= 25){
630                         // Node timers
631                         m_node_timers.serialize(os, version);
632                 }
633         }
634 }
635
636 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
637 {
638         if(!ser_ver_supported(version))
639                 throw VersionMismatchException("ERROR: MapBlock format not supported");
640         
641         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())<<std::endl);
642
643         m_day_night_differs_expired = false;
644
645         if(version <= 21)
646         {
647                 deSerialize_pre22(is, version, disk);
648                 return;
649         }
650
651         u8 flags = readU8(is);
652         is_underground = (flags & 0x01) ? true : false;
653         m_day_night_differs = (flags & 0x02) ? true : false;
654         m_lighting_expired = (flags & 0x04) ? true : false;
655         m_generated = (flags & 0x08) ? false : true;
656
657         /*
658                 Bulk node data
659         */
660         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
661                         <<": Bulk node data"<<std::endl);
662         u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
663         u8 content_width = readU8(is);
664         u8 params_width = readU8(is);
665         if(content_width != 1 && content_width != 2)
666                 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
667         if(params_width != 2)
668                 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
669         MapNode::deSerializeBulk(is, version, data, nodecount,
670                         content_width, params_width, true);
671
672         /*
673                 NodeMetadata
674         */
675         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
676                         <<": Node metadata"<<std::endl);
677         // Ignore errors
678         try{
679                 std::ostringstream oss(std::ios_base::binary);
680                 decompressZlib(is, oss);
681                 std::istringstream iss(oss.str(), std::ios_base::binary);
682                 if(version >= 23)
683                         m_node_metadata.deSerialize(iss, m_gamedef);
684                 else
685                         content_nodemeta_deserialize_legacy(iss,
686                                         &m_node_metadata, &m_node_timers,
687                                         m_gamedef);
688         }
689         catch(SerializationError &e)
690         {
691                 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
692                                 <<" while deserializing node metadata at ("
693                                 <<PP(getPos())<<": "<<e.what()<<std::endl;
694         }
695
696         /*
697                 Data that is only on disk
698         */
699         if(disk)
700         {
701                 // Node timers
702                 if(version == 23){
703                         // Read unused zero
704                         readU8(is);
705                 }
706                 if(version == 24){
707                         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
708                                         <<": Node timers (ver==24)"<<std::endl);
709                         m_node_timers.deSerialize(is, version);
710                 }
711
712                 // Static objects
713                 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
714                                 <<": Static objects"<<std::endl);
715                 m_static_objects.deSerialize(is);
716                 
717                 // Timestamp
718                 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
719                                 <<": Timestamp"<<std::endl);
720                 setTimestamp(readU32(is));
721                 m_disk_timestamp = m_timestamp;
722                 
723                 // Dynamically re-set ids based on node names
724                 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
725                                 <<": NameIdMapping"<<std::endl);
726                 NameIdMapping nimap;
727                 nimap.deSerialize(is);
728                 correctBlockNodeIds(&nimap, data, m_gamedef);
729
730                 if(version >= 25){
731                         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
732                                         <<": Node timers (ver>=25)"<<std::endl);
733                         m_node_timers.deSerialize(is, version);
734                 }
735         }
736                 
737         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
738                         <<": Done."<<std::endl);
739 }
740
741 /*
742         Legacy serialization
743 */
744
745 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
746 {
747         u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
748
749         // Initialize default flags
750         is_underground = false;
751         m_day_night_differs = false;
752         m_lighting_expired = false;
753         m_generated = true;
754
755         // Make a temporary buffer
756         u32 ser_length = MapNode::serializedLength(version);
757         SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
758
759         // These have no compression
760         if(version <= 3 || version == 5 || version == 6)
761         {
762                 char tmp;
763                 is.read(&tmp, 1);
764                 if(is.gcount() != 1)
765                         throw SerializationError
766                                         ("MapBlock::deSerialize: no enough input data");
767                 is_underground = tmp;
768                 is.read((char*)*databuf_nodelist, nodecount * ser_length);
769                 if((u32)is.gcount() != nodecount * ser_length)
770                         throw SerializationError
771                                         ("MapBlock::deSerialize: no enough input data");
772         }
773         else if(version <= 10)
774         {
775                 u8 t8;
776                 is.read((char*)&t8, 1);
777                 is_underground = t8;
778
779                 {
780                         // Uncompress and set material data
781                         std::ostringstream os(std::ios_base::binary);
782                         decompress(is, os, version);
783                         std::string s = os.str();
784                         if(s.size() != nodecount)
785                                 throw SerializationError
786                                                 ("MapBlock::deSerialize: invalid format");
787                         for(u32 i=0; i<s.size(); i++)
788                         {
789                                 databuf_nodelist[i*ser_length] = s[i];
790                         }
791                 }
792                 {
793                         // Uncompress and set param data
794                         std::ostringstream os(std::ios_base::binary);
795                         decompress(is, os, version);
796                         std::string s = os.str();
797                         if(s.size() != nodecount)
798                                 throw SerializationError
799                                                 ("MapBlock::deSerialize: invalid format");
800                         for(u32 i=0; i<s.size(); i++)
801                         {
802                                 databuf_nodelist[i*ser_length + 1] = s[i];
803                         }
804                 }
805         
806                 if(version >= 10)
807                 {
808                         // Uncompress and set param2 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                                 databuf_nodelist[i*ser_length + 2] = s[i];
818                         }
819                 }
820         }
821         // All other versions (newest)
822         else
823         {
824                 u8 flags;
825                 is.read((char*)&flags, 1);
826                 is_underground = (flags & 0x01) ? true : false;
827                 m_day_night_differs = (flags & 0x02) ? true : false;
828                 m_lighting_expired = (flags & 0x04) ? true : false;
829                 if(version >= 18)
830                         m_generated = (flags & 0x08) ? false : true;
831
832                 // Uncompress data
833                 std::ostringstream os(std::ios_base::binary);
834                 decompress(is, os, version);
835                 std::string s = os.str();
836                 if(s.size() != nodecount*3)
837                         throw SerializationError
838                                         ("MapBlock::deSerialize: decompress resulted in size"
839                                         " other than nodecount*3");
840
841                 // deserialize nodes from buffer
842                 for(u32 i=0; i<nodecount; i++)
843                 {
844                         databuf_nodelist[i*ser_length] = s[i];
845                         databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
846                         databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
847                 }
848                 
849                 /*
850                         NodeMetadata
851                 */
852                 if(version >= 14)
853                 {
854                         // Ignore errors
855                         try{
856                                 if(version <= 15)
857                                 {
858                                         std::string data = deSerializeString(is);
859                                         std::istringstream iss(data, std::ios_base::binary);
860                                         content_nodemeta_deserialize_legacy(iss,
861                                                         &m_node_metadata, &m_node_timers,
862                                                         m_gamedef);
863                                 }
864                                 else
865                                 {
866                                         //std::string data = deSerializeLongString(is);
867                                         std::ostringstream oss(std::ios_base::binary);
868                                         decompressZlib(is, oss);
869                                         std::istringstream iss(oss.str(), std::ios_base::binary);
870                                         content_nodemeta_deserialize_legacy(iss,
871                                                         &m_node_metadata, &m_node_timers,
872                                                         m_gamedef);
873                                 }
874                         }
875                         catch(SerializationError &e)
876                         {
877                                 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
878                                                 <<" while deserializing node metadata"<<std::endl;
879                         }
880                 }
881         }
882
883         // Deserialize node data
884         for(u32 i=0; i<nodecount; i++)
885         {
886                 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
887         }
888
889         if(disk)
890         {
891                 /*
892                         Versions up from 9 have block objects. (DEPRECATED)
893                 */
894                 if(version >= 9){
895                         u16 count = readU16(is);
896                         // Not supported and length not known if count is not 0
897                         if(count != 0){
898                                 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
899                                                 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
900                                 return;
901                         }
902                 }
903
904                 /*
905                         Versions up from 15 have static objects.
906                 */
907                 if(version >= 15)
908                         m_static_objects.deSerialize(is);
909
910                 // Timestamp
911                 if(version >= 17){
912                         setTimestamp(readU32(is));
913                         m_disk_timestamp = m_timestamp;
914                 } else {
915                         setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
916                 }
917
918                 // Dynamically re-set ids based on node names
919                 NameIdMapping nimap;
920                 // If supported, read node definition id mapping
921                 if(version >= 21){
922                         nimap.deSerialize(is);
923                 // Else set the legacy mapping
924                 } else {
925                         content_mapnode_get_name_id_mapping(&nimap);
926                 }
927                 correctBlockNodeIds(&nimap, data, m_gamedef);
928         }
929
930
931         // Legacy data changes
932         // This code has to convert from pre-22 to post-22 format.
933         INodeDefManager *nodedef = m_gamedef->ndef();
934         for(u32 i=0; i<nodecount; i++)
935         {
936                 const ContentFeatures &f = nodedef->get(data[i].getContent());
937                 // Mineral
938                 if(nodedef->getId("default:stone") == data[i].getContent()
939                                 && data[i].getParam1() == 1)
940                 {
941                         data[i].setContent(nodedef->getId("default:stone_with_coal"));
942                         data[i].setParam1(0);
943                 }
944                 else if(nodedef->getId("default:stone") == data[i].getContent()
945                                 && data[i].getParam1() == 2)
946                 {
947                         data[i].setContent(nodedef->getId("default:stone_with_iron"));
948                         data[i].setParam1(0);
949                 }
950                 // facedir_simple
951                 if(f.legacy_facedir_simple)
952                 {
953                         data[i].setParam2(data[i].getParam1());
954                         data[i].setParam1(0);
955                 }
956                 // wall_mounted
957                 if(f.legacy_wallmounted)
958                 {
959                         u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
960                         u8 dir_old_format = data[i].getParam2();
961                         u8 dir_new_format = 0;
962                         for(u8 j=0; j<8; j++)
963                         {
964                                 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
965                                 {
966                                         dir_new_format = j;
967                                         break;
968                                 }
969                         }
970                         data[i].setParam2(dir_new_format);
971                 }
972         }
973
974 }
975
976 /*
977         Get a quick string to describe what a block actually contains
978 */
979 std::string analyze_block(MapBlock *block)
980 {
981         if(block == NULL)
982                 return "NULL";
983
984         std::ostringstream desc;
985         
986         v3s16 p = block->getPos();
987         char spos[20];
988         snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
989         desc<<spos;
990         
991         switch(block->getModified())
992         {
993         case MOD_STATE_CLEAN:
994                 desc<<"CLEAN,           ";
995                 break;
996         case MOD_STATE_WRITE_AT_UNLOAD:
997                 desc<<"WRITE_AT_UNLOAD, ";
998                 break;
999         case MOD_STATE_WRITE_NEEDED:
1000                 desc<<"WRITE_NEEDED,    ";
1001                 break;
1002         default:
1003                 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1004         }
1005
1006         if(block->isGenerated())
1007                 desc<<"is_gen [X], ";
1008         else
1009                 desc<<"is_gen [ ], ";
1010
1011         if(block->getIsUnderground())
1012                 desc<<"is_ug [X], ";
1013         else
1014                 desc<<"is_ug [ ], ";
1015
1016         if(block->getLightingExpired())
1017                 desc<<"lighting_exp [X], ";
1018         else
1019                 desc<<"lighting_exp [ ], ";
1020
1021         if(block->isDummy())
1022         {
1023                 desc<<"Dummy, ";
1024         }
1025         else
1026         {
1027                 bool full_ignore = true;
1028                 bool some_ignore = false;
1029                 bool full_air = true;
1030                 bool some_air = false;
1031                 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1032                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1033                 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1034                 {
1035                         v3s16 p(x0,y0,z0);
1036                         MapNode n = block->getNode(p);
1037                         content_t c = n.getContent();
1038                         if(c == CONTENT_IGNORE)
1039                                 some_ignore = true;
1040                         else
1041                                 full_ignore = false;
1042                         if(c == CONTENT_AIR)
1043                                 some_air = true;
1044                         else
1045                                 full_air = false;
1046                 }
1047                 
1048                 desc<<"content {";
1049                 
1050                 std::ostringstream ss;
1051                 
1052                 if(full_ignore)
1053                         ss<<"IGNORE (full), ";
1054                 else if(some_ignore)
1055                         ss<<"IGNORE, ";
1056                 
1057                 if(full_air)
1058                         ss<<"AIR (full), ";
1059                 else if(some_air)
1060                         ss<<"AIR, ";
1061                 
1062                 if(ss.str().size()>=2)
1063                         desc<<ss.str().substr(0, ss.str().size()-2);
1064
1065                 desc<<"}, ";
1066         }
1067
1068         return desc.str().substr(0, desc.str().size()-2);
1069 }
1070
1071
1072 //END