Fix bone-attached entities (#10015)
[oweals/minetest.git] / src / mapnode.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 "irrlichttypes_extrabloated.h"
21 #include "mapnode.h"
22 #include "porting.h"
23 #include "nodedef.h"
24 #include "map.h"
25 #include "content_mapnode.h" // For mapnode_translate_*_internal
26 #include "serialization.h" // For ser_ver_supported
27 #include "util/serialize.h"
28 #include "log.h"
29 #include "util/directiontables.h"
30 #include "util/numeric.h"
31 #include <string>
32 #include <sstream>
33
34 static const Rotation wallmounted_to_rot[] = {
35         ROTATE_0, ROTATE_180, ROTATE_90, ROTATE_270
36 };
37
38 static const u8 rot_to_wallmounted[] = {
39         2, 4, 3, 5
40 };
41
42
43 /*
44         MapNode
45 */
46
47 void MapNode::getColor(const ContentFeatures &f, video::SColor *color) const
48 {
49         if (f.palette) {
50                 *color = (*f.palette)[param2];
51                 return;
52         }
53         *color = f.color;
54 }
55
56 void MapNode::setLight(LightBank bank, u8 a_light, const ContentFeatures &f) noexcept
57 {
58         // If node doesn't contain light data, ignore this
59         if(f.param_type != CPT_LIGHT)
60                 return;
61         if(bank == LIGHTBANK_DAY)
62         {
63                 param1 &= 0xf0;
64                 param1 |= a_light & 0x0f;
65         }
66         else if(bank == LIGHTBANK_NIGHT)
67         {
68                 param1 &= 0x0f;
69                 param1 |= (a_light & 0x0f)<<4;
70         }
71         else
72                 assert("Invalid light bank" == NULL);
73 }
74
75 void MapNode::setLight(LightBank bank, u8 a_light, const NodeDefManager *nodemgr)
76 {
77         setLight(bank, a_light, nodemgr->get(*this));
78 }
79
80 bool MapNode::isLightDayNightEq(const NodeDefManager *nodemgr) const
81 {
82         const ContentFeatures &f = nodemgr->get(*this);
83         bool isEqual;
84
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;
89         } else {
90                 isEqual = true;
91         }
92
93         return isEqual;
94 }
95
96 u8 MapNode::getLight(LightBank bank, const NodeDefManager *nodemgr) const
97 {
98         // Select the brightest of [light source, propagated light]
99         const ContentFeatures &f = nodemgr->get(*this);
100
101         u8 light;
102         if(f.param_type == CPT_LIGHT)
103                 light = bank == LIGHTBANK_DAY ? param1 & 0x0f : (param1 >> 4) & 0x0f;
104         else
105                 light = 0;
106
107         return MYMAX(f.light_source, light);
108 }
109
110 u8 MapNode::getLightRaw(LightBank bank, const ContentFeatures &f) const noexcept
111 {
112         if(f.param_type == CPT_LIGHT)
113                 return bank == LIGHTBANK_DAY ? param1 & 0x0f : (param1 >> 4) & 0x0f;
114         return 0;
115 }
116
117 u8 MapNode::getLightNoChecks(LightBank bank, const ContentFeatures *f) const noexcept
118 {
119         return MYMAX(f->light_source,
120                      bank == LIGHTBANK_DAY ? param1 & 0x0f : (param1 >> 4) & 0x0f);
121 }
122
123 bool MapNode::getLightBanks(u8 &lightday, u8 &lightnight,
124         const NodeDefManager *nodemgr) const
125 {
126         // Select the brightest of [light source, propagated light]
127         const ContentFeatures &f = nodemgr->get(*this);
128         if(f.param_type == CPT_LIGHT)
129         {
130                 lightday = param1 & 0x0f;
131                 lightnight = (param1>>4)&0x0f;
132         }
133         else
134         {
135                 lightday = 0;
136                 lightnight = 0;
137         }
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;
143 }
144
145 u8 MapNode::getFaceDir(const NodeDefManager *nodemgr,
146         bool allow_wallmounted) const
147 {
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];
155         return 0;
156 }
157
158 u8 MapNode::getWallMounted(const NodeDefManager *nodemgr) const
159 {
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;
164         return 0;
165 }
166
167 v3s16 MapNode::getWallMountedDir(const NodeDefManager *nodemgr) const
168 {
169         switch(getWallMounted(nodemgr))
170         {
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);
177         }
178 }
179
180 void MapNode::rotateAlongYAxis(const NodeDefManager *nodemgr, Rotation rot)
181 {
182         ContentParamType2 cpt2 = nodemgr->get(*this).param_type_2;
183
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)
189
190                         0, 1, 2, 3,  // Initial facedir 0 to 3
191                         1, 2, 3, 0,
192                         2, 3, 0, 1,
193                         3, 0, 1, 2,
194
195                         4, 13, 10, 19,  // 4 to 7
196                         5, 14, 11, 16,
197                         6, 15, 8, 17,
198                         7, 12, 9, 18,
199
200                         8, 17, 6, 15,  // 8 to 11
201                         9, 18, 7, 12,
202                         10, 19, 4, 13,
203                         11, 16, 5, 14,
204
205                         12, 9, 18, 7,  // 12 to 15
206                         13, 10, 19, 4,
207                         14, 11, 16, 5,
208                         15, 8, 17, 6,
209
210                         16, 5, 14, 11,  // 16 to 19
211                         17, 6, 15, 8,
212                         18, 7, 12, 9,
213                         19, 4, 13, 10,
214
215                         20, 23, 22, 21,  // 20 to 23
216                         21, 20, 23, 22,
217                         22, 21, 20, 23,
218                         23, 22, 21, 20
219                 };
220                 u8 facedir = (param2 & 31) % 24;
221                 u8 index = facedir * 4 + rot;
222                 param2 &= ~31;
223                 param2 |= rotate_facedir[index];
224         } else if (cpt2 == CPT2_WALLMOUNTED ||
225                         cpt2 == CPT2_COLORED_WALLMOUNTED) {
226                 u8 wmountface = (param2 & 7);
227                 if (wmountface <= 1)
228                         return;
229
230                 Rotation oldrot = wallmounted_to_rot[wmountface - 2];
231                 param2 &= ~7;
232                 param2 |= rot_to_wallmounted[(oldrot - rot) & 3];
233         }
234 }
235
236 void transformNodeBox(const MapNode &n, const NodeBox &nodebox,
237         const NodeDefManager *nodemgr, std::vector<aabb3f> *p_boxes,
238         u8 neighbors = 0)
239 {
240         std::vector<aabb3f> &boxes = *p_boxes;
241
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;
246                 facedir&=0x03;
247                 for (aabb3f box : fixed) {
248                         if (nodebox.type == NODEBOX_LEVELED)
249                                 box.MaxEdge.Y = (-0.5f + n.getLevel(nodemgr) / 64.0f) * BS;
250
251                         switch (axisdir) {
252                         case 0:
253                                 if(facedir == 1)
254                                 {
255                                         box.MinEdge.rotateXZBy(-90);
256                                         box.MaxEdge.rotateXZBy(-90);
257                                 }
258                                 else if(facedir == 2)
259                                 {
260                                         box.MinEdge.rotateXZBy(180);
261                                         box.MaxEdge.rotateXZBy(180);
262                                 }
263                                 else if(facedir == 3)
264                                 {
265                                         box.MinEdge.rotateXZBy(90);
266                                         box.MaxEdge.rotateXZBy(90);
267                                 }
268                                 break;
269                         case 1: // z+
270                                 box.MinEdge.rotateYZBy(90);
271                                 box.MaxEdge.rotateYZBy(90);
272                                 if(facedir == 1)
273                                 {
274                                         box.MinEdge.rotateXYBy(90);
275                                         box.MaxEdge.rotateXYBy(90);
276                                 }
277                                 else if(facedir == 2)
278                                 {
279                                         box.MinEdge.rotateXYBy(180);
280                                         box.MaxEdge.rotateXYBy(180);
281                                 }
282                                 else if(facedir == 3)
283                                 {
284                                         box.MinEdge.rotateXYBy(-90);
285                                         box.MaxEdge.rotateXYBy(-90);
286                                 }
287                                 break;
288                         case 2: //z-
289                                 box.MinEdge.rotateYZBy(-90);
290                                 box.MaxEdge.rotateYZBy(-90);
291                                 if(facedir == 1)
292                                 {
293                                         box.MinEdge.rotateXYBy(-90);
294                                         box.MaxEdge.rotateXYBy(-90);
295                                 }
296                                 else if(facedir == 2)
297                                 {
298                                         box.MinEdge.rotateXYBy(180);
299                                         box.MaxEdge.rotateXYBy(180);
300                                 }
301                                 else if(facedir == 3)
302                                 {
303                                         box.MinEdge.rotateXYBy(90);
304                                         box.MaxEdge.rotateXYBy(90);
305                                 }
306                                 break;
307                         case 3:  //x+
308                                 box.MinEdge.rotateXYBy(-90);
309                                 box.MaxEdge.rotateXYBy(-90);
310                                 if(facedir == 1)
311                                 {
312                                         box.MinEdge.rotateYZBy(90);
313                                         box.MaxEdge.rotateYZBy(90);
314                                 }
315                                 else if(facedir == 2)
316                                 {
317                                         box.MinEdge.rotateYZBy(180);
318                                         box.MaxEdge.rotateYZBy(180);
319                                 }
320                                 else if(facedir == 3)
321                                 {
322                                         box.MinEdge.rotateYZBy(-90);
323                                         box.MaxEdge.rotateYZBy(-90);
324                                 }
325                                 break;
326                         case 4:  //x-
327                                 box.MinEdge.rotateXYBy(90);
328                                 box.MaxEdge.rotateXYBy(90);
329                                 if(facedir == 1)
330                                 {
331                                         box.MinEdge.rotateYZBy(-90);
332                                         box.MaxEdge.rotateYZBy(-90);
333                                 }
334                                 else if(facedir == 2)
335                                 {
336                                         box.MinEdge.rotateYZBy(180);
337                                         box.MaxEdge.rotateYZBy(180);
338                                 }
339                                 else if(facedir == 3)
340                                 {
341                                         box.MinEdge.rotateYZBy(90);
342                                         box.MaxEdge.rotateYZBy(90);
343                                 }
344                                 break;
345                         case 5:
346                                 box.MinEdge.rotateXYBy(-180);
347                                 box.MaxEdge.rotateXYBy(-180);
348                                 if(facedir == 1)
349                                 {
350                                         box.MinEdge.rotateXZBy(90);
351                                         box.MaxEdge.rotateXZBy(90);
352                                 }
353                                 else if(facedir == 2)
354                                 {
355                                         box.MinEdge.rotateXZBy(180);
356                                         box.MaxEdge.rotateXZBy(180);
357                                 }
358                                 else if(facedir == 3)
359                                 {
360                                         box.MinEdge.rotateXZBy(-90);
361                                         box.MaxEdge.rotateXZBy(-90);
362                                 }
363                                 break;
364                         default:
365                                 break;
366                         }
367                         box.repair();
368                         boxes.push_back(box);
369                 }
370         }
371         else if(nodebox.type == NODEBOX_WALLMOUNTED)
372         {
373                 v3s16 dir = n.getWallMountedDir(nodemgr);
374
375                 // top
376                 if(dir == v3s16(0,1,0))
377                 {
378                         boxes.push_back(nodebox.wall_top);
379                 }
380                 // bottom
381                 else if(dir == v3s16(0,-1,0))
382                 {
383                         boxes.push_back(nodebox.wall_bottom);
384                 }
385                 // side
386                 else
387                 {
388                         v3f vertices[2] =
389                         {
390                                 nodebox.wall_side.MinEdge,
391                                 nodebox.wall_side.MaxEdge
392                         };
393
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);
403                         }
404
405                         aabb3f box = aabb3f(vertices[0]);
406                         box.addInternalPoint(vertices[1]);
407                         boxes.push_back(box);
408                 }
409         }
410         else if (nodebox.type == NODEBOX_CONNECTED)
411         {
412                 size_t boxes_size = boxes.size();
413                 boxes_size += nodebox.fixed.size();
414                 if (neighbors & 1)
415                         boxes_size += nodebox.connect_top.size();
416                 else
417                         boxes_size += nodebox.disconnected_top.size();
418
419                 if (neighbors & 2)
420                         boxes_size += nodebox.connect_bottom.size();
421                 else
422                         boxes_size += nodebox.disconnected_bottom.size();
423
424                 if (neighbors & 4)
425                         boxes_size += nodebox.connect_front.size();
426                 else
427                         boxes_size += nodebox.disconnected_front.size();
428
429                 if (neighbors & 8)
430                         boxes_size += nodebox.connect_left.size();
431                 else
432                         boxes_size += nodebox.disconnected_left.size();
433
434                 if (neighbors & 16)
435                         boxes_size += nodebox.connect_back.size();
436                 else
437                         boxes_size += nodebox.disconnected_back.size();
438
439                 if (neighbors & 32)
440                         boxes_size += nodebox.connect_right.size();
441                 else
442                         boxes_size += nodebox.disconnected_right.size();
443
444                 if (neighbors == 0)
445                         boxes_size += nodebox.disconnected.size();
446
447                 if (neighbors < 4)
448                         boxes_size += nodebox.disconnected_sides.size();
449
450                 boxes.reserve(boxes_size);
451
452 #define BOXESPUSHBACK(c) \
453                 for (std::vector<aabb3f>::const_iterator \
454                                 it = (c).begin(); \
455                                 it != (c).end(); ++it) \
456                         (boxes).push_back(*it);
457
458                 BOXESPUSHBACK(nodebox.fixed);
459
460                 if (neighbors & 1) {
461                         BOXESPUSHBACK(nodebox.connect_top);
462                 } else {
463                         BOXESPUSHBACK(nodebox.disconnected_top);
464                 }
465
466                 if (neighbors & 2) {
467                         BOXESPUSHBACK(nodebox.connect_bottom);
468                 } else {
469                         BOXESPUSHBACK(nodebox.disconnected_bottom);
470                 }
471
472                 if (neighbors & 4) {
473                         BOXESPUSHBACK(nodebox.connect_front);
474                 } else {
475                         BOXESPUSHBACK(nodebox.disconnected_front);
476                 }
477
478                 if (neighbors & 8) {
479                         BOXESPUSHBACK(nodebox.connect_left);
480                 } else {
481                         BOXESPUSHBACK(nodebox.disconnected_left);
482                 }
483
484                 if (neighbors & 16) {
485                         BOXESPUSHBACK(nodebox.connect_back);
486                 } else {
487                         BOXESPUSHBACK(nodebox.disconnected_back);
488                 }
489
490                 if (neighbors & 32) {
491                         BOXESPUSHBACK(nodebox.connect_right);
492                 } else {
493                         BOXESPUSHBACK(nodebox.disconnected_right);
494                 }
495
496                 if (neighbors == 0) {
497                         BOXESPUSHBACK(nodebox.disconnected);
498                 }
499
500                 if (neighbors < 4) {
501                         BOXESPUSHBACK(nodebox.disconnected_sides);
502                 }
503
504         }
505         else // NODEBOX_REGULAR
506         {
507                 boxes.emplace_back(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2);
508         }
509 }
510
511 static inline void getNeighborConnectingFace(
512         const v3s16 &p, const NodeDefManager *nodedef,
513         Map *map, MapNode n, u8 bitmask, u8 *neighbors)
514 {
515         MapNode n2 = map->getNode(p);
516         if (nodedef->nodeboxConnects(n, n2, bitmask))
517                 *neighbors |= bitmask;
518 }
519
520 u8 MapNode::getNeighbors(v3s16 p, Map *map) const
521 {
522         const NodeDefManager *nodedef = map->getNodeDefManager();
523         u8 neighbors = 0;
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) {
527                 v3s16 p2 = p;
528
529                 p2.Y++;
530                 getNeighborConnectingFace(p2, nodedef, map, *this, 1, &neighbors);
531
532                 p2 = p;
533                 p2.Y--;
534                 getNeighborConnectingFace(p2, nodedef, map, *this, 2, &neighbors);
535
536                 p2 = p;
537                 p2.Z--;
538                 getNeighborConnectingFace(p2, nodedef, map, *this, 4, &neighbors);
539
540                 p2 = p;
541                 p2.X--;
542                 getNeighborConnectingFace(p2, nodedef, map, *this, 8, &neighbors);
543
544                 p2 = p;
545                 p2.Z++;
546                 getNeighborConnectingFace(p2, nodedef, map, *this, 16, &neighbors);
547
548                 p2 = p;
549                 p2.X++;
550                 getNeighborConnectingFace(p2, nodedef, map, *this, 32, &neighbors);
551         }
552
553         return neighbors;
554 }
555
556 void MapNode::getNodeBoxes(const NodeDefManager *nodemgr,
557         std::vector<aabb3f> *boxes, u8 neighbors) const
558 {
559         const ContentFeatures &f = nodemgr->get(*this);
560         transformNodeBox(*this, f.node_box, nodemgr, boxes, neighbors);
561 }
562
563 void MapNode::getCollisionBoxes(const NodeDefManager *nodemgr,
564         std::vector<aabb3f> *boxes, u8 neighbors) const
565 {
566         const ContentFeatures &f = nodemgr->get(*this);
567         if (f.collision_box.fixed.empty())
568                 transformNodeBox(*this, f.node_box, nodemgr, boxes, neighbors);
569         else
570                 transformNodeBox(*this, f.collision_box, nodemgr, boxes, neighbors);
571 }
572
573 void MapNode::getSelectionBoxes(const NodeDefManager *nodemgr,
574         std::vector<aabb3f> *boxes, u8 neighbors) const
575 {
576         const ContentFeatures &f = nodemgr->get(*this);
577         transformNodeBox(*this, f.selection_box, nodemgr, boxes, neighbors);
578 }
579
580 u8 MapNode::getMaxLevel(const NodeDefManager *nodemgr) const
581 {
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;
588         return 0;
589 }
590
591 u8 MapNode::getLevel(const NodeDefManager *nodemgr) const
592 {
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;
603                 if (level)
604                         return level;
605         }
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;
609         return f.leveled;
610 }
611
612 s8 MapNode::setLevel(const NodeDefManager *nodemgr, s16 level)
613 {
614         s8 rest = 0;
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);
621                         return 0;
622                 }
623                 if (level >= LIQUID_LEVEL_SOURCE) {
624                         rest = level - LIQUID_LEVEL_SOURCE;
625                         setContent(f.liquid_alternative_source_id);
626                         setParam2(0);
627                 } else {
628                         setContent(f.liquid_alternative_flowing_id);
629                         setParam2((level & LIQUID_LEVEL_MASK) | (getParam2() & ~LIQUID_LEVEL_MASK));
630                 }
631         } else if (f.param_type_2 == CPT2_LEVELED) {
632                 if (level < 0) { // zero means default for a leveled nodebox
633                         rest = level;
634                         level = 0;
635                 } else if (level > f.leveled_max) {
636                         rest = level - f.leveled_max;
637                         level = f.leveled_max;
638                 }
639                 setParam2((level & LEVELED_MASK) | (getParam2() & ~LEVELED_MASK));
640         }
641         return rest;
642 }
643
644 s8 MapNode::addLevel(const NodeDefManager *nodemgr, s16 add)
645 {
646         s16 level = getLevel(nodemgr);
647         level += add;
648         return setLevel(nodemgr, level);
649 }
650
651 u32 MapNode::serializedLength(u8 version)
652 {
653         if(!ser_ver_supported(version))
654                 throw VersionMismatchException("ERROR: MapNode format not supported");
655
656         if (version == 0)
657                 return 1;
658
659         if (version <= 9)
660                 return 2;
661
662         if (version <= 23)
663                 return 3;
664
665         return 4;
666 }
667 void MapNode::serialize(u8 *dest, u8 version) const
668 {
669         if(!ser_ver_supported(version))
670                 throw VersionMismatchException("ERROR: MapNode format not supported");
671
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.
674         if(version < 24)
675                 throw SerializationError("MapNode::serialize: serialization to "
676                                 "version < 24 not possible");
677
678         writeU16(dest+0, param0);
679         writeU8(dest+2, param1);
680         writeU8(dest+3, param2);
681 }
682 void MapNode::deSerialize(u8 *source, u8 version)
683 {
684         if(!ser_ver_supported(version))
685                 throw VersionMismatchException("ERROR: MapNode format not supported");
686
687         if(version <= 21)
688         {
689                 deSerialize_pre22(source, version);
690                 return;
691         }
692
693         if(version >= 24){
694                 param0 = readU16(source+0);
695                 param1 = readU8(source+2);
696                 param2 = readU8(source+3);
697         }else{
698                 param0 = readU8(source+0);
699                 param1 = readU8(source+1);
700                 param2 = readU8(source+2);
701                 if(param0 > 0x7F){
702                         param0 |= ((param2&0xF0)<<4);
703                         param2 &= 0x0F;
704                 }
705         }
706 }
707 void MapNode::serializeBulk(std::ostream &os, int version,
708                 const MapNode *nodes, u32 nodecount,
709                 u8 content_width, u8 params_width, bool compressed)
710 {
711         if (!ser_ver_supported(version))
712                 throw VersionMismatchException("ERROR: MapNode format not supported");
713
714         sanity_check(content_width == 2);
715         sanity_check(params_width == 2);
716
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.
719         if (version < 24)
720                 throw SerializationError("MapNode::serializeBulk: serialization to "
721                                 "version < 24 not possible");
722
723         size_t databuf_size = nodecount * (content_width + params_width);
724         u8 *databuf = new u8[databuf_size];
725
726         u32 start1 = content_width * nodecount;
727         u32 start2 = (content_width + 1) * nodecount;
728
729         // Serialize content
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);
734         }
735
736         /*
737                 Compress data to output stream
738         */
739
740         if (compressed)
741                 compressZlib(databuf, databuf_size, os);
742         else
743                 os.write((const char*) &databuf[0], databuf_size);
744
745         delete [] databuf;
746 }
747
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)
752 {
753         if(!ser_ver_supported(version))
754                 throw VersionMismatchException("ERROR: MapNode format not supported");
755
756         if (version < 22
757                         || (content_width != 1 && content_width != 2)
758                         || params_width != 2)
759                 FATAL_ERROR("Deserialize bulk node data error");
760
761         // Uncompress or read data
762         u32 len = nodecount * (content_width + params_width);
763         SharedBuffer<u8> databuf(len);
764         if(compressed)
765         {
766                 std::ostringstream os(std::ios_base::binary);
767                 decompressZlib(is, os);
768                 std::string s = os.str();
769                 if(s.size() != len)
770                         throw SerializationError("deSerializeBulkNodes: "
771                                         "decompress resulted in invalid size");
772                 memcpy(&databuf[0], s.c_str(), len);
773         }
774         else
775         {
776                 is.read((char*) &databuf[0], len);
777                 if(is.eof() || is.fail())
778                         throw SerializationError("deSerializeBulkNodes: "
779                                         "failed to read bulk node data");
780         }
781
782         // Deserialize content
783         if(content_width == 1)
784         {
785                 for(u32 i=0; i<nodecount; i++)
786                         nodes[i].param0 = readU8(&databuf[i]);
787         }
788         else if(content_width == 2)
789         {
790                 for(u32 i=0; i<nodecount; i++)
791                         nodes[i].param0 = readU16(&databuf[i*2]);
792         }
793
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]);
798
799         // Deserialize param2
800         u32 start2 = (content_width + 1) * nodecount;
801         if(content_width == 1)
802         {
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;
809                         }
810                 }
811         }
812         else if(content_width == 2)
813         {
814                 for(u32 i=0; i<nodecount; i++)
815                         nodes[i].param2 = readU8(&databuf[start2 + i]);
816         }
817 }
818
819 /*
820         Legacy serialization
821 */
822 void MapNode::deSerialize_pre22(const u8 *source, u8 version)
823 {
824         if(version <= 1)
825         {
826                 param0 = source[0];
827         }
828         else if(version <= 9)
829         {
830                 param0 = source[0];
831                 param1 = source[1];
832         }
833         else
834         {
835                 param0 = source[0];
836                 param1 = source[1];
837                 param2 = source[2];
838                 if(param0 > 0x7f){
839                         param0 <<= 4;
840                         param0 |= (param2&0xf0)>>4;
841                         param2 &= 0x0f;
842                 }
843         }
844
845         // Convert special values from old version to new
846         if(version <= 19)
847         {
848                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
849                 // are 255 and 254
850                 // Version 19 is messed up with sometimes the old values and sometimes not
851                 if(param0 == 255)
852                         param0 = CONTENT_IGNORE;
853                 else if(param0 == 254)
854                         param0 = CONTENT_AIR;
855         }
856
857         // Translate to our known version
858         *this = mapnode_translate_to_internal(*this, version);
859 }