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