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