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