3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #include "irrlichttypes_extrabloated.h"
25 #include "content_mapnode.h" // For mapnode_translate_*_internal
26 #include "serialization.h" // For ser_ver_supported
27 #include "util/serialize.h"
29 #include "util/directiontables.h"
30 #include "util/numeric.h"
34 static const Rotation wallmounted_to_rot
[] = {
35 ROTATE_0
, ROTATE_180
, ROTATE_90
, ROTATE_270
38 static const u8 rot_to_wallmounted
[] = {
47 void MapNode::getColor(const ContentFeatures
&f
, video::SColor
*color
) const
50 *color
= (*f
.palette
)[param2
];
56 void MapNode::setLight(LightBank bank
, u8 a_light
, const ContentFeatures
&f
) noexcept
58 // If node doesn't contain light data, ignore this
59 if(f
.param_type
!= CPT_LIGHT
)
61 if(bank
== LIGHTBANK_DAY
)
64 param1
|= a_light
& 0x0f;
66 else if(bank
== LIGHTBANK_NIGHT
)
69 param1
|= (a_light
& 0x0f)<<4;
72 assert("Invalid light bank" == NULL
);
75 void MapNode::setLight(LightBank bank
, u8 a_light
, const NodeDefManager
*nodemgr
)
77 setLight(bank
, a_light
, nodemgr
->get(*this));
80 bool MapNode::isLightDayNightEq(const NodeDefManager
*nodemgr
) const
82 const ContentFeatures
&f
= nodemgr
->get(*this);
85 if (f
.param_type
== CPT_LIGHT
) {
86 u8 day
= MYMAX(f
.light_source
, param1
& 0x0f);
87 u8 night
= MYMAX(f
.light_source
, (param1
>> 4) & 0x0f);
88 isEqual
= day
== night
;
96 u8
MapNode::getLight(LightBank bank
, const NodeDefManager
*nodemgr
) const
98 // Select the brightest of [light source, propagated light]
99 const ContentFeatures
&f
= nodemgr
->get(*this);
102 if(f
.param_type
== CPT_LIGHT
)
103 light
= bank
== LIGHTBANK_DAY
? param1
& 0x0f : (param1
>> 4) & 0x0f;
107 return MYMAX(f
.light_source
, light
);
110 u8
MapNode::getLightRaw(LightBank bank
, const ContentFeatures
&f
) const noexcept
112 if(f
.param_type
== CPT_LIGHT
)
113 return bank
== LIGHTBANK_DAY
? param1
& 0x0f : (param1
>> 4) & 0x0f;
117 u8
MapNode::getLightNoChecks(LightBank bank
, const ContentFeatures
*f
) const noexcept
119 return MYMAX(f
->light_source
,
120 bank
== LIGHTBANK_DAY
? param1
& 0x0f : (param1
>> 4) & 0x0f);
123 bool MapNode::getLightBanks(u8
&lightday
, u8
&lightnight
,
124 const NodeDefManager
*nodemgr
) const
126 // Select the brightest of [light source, propagated light]
127 const ContentFeatures
&f
= nodemgr
->get(*this);
128 if(f
.param_type
== CPT_LIGHT
)
130 lightday
= param1
& 0x0f;
131 lightnight
= (param1
>>4)&0x0f;
138 if(f
.light_source
> lightday
)
139 lightday
= f
.light_source
;
140 if(f
.light_source
> lightnight
)
141 lightnight
= f
.light_source
;
142 return f
.param_type
== CPT_LIGHT
|| f
.light_source
!= 0;
145 u8
MapNode::getFaceDir(const NodeDefManager
*nodemgr
,
146 bool allow_wallmounted
) const
148 const ContentFeatures
&f
= nodemgr
->get(*this);
149 if (f
.param_type_2
== CPT2_FACEDIR
||
150 f
.param_type_2
== CPT2_COLORED_FACEDIR
)
151 return (getParam2() & 0x1F) % 24;
152 if (allow_wallmounted
&& (f
.param_type_2
== CPT2_WALLMOUNTED
||
153 f
.param_type_2
== CPT2_COLORED_WALLMOUNTED
))
154 return wallmounted_to_facedir
[getParam2() & 0x07];
158 u8
MapNode::getWallMounted(const NodeDefManager
*nodemgr
) const
160 const ContentFeatures
&f
= nodemgr
->get(*this);
161 if (f
.param_type_2
== CPT2_WALLMOUNTED
||
162 f
.param_type_2
== CPT2_COLORED_WALLMOUNTED
)
163 return getParam2() & 0x07;
167 v3s16
MapNode::getWallMountedDir(const NodeDefManager
*nodemgr
) const
169 switch(getWallMounted(nodemgr
))
171 case 0: default: return v3s16(0,1,0);
172 case 1: return v3s16(0,-1,0);
173 case 2: return v3s16(1,0,0);
174 case 3: return v3s16(-1,0,0);
175 case 4: return v3s16(0,0,1);
176 case 5: return v3s16(0,0,-1);
180 void MapNode::rotateAlongYAxis(const NodeDefManager
*nodemgr
, Rotation rot
)
182 ContentParamType2 cpt2
= nodemgr
->get(*this).param_type_2
;
184 if (cpt2
== CPT2_FACEDIR
|| cpt2
== CPT2_COLORED_FACEDIR
) {
185 static const u8 rotate_facedir
[24 * 4] = {
186 // Table value = rotated facedir
187 // Columns: 0, 90, 180, 270 degrees rotation around vertical axis
188 // Rotation is anticlockwise as seen from above (+Y)
190 0, 1, 2, 3, // Initial facedir 0 to 3
195 4, 13, 10, 19, // 4 to 7
200 8, 17, 6, 15, // 8 to 11
205 12, 9, 18, 7, // 12 to 15
210 16, 5, 14, 11, // 16 to 19
215 20, 23, 22, 21, // 20 to 23
220 u8 facedir
= (param2
& 31) % 24;
221 u8 index
= facedir
* 4 + rot
;
223 param2
|= rotate_facedir
[index
];
224 } else if (cpt2
== CPT2_WALLMOUNTED
||
225 cpt2
== CPT2_COLORED_WALLMOUNTED
) {
226 u8 wmountface
= (param2
& 7);
230 Rotation oldrot
= wallmounted_to_rot
[wmountface
- 2];
232 param2
|= rot_to_wallmounted
[(oldrot
- rot
) & 3];
236 void transformNodeBox(const MapNode
&n
, const NodeBox
&nodebox
,
237 const NodeDefManager
*nodemgr
, std::vector
<aabb3f
> *p_boxes
,
240 std::vector
<aabb3f
> &boxes
= *p_boxes
;
242 if (nodebox
.type
== NODEBOX_FIXED
|| nodebox
.type
== NODEBOX_LEVELED
) {
243 const std::vector
<aabb3f
> &fixed
= nodebox
.fixed
;
244 int facedir
= n
.getFaceDir(nodemgr
, true);
245 u8 axisdir
= facedir
>>2;
247 for (aabb3f box
: fixed
) {
248 if (nodebox
.type
== NODEBOX_LEVELED
)
249 box
.MaxEdge
.Y
= (-0.5f
+ n
.getLevel(nodemgr
) / 64.0f
) * BS
;
255 box
.MinEdge
.rotateXZBy(-90);
256 box
.MaxEdge
.rotateXZBy(-90);
258 else if(facedir
== 2)
260 box
.MinEdge
.rotateXZBy(180);
261 box
.MaxEdge
.rotateXZBy(180);
263 else if(facedir
== 3)
265 box
.MinEdge
.rotateXZBy(90);
266 box
.MaxEdge
.rotateXZBy(90);
270 box
.MinEdge
.rotateYZBy(90);
271 box
.MaxEdge
.rotateYZBy(90);
274 box
.MinEdge
.rotateXYBy(90);
275 box
.MaxEdge
.rotateXYBy(90);
277 else if(facedir
== 2)
279 box
.MinEdge
.rotateXYBy(180);
280 box
.MaxEdge
.rotateXYBy(180);
282 else if(facedir
== 3)
284 box
.MinEdge
.rotateXYBy(-90);
285 box
.MaxEdge
.rotateXYBy(-90);
289 box
.MinEdge
.rotateYZBy(-90);
290 box
.MaxEdge
.rotateYZBy(-90);
293 box
.MinEdge
.rotateXYBy(-90);
294 box
.MaxEdge
.rotateXYBy(-90);
296 else if(facedir
== 2)
298 box
.MinEdge
.rotateXYBy(180);
299 box
.MaxEdge
.rotateXYBy(180);
301 else if(facedir
== 3)
303 box
.MinEdge
.rotateXYBy(90);
304 box
.MaxEdge
.rotateXYBy(90);
308 box
.MinEdge
.rotateXYBy(-90);
309 box
.MaxEdge
.rotateXYBy(-90);
312 box
.MinEdge
.rotateYZBy(90);
313 box
.MaxEdge
.rotateYZBy(90);
315 else if(facedir
== 2)
317 box
.MinEdge
.rotateYZBy(180);
318 box
.MaxEdge
.rotateYZBy(180);
320 else if(facedir
== 3)
322 box
.MinEdge
.rotateYZBy(-90);
323 box
.MaxEdge
.rotateYZBy(-90);
327 box
.MinEdge
.rotateXYBy(90);
328 box
.MaxEdge
.rotateXYBy(90);
331 box
.MinEdge
.rotateYZBy(-90);
332 box
.MaxEdge
.rotateYZBy(-90);
334 else if(facedir
== 2)
336 box
.MinEdge
.rotateYZBy(180);
337 box
.MaxEdge
.rotateYZBy(180);
339 else if(facedir
== 3)
341 box
.MinEdge
.rotateYZBy(90);
342 box
.MaxEdge
.rotateYZBy(90);
346 box
.MinEdge
.rotateXYBy(-180);
347 box
.MaxEdge
.rotateXYBy(-180);
350 box
.MinEdge
.rotateXZBy(90);
351 box
.MaxEdge
.rotateXZBy(90);
353 else if(facedir
== 2)
355 box
.MinEdge
.rotateXZBy(180);
356 box
.MaxEdge
.rotateXZBy(180);
358 else if(facedir
== 3)
360 box
.MinEdge
.rotateXZBy(-90);
361 box
.MaxEdge
.rotateXZBy(-90);
368 boxes
.push_back(box
);
371 else if(nodebox
.type
== NODEBOX_WALLMOUNTED
)
373 v3s16 dir
= n
.getWallMountedDir(nodemgr
);
376 if(dir
== v3s16(0,1,0))
378 boxes
.push_back(nodebox
.wall_top
);
381 else if(dir
== v3s16(0,-1,0))
383 boxes
.push_back(nodebox
.wall_bottom
);
390 nodebox
.wall_side
.MinEdge
,
391 nodebox
.wall_side
.MaxEdge
394 for (v3f
&vertex
: vertices
) {
395 if(dir
== v3s16(-1,0,0))
396 vertex
.rotateXZBy(0);
397 if(dir
== v3s16(1,0,0))
398 vertex
.rotateXZBy(180);
399 if(dir
== v3s16(0,0,-1))
400 vertex
.rotateXZBy(90);
401 if(dir
== v3s16(0,0,1))
402 vertex
.rotateXZBy(-90);
405 aabb3f box
= aabb3f(vertices
[0]);
406 box
.addInternalPoint(vertices
[1]);
407 boxes
.push_back(box
);
410 else if (nodebox
.type
== NODEBOX_CONNECTED
)
412 size_t boxes_size
= boxes
.size();
413 boxes_size
+= nodebox
.fixed
.size();
415 boxes_size
+= nodebox
.connect_top
.size();
417 boxes_size
+= nodebox
.disconnected_top
.size();
420 boxes_size
+= nodebox
.connect_bottom
.size();
422 boxes_size
+= nodebox
.disconnected_bottom
.size();
425 boxes_size
+= nodebox
.connect_front
.size();
427 boxes_size
+= nodebox
.disconnected_front
.size();
430 boxes_size
+= nodebox
.connect_left
.size();
432 boxes_size
+= nodebox
.disconnected_left
.size();
435 boxes_size
+= nodebox
.connect_back
.size();
437 boxes_size
+= nodebox
.disconnected_back
.size();
440 boxes_size
+= nodebox
.connect_right
.size();
442 boxes_size
+= nodebox
.disconnected_right
.size();
445 boxes_size
+= nodebox
.disconnected
.size();
448 boxes_size
+= nodebox
.disconnected_sides
.size();
450 boxes
.reserve(boxes_size
);
452 #define BOXESPUSHBACK(c) \
453 for (std::vector<aabb3f>::const_iterator \
455 it != (c).end(); ++it) \
456 (boxes).push_back(*it);
458 BOXESPUSHBACK(nodebox
.fixed
);
461 BOXESPUSHBACK(nodebox
.connect_top
);
463 BOXESPUSHBACK(nodebox
.disconnected_top
);
467 BOXESPUSHBACK(nodebox
.connect_bottom
);
469 BOXESPUSHBACK(nodebox
.disconnected_bottom
);
473 BOXESPUSHBACK(nodebox
.connect_front
);
475 BOXESPUSHBACK(nodebox
.disconnected_front
);
479 BOXESPUSHBACK(nodebox
.connect_left
);
481 BOXESPUSHBACK(nodebox
.disconnected_left
);
484 if (neighbors
& 16) {
485 BOXESPUSHBACK(nodebox
.connect_back
);
487 BOXESPUSHBACK(nodebox
.disconnected_back
);
490 if (neighbors
& 32) {
491 BOXESPUSHBACK(nodebox
.connect_right
);
493 BOXESPUSHBACK(nodebox
.disconnected_right
);
496 if (neighbors
== 0) {
497 BOXESPUSHBACK(nodebox
.disconnected
);
501 BOXESPUSHBACK(nodebox
.disconnected_sides
);
505 else // NODEBOX_REGULAR
507 boxes
.emplace_back(-BS
/2,-BS
/2,-BS
/2,BS
/2,BS
/2,BS
/2);
511 static inline void getNeighborConnectingFace(
512 const v3s16
&p
, const NodeDefManager
*nodedef
,
513 Map
*map
, MapNode n
, u8 bitmask
, u8
*neighbors
)
515 MapNode n2
= map
->getNode(p
);
516 if (nodedef
->nodeboxConnects(n
, n2
, bitmask
))
517 *neighbors
|= bitmask
;
520 u8
MapNode::getNeighbors(v3s16 p
, Map
*map
) const
522 const NodeDefManager
*nodedef
= map
->getNodeDefManager();
524 const ContentFeatures
&f
= nodedef
->get(*this);
525 // locate possible neighboring nodes to connect to
526 if (f
.drawtype
== NDT_NODEBOX
&& f
.node_box
.type
== NODEBOX_CONNECTED
) {
530 getNeighborConnectingFace(p2
, nodedef
, map
, *this, 1, &neighbors
);
534 getNeighborConnectingFace(p2
, nodedef
, map
, *this, 2, &neighbors
);
538 getNeighborConnectingFace(p2
, nodedef
, map
, *this, 4, &neighbors
);
542 getNeighborConnectingFace(p2
, nodedef
, map
, *this, 8, &neighbors
);
546 getNeighborConnectingFace(p2
, nodedef
, map
, *this, 16, &neighbors
);
550 getNeighborConnectingFace(p2
, nodedef
, map
, *this, 32, &neighbors
);
556 void MapNode::getNodeBoxes(const NodeDefManager
*nodemgr
,
557 std::vector
<aabb3f
> *boxes
, u8 neighbors
) const
559 const ContentFeatures
&f
= nodemgr
->get(*this);
560 transformNodeBox(*this, f
.node_box
, nodemgr
, boxes
, neighbors
);
563 void MapNode::getCollisionBoxes(const NodeDefManager
*nodemgr
,
564 std::vector
<aabb3f
> *boxes
, u8 neighbors
) const
566 const ContentFeatures
&f
= nodemgr
->get(*this);
567 if (f
.collision_box
.fixed
.empty())
568 transformNodeBox(*this, f
.node_box
, nodemgr
, boxes
, neighbors
);
570 transformNodeBox(*this, f
.collision_box
, nodemgr
, boxes
, neighbors
);
573 void MapNode::getSelectionBoxes(const NodeDefManager
*nodemgr
,
574 std::vector
<aabb3f
> *boxes
, u8 neighbors
) const
576 const ContentFeatures
&f
= nodemgr
->get(*this);
577 transformNodeBox(*this, f
.selection_box
, nodemgr
, boxes
, neighbors
);
580 u8
MapNode::getMaxLevel(const NodeDefManager
*nodemgr
) const
582 const ContentFeatures
&f
= nodemgr
->get(*this);
583 // todo: after update in all games leave only if (f.param_type_2 ==
584 if( f
.liquid_type
== LIQUID_FLOWING
|| f
.param_type_2
== CPT2_FLOWINGLIQUID
)
585 return LIQUID_LEVEL_MAX
;
586 if(f
.leveled
|| f
.param_type_2
== CPT2_LEVELED
)
587 return f
.leveled_max
;
591 u8
MapNode::getLevel(const NodeDefManager
*nodemgr
) const
593 const ContentFeatures
&f
= nodemgr
->get(*this);
594 // todo: after update in all games leave only if (f.param_type_2 ==
595 if(f
.liquid_type
== LIQUID_SOURCE
)
596 return LIQUID_LEVEL_SOURCE
;
597 if (f
.param_type_2
== CPT2_FLOWINGLIQUID
)
598 return getParam2() & LIQUID_LEVEL_MASK
;
599 if(f
.liquid_type
== LIQUID_FLOWING
) // can remove if all param_type_2 setted
600 return getParam2() & LIQUID_LEVEL_MASK
;
601 if (f
.param_type_2
== CPT2_LEVELED
) {
602 u8 level
= getParam2() & LEVELED_MASK
;
606 // Return static value from nodedef if param2 isn't used for level
607 if (f
.leveled
> f
.leveled_max
)
608 return f
.leveled_max
;
612 s8
MapNode::setLevel(const NodeDefManager
*nodemgr
, s16 level
)
615 const ContentFeatures
&f
= nodemgr
->get(*this);
616 if (f
.param_type_2
== CPT2_FLOWINGLIQUID
617 || f
.liquid_type
== LIQUID_FLOWING
618 || f
.liquid_type
== LIQUID_SOURCE
) {
619 if (level
<= 0) { // liquid can’t exist with zero level
620 setContent(CONTENT_AIR
);
623 if (level
>= LIQUID_LEVEL_SOURCE
) {
624 rest
= level
- LIQUID_LEVEL_SOURCE
;
625 setContent(f
.liquid_alternative_source_id
);
628 setContent(f
.liquid_alternative_flowing_id
);
629 setParam2((level
& LIQUID_LEVEL_MASK
) | (getParam2() & ~LIQUID_LEVEL_MASK
));
631 } else if (f
.param_type_2
== CPT2_LEVELED
) {
632 if (level
< 0) { // zero means default for a leveled nodebox
635 } else if (level
> f
.leveled_max
) {
636 rest
= level
- f
.leveled_max
;
637 level
= f
.leveled_max
;
639 setParam2((level
& LEVELED_MASK
) | (getParam2() & ~LEVELED_MASK
));
644 s8
MapNode::addLevel(const NodeDefManager
*nodemgr
, s16 add
)
646 s16 level
= getLevel(nodemgr
);
648 return setLevel(nodemgr
, level
);
651 u32
MapNode::serializedLength(u8 version
)
653 if(!ser_ver_supported(version
))
654 throw VersionMismatchException("ERROR: MapNode format not supported");
667 void MapNode::serialize(u8
*dest
, u8 version
) const
669 if(!ser_ver_supported(version
))
670 throw VersionMismatchException("ERROR: MapNode format not supported");
672 // Can't do this anymore; we have 16-bit dynamically allocated node IDs
673 // in memory; conversion just won't work in this direction.
675 throw SerializationError("MapNode::serialize: serialization to "
676 "version < 24 not possible");
678 writeU16(dest
+0, param0
);
679 writeU8(dest
+2, param1
);
680 writeU8(dest
+3, param2
);
682 void MapNode::deSerialize(u8
*source
, u8 version
)
684 if(!ser_ver_supported(version
))
685 throw VersionMismatchException("ERROR: MapNode format not supported");
689 deSerialize_pre22(source
, version
);
694 param0
= readU16(source
+0);
695 param1
= readU8(source
+2);
696 param2
= readU8(source
+3);
698 param0
= readU8(source
+0);
699 param1
= readU8(source
+1);
700 param2
= readU8(source
+2);
702 param0
|= ((param2
&0xF0)<<4);
707 void MapNode::serializeBulk(std::ostream
&os
, int version
,
708 const MapNode
*nodes
, u32 nodecount
,
709 u8 content_width
, u8 params_width
, bool compressed
)
711 if (!ser_ver_supported(version
))
712 throw VersionMismatchException("ERROR: MapNode format not supported");
714 sanity_check(content_width
== 2);
715 sanity_check(params_width
== 2);
717 // Can't do this anymore; we have 16-bit dynamically allocated node IDs
718 // in memory; conversion just won't work in this direction.
720 throw SerializationError("MapNode::serializeBulk: serialization to "
721 "version < 24 not possible");
723 size_t databuf_size
= nodecount
* (content_width
+ params_width
);
724 u8
*databuf
= new u8
[databuf_size
];
726 u32 start1
= content_width
* nodecount
;
727 u32 start2
= (content_width
+ 1) * nodecount
;
730 for (u32 i
= 0; i
< nodecount
; i
++) {
731 writeU16(&databuf
[i
* 2], nodes
[i
].param0
);
732 writeU8(&databuf
[start1
+ i
], nodes
[i
].param1
);
733 writeU8(&databuf
[start2
+ i
], nodes
[i
].param2
);
737 Compress data to output stream
741 compressZlib(databuf
, databuf_size
, os
);
743 os
.write((const char*) &databuf
[0], databuf_size
);
748 // Deserialize bulk node data
749 void MapNode::deSerializeBulk(std::istream
&is
, int version
,
750 MapNode
*nodes
, u32 nodecount
,
751 u8 content_width
, u8 params_width
, bool compressed
)
753 if(!ser_ver_supported(version
))
754 throw VersionMismatchException("ERROR: MapNode format not supported");
757 || (content_width
!= 1 && content_width
!= 2)
758 || params_width
!= 2)
759 FATAL_ERROR("Deserialize bulk node data error");
761 // Uncompress or read data
762 u32 len
= nodecount
* (content_width
+ params_width
);
763 SharedBuffer
<u8
> databuf(len
);
766 std::ostringstream
os(std::ios_base::binary
);
767 decompressZlib(is
, os
);
768 std::string s
= os
.str();
770 throw SerializationError("deSerializeBulkNodes: "
771 "decompress resulted in invalid size");
772 memcpy(&databuf
[0], s
.c_str(), len
);
776 is
.read((char*) &databuf
[0], len
);
777 if(is
.eof() || is
.fail())
778 throw SerializationError("deSerializeBulkNodes: "
779 "failed to read bulk node data");
782 // Deserialize content
783 if(content_width
== 1)
785 for(u32 i
=0; i
<nodecount
; i
++)
786 nodes
[i
].param0
= readU8(&databuf
[i
]);
788 else if(content_width
== 2)
790 for(u32 i
=0; i
<nodecount
; i
++)
791 nodes
[i
].param0
= readU16(&databuf
[i
*2]);
794 // Deserialize param1
795 u32 start1
= content_width
* nodecount
;
796 for(u32 i
=0; i
<nodecount
; i
++)
797 nodes
[i
].param1
= readU8(&databuf
[start1
+ i
]);
799 // Deserialize param2
800 u32 start2
= (content_width
+ 1) * nodecount
;
801 if(content_width
== 1)
803 for(u32 i
=0; i
<nodecount
; i
++) {
804 nodes
[i
].param2
= readU8(&databuf
[start2
+ i
]);
805 if(nodes
[i
].param0
> 0x7F){
806 nodes
[i
].param0
<<= 4;
807 nodes
[i
].param0
|= (nodes
[i
].param2
&0xF0)>>4;
808 nodes
[i
].param2
&= 0x0F;
812 else if(content_width
== 2)
814 for(u32 i
=0; i
<nodecount
; i
++)
815 nodes
[i
].param2
= readU8(&databuf
[start2
+ i
]);
822 void MapNode::deSerialize_pre22(const u8
*source
, u8 version
)
828 else if(version
<= 9)
840 param0
|= (param2
&0xf0)>>4;
845 // Convert special values from old version to new
848 // In these versions, CONTENT_IGNORE and CONTENT_AIR
850 // Version 19 is messed up with sometimes the old values and sometimes not
852 param0
= CONTENT_IGNORE
;
853 else if(param0
== 254)
854 param0
= CONTENT_AIR
;
857 // Translate to our known version
858 *this = mapnode_translate_to_internal(*this, version
);