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