Add set_breath and get_breath to lua API.
[oweals/minetest.git] / src / nodedef.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 "nodedef.h"
21
22 #include "main.h" // For g_settings
23 #include "itemdef.h"
24 #ifndef SERVER
25 #include "tile.h"
26 #endif
27 #include "log.h"
28 #include "settings.h"
29 #include "nameidmapping.h"
30 #include "util/numeric.h"
31 #include "util/serialize.h"
32 //#include "profiler.h" // For TimeTaker
33
34 /*
35         NodeBox
36 */
37
38 void NodeBox::reset()
39 {
40         type = NODEBOX_REGULAR;
41         // default is empty
42         fixed.clear();
43         // default is sign/ladder-like
44         wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
45         wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
46         wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
47 }
48
49 void NodeBox::serialize(std::ostream &os) const
50 {
51         writeU8(os, 1); // version
52         writeU8(os, type);
53
54         if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
55         {
56                 writeU16(os, fixed.size());
57                 for(std::vector<aabb3f>::const_iterator
58                                 i = fixed.begin();
59                                 i != fixed.end(); i++)
60                 {
61                         writeV3F1000(os, i->MinEdge);
62                         writeV3F1000(os, i->MaxEdge);
63                 }
64         }
65         else if(type == NODEBOX_WALLMOUNTED)
66         {
67                 writeV3F1000(os, wall_top.MinEdge);
68                 writeV3F1000(os, wall_top.MaxEdge);
69                 writeV3F1000(os, wall_bottom.MinEdge);
70                 writeV3F1000(os, wall_bottom.MaxEdge);
71                 writeV3F1000(os, wall_side.MinEdge);
72                 writeV3F1000(os, wall_side.MaxEdge);
73         }
74 }
75
76 void NodeBox::deSerialize(std::istream &is)
77 {
78         int version = readU8(is);
79         if(version != 1)
80                 throw SerializationError("unsupported NodeBox version");
81
82         reset();
83
84         type = (enum NodeBoxType)readU8(is);
85
86         if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
87         {
88                 u16 fixed_count = readU16(is);
89                 while(fixed_count--)
90                 {
91                         aabb3f box;
92                         box.MinEdge = readV3F1000(is);
93                         box.MaxEdge = readV3F1000(is);
94                         fixed.push_back(box);
95                 }
96         }
97         else if(type == NODEBOX_WALLMOUNTED)
98         {
99                 wall_top.MinEdge = readV3F1000(is);
100                 wall_top.MaxEdge = readV3F1000(is);
101                 wall_bottom.MinEdge = readV3F1000(is);
102                 wall_bottom.MaxEdge = readV3F1000(is);
103                 wall_side.MinEdge = readV3F1000(is);
104                 wall_side.MaxEdge = readV3F1000(is);
105         }
106 }
107
108 /*
109         TileDef
110 */
111
112 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
113 {
114         if(protocol_version >= 17)
115                 writeU8(os, 1); 
116         else
117                 writeU8(os, 0);
118         os<<serializeString(name);
119         writeU8(os, animation.type);
120         writeU16(os, animation.aspect_w);
121         writeU16(os, animation.aspect_h);
122         writeF1000(os, animation.length);
123         if(protocol_version >= 17)
124                 writeU8(os, backface_culling);
125 }
126
127 void TileDef::deSerialize(std::istream &is)
128 {
129         int version = readU8(is);
130         name = deSerializeString(is);
131         animation.type = (TileAnimationType)readU8(is);
132         animation.aspect_w = readU16(is);
133         animation.aspect_h = readU16(is);
134         animation.length = readF1000(is);
135         if(version >= 1)
136                 backface_culling = readU8(is);
137 }
138
139 /*
140         SimpleSoundSpec serialization
141 */
142
143 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
144                 std::ostream &os)
145 {
146         os<<serializeString(ss.name);
147         writeF1000(os, ss.gain);
148 }
149 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is)
150 {
151         ss.name = deSerializeString(is);
152         ss.gain = readF1000(is);
153 }
154
155 /*
156         ContentFeatures
157 */
158
159 ContentFeatures::ContentFeatures()
160 {
161         reset();
162 }
163
164 ContentFeatures::~ContentFeatures()
165 {
166 }
167
168 void ContentFeatures::reset()
169 {
170         /*
171                 Cached stuff
172         */
173 #ifndef SERVER
174         solidness = 2;
175         visual_solidness = 0;
176         backface_culling = true;
177 #endif
178         has_on_construct = false;
179         has_on_destruct = false;
180         has_after_destruct = false;
181         /*
182                 Actual data
183
184                 NOTE: Most of this is always overridden by the default values given
185                       in builtin.lua
186         */
187         name = "";
188         groups.clear();
189         // Unknown nodes can be dug
190         groups["dig_immediate"] = 2;
191         drawtype = NDT_NORMAL;
192         visual_scale = 1.0;
193         for(u32 i=0; i<6; i++)
194                 tiledef[i] = TileDef();
195         for(u16 j=0; j<CF_SPECIAL_COUNT; j++)
196                 tiledef_special[j] = TileDef();
197         alpha = 255;
198         post_effect_color = video::SColor(0, 0, 0, 0);
199         param_type = CPT_NONE;
200         param_type_2 = CPT2_NONE;
201         is_ground_content = false;
202         light_propagates = false;
203         sunlight_propagates = false;
204         walkable = true;
205         pointable = true;
206         diggable = true;
207         climbable = false;
208         buildable_to = false;
209         rightclickable = true;
210         leveled = 0;
211         liquid_type = LIQUID_NONE;
212         liquid_alternative_flowing = "";
213         liquid_alternative_source = "";
214         liquid_viscosity = 0;
215         liquid_renewable = true;
216         drowning = true;
217         light_source = 0;
218         damage_per_second = 0;
219         node_box = NodeBox();
220         selection_box = NodeBox();
221         legacy_facedir_simple = false;
222         legacy_wallmounted = false;
223         sound_footstep = SimpleSoundSpec();
224         sound_dig = SimpleSoundSpec("__group");
225         sound_dug = SimpleSoundSpec();
226 }
227
228 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version)
229 {
230         if(protocol_version < 14){
231                 serializeOld(os, protocol_version);
232                 return;
233         }
234
235         writeU8(os, 6); // version
236         os<<serializeString(name);
237         writeU16(os, groups.size());
238         for(ItemGroupList::const_iterator
239                         i = groups.begin(); i != groups.end(); i++){
240                 os<<serializeString(i->first);
241                 writeS16(os, i->second);
242         }
243         writeU8(os, drawtype);
244         writeF1000(os, visual_scale);
245         writeU8(os, 6);
246         for(u32 i=0; i<6; i++)
247                 tiledef[i].serialize(os, protocol_version);
248         writeU8(os, CF_SPECIAL_COUNT);
249         for(u32 i=0; i<CF_SPECIAL_COUNT; i++){
250                 tiledef_special[i].serialize(os, protocol_version);
251         }
252         writeU8(os, alpha);
253         writeU8(os, post_effect_color.getAlpha());
254         writeU8(os, post_effect_color.getRed());
255         writeU8(os, post_effect_color.getGreen());
256         writeU8(os, post_effect_color.getBlue());
257         writeU8(os, param_type);
258         writeU8(os, param_type_2);
259         writeU8(os, is_ground_content);
260         writeU8(os, light_propagates);
261         writeU8(os, sunlight_propagates);
262         writeU8(os, walkable);
263         writeU8(os, pointable);
264         writeU8(os, diggable);
265         writeU8(os, climbable);
266         writeU8(os, buildable_to);
267         os<<serializeString(""); // legacy: used to be metadata_name
268         writeU8(os, liquid_type);
269         os<<serializeString(liquid_alternative_flowing);
270         os<<serializeString(liquid_alternative_source);
271         writeU8(os, liquid_viscosity);
272         writeU8(os, liquid_renewable);
273         writeU8(os, light_source);
274         writeU32(os, damage_per_second);
275         node_box.serialize(os);
276         selection_box.serialize(os);
277         writeU8(os, legacy_facedir_simple);
278         writeU8(os, legacy_wallmounted);
279         serializeSimpleSoundSpec(sound_footstep, os);
280         serializeSimpleSoundSpec(sound_dig, os);
281         serializeSimpleSoundSpec(sound_dug, os);
282         writeU8(os, rightclickable);
283         // Stuff below should be moved to correct place in a version that otherwise changes
284         // the protocol version
285         writeU8(os, drowning);
286         writeU8(os, leveled);
287 }
288
289 void ContentFeatures::deSerialize(std::istream &is)
290 {
291         int version = readU8(is);
292         if(version != 6){
293                 deSerializeOld(is, version);
294                 return;
295         }
296
297         name = deSerializeString(is);
298         groups.clear();
299         u32 groups_size = readU16(is);
300         for(u32 i=0; i<groups_size; i++){
301                 std::string name = deSerializeString(is);
302                 int value = readS16(is);
303                 groups[name] = value;
304         }
305         drawtype = (enum NodeDrawType)readU8(is);
306         visual_scale = readF1000(is);
307         if(readU8(is) != 6)
308                 throw SerializationError("unsupported tile count");
309         for(u32 i=0; i<6; i++)
310                 tiledef[i].deSerialize(is);
311         if(readU8(is) != CF_SPECIAL_COUNT)
312                 throw SerializationError("unsupported CF_SPECIAL_COUNT");
313         for(u32 i=0; i<CF_SPECIAL_COUNT; i++)
314                 tiledef_special[i].deSerialize(is);
315         alpha = readU8(is);
316         post_effect_color.setAlpha(readU8(is));
317         post_effect_color.setRed(readU8(is));
318         post_effect_color.setGreen(readU8(is));
319         post_effect_color.setBlue(readU8(is));
320         param_type = (enum ContentParamType)readU8(is);
321         param_type_2 = (enum ContentParamType2)readU8(is);
322         is_ground_content = readU8(is);
323         light_propagates = readU8(is);
324         sunlight_propagates = readU8(is);
325         walkable = readU8(is);
326         pointable = readU8(is);
327         diggable = readU8(is);
328         climbable = readU8(is);
329         buildable_to = readU8(is);
330         deSerializeString(is); // legacy: used to be metadata_name
331         liquid_type = (enum LiquidType)readU8(is);
332         liquid_alternative_flowing = deSerializeString(is);
333         liquid_alternative_source = deSerializeString(is);
334         liquid_viscosity = readU8(is);
335         liquid_renewable = readU8(is);
336         light_source = readU8(is);
337         damage_per_second = readU32(is);
338         node_box.deSerialize(is);
339         selection_box.deSerialize(is);
340         legacy_facedir_simple = readU8(is);
341         legacy_wallmounted = readU8(is);
342         deSerializeSimpleSoundSpec(sound_footstep, is);
343         deSerializeSimpleSoundSpec(sound_dig, is);
344         deSerializeSimpleSoundSpec(sound_dug, is);
345         rightclickable = readU8(is);
346         // If you add anything here, insert it primarily inside the try-catch
347         // block to not need to increase the version.
348         try{
349                 // Stuff below should be moved to correct place in a version that
350                 // otherwise changes the protocol version
351                 drowning = readU8(is);
352                 leveled = readU8(is);
353         }catch(SerializationError &e) {};
354 }
355
356 /*
357         CNodeDefManager
358 */
359
360 class CNodeDefManager: public IWritableNodeDefManager
361 {
362 public:
363         void clear()
364         {
365                 m_content_features.clear();
366                 m_name_id_mapping.clear();
367                 m_name_id_mapping_with_aliases.clear();
368                 m_group_to_items.clear();
369                 m_next_id = 0;
370
371                 u32 initial_length = 0;
372                 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
373                 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
374                 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
375                 m_content_features.resize(initial_length);
376
377                 // Set CONTENT_UNKNOWN
378                 {
379                         ContentFeatures f;
380                         f.name = "unknown";
381                         // Insert directly into containers
382                         content_t c = CONTENT_UNKNOWN;
383                         m_content_features[c] = f;
384                         addNameIdMapping(c, f.name);
385                 }
386
387                 // Set CONTENT_AIR
388                 {
389                         ContentFeatures f;
390                         f.name = "air";
391                         f.drawtype = NDT_AIRLIKE;
392                         f.param_type = CPT_LIGHT;
393                         f.light_propagates = true;
394                         f.sunlight_propagates = true;
395                         f.walkable = false;
396                         f.pointable = false;
397                         f.diggable = false;
398                         f.buildable_to = true;
399                         // Insert directly into containers
400                         content_t c = CONTENT_AIR;
401                         m_content_features[c] = f;
402                         addNameIdMapping(c, f.name);
403                 }
404
405                 // Set CONTENT_IGNORE
406                 {
407                         ContentFeatures f;
408                         f.name = "ignore";
409                         f.drawtype = NDT_AIRLIKE;
410                         f.param_type = CPT_NONE;
411                         f.light_propagates = false;
412                         f.sunlight_propagates = false;
413                         f.walkable = false;
414                         f.pointable = false;
415                         f.diggable = false;
416                         // A way to remove accidental CONTENT_IGNOREs
417                         f.buildable_to = true;
418                         // Insert directly into containers
419                         content_t c = CONTENT_IGNORE;
420                         m_content_features[c] = f;
421                         addNameIdMapping(c, f.name);
422                 }
423         }
424         CNodeDefManager()
425         {
426                 clear();
427         }
428         virtual ~CNodeDefManager()
429         {
430         }
431         virtual IWritableNodeDefManager* clone()
432         {
433                 CNodeDefManager *mgr = new CNodeDefManager();
434                 *mgr = *this;
435                 return mgr;
436         }
437         virtual const ContentFeatures& get(content_t c) const
438         {
439                 if(c < m_content_features.size())
440                         return m_content_features[c];
441                 else
442                         return m_content_features[CONTENT_UNKNOWN];
443         }
444         virtual const ContentFeatures& get(const MapNode &n) const
445         {
446                 return get(n.getContent());
447         }
448         virtual bool getId(const std::string &name, content_t &result) const
449         {
450                 std::map<std::string, content_t>::const_iterator
451                         i = m_name_id_mapping_with_aliases.find(name);
452                 if(i == m_name_id_mapping_with_aliases.end())
453                         return false;
454                 result = i->second;
455                 return true;
456         }
457         virtual content_t getId(const std::string &name) const
458         {
459                 content_t id = CONTENT_IGNORE;
460                 getId(name, id);
461                 return id;
462         }
463         virtual void getIds(const std::string &name, std::set<content_t> &result)
464                         const
465         {
466                 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
467                 if(name.substr(0,6) != "group:"){
468                         content_t id = CONTENT_IGNORE;
469                         if(getId(name, id))
470                                 result.insert(id);
471                         return;
472                 }
473                 std::string group = name.substr(6);
474
475                 std::map<std::string, GroupItems>::const_iterator
476                         i = m_group_to_items.find(group);
477                 if (i == m_group_to_items.end())
478                         return;
479
480                 const GroupItems &items = i->second;
481                 for (GroupItems::const_iterator j = items.begin();
482                         j != items.end(); ++j) {
483                         if ((*j).second != 0)
484                                 result.insert((*j).first);
485                 }
486                 //printf("getIds: %dus\n", t.stop());
487         }
488         virtual const ContentFeatures& get(const std::string &name) const
489         {
490                 content_t id = CONTENT_UNKNOWN;
491                 getId(name, id);
492                 return get(id);
493         }
494         // returns CONTENT_IGNORE if no free ID found
495         content_t allocateId()
496         {
497                 for(content_t id = m_next_id;
498                                 id >= m_next_id; // overflow?
499                                 ++id){
500                         while(id >= m_content_features.size()){
501                                 m_content_features.push_back(ContentFeatures());
502                         }
503                         const ContentFeatures &f = m_content_features[id];
504                         if(f.name == ""){
505                                 m_next_id = id + 1;
506                                 return id;
507                         }
508                 }
509                 // If we arrive here, an overflow occurred in id.
510                 // That means no ID was found
511                 return CONTENT_IGNORE;
512         }
513         // IWritableNodeDefManager
514         virtual content_t set(const std::string &name,
515                         const ContentFeatures &def)
516         {
517                 assert(name != "");
518                 assert(name == def.name);
519
520                 // Don't allow redefining ignore (but allow air and unknown)
521                 if(name == "ignore"){
522                         infostream<<"NodeDefManager: WARNING: Ignoring "
523                                         <<"CONTENT_IGNORE redefinition"<<std::endl;
524                         return CONTENT_IGNORE;
525                 }
526
527                 content_t id = CONTENT_IGNORE;
528                 bool found = m_name_id_mapping.getId(name, id);  // ignore aliases
529                 if(!found){
530                         // Get new id
531                         id = allocateId();
532                         if(id == CONTENT_IGNORE){
533                                 infostream<<"NodeDefManager: WARNING: Absolute "
534                                                 <<"limit reached"<<std::endl;
535                                 return CONTENT_IGNORE;
536                         }
537                         assert(id != CONTENT_IGNORE);
538                         addNameIdMapping(id, name);
539                 }
540                 m_content_features[id] = def;
541                 verbosestream<<"NodeDefManager: registering content id \""<<id
542                                 <<"\": name=\""<<def.name<<"\""<<std::endl;
543
544                 // Add this content to the list of all groups it belongs to
545                 // FIXME: This should remove a node from groups it no longer
546                 // belongs to when a node is re-registered
547                 for (ItemGroupList::const_iterator i = def.groups.begin();
548                         i != def.groups.end(); ++i) {
549                         std::string group_name = i->first;
550                         
551                         std::map<std::string, GroupItems>::iterator
552                                 j = m_group_to_items.find(group_name);
553                         if (j == m_group_to_items.end()) {
554                                 m_group_to_items[group_name].push_back(
555                                                 std::make_pair(id, i->second));
556                         } else {
557                                 GroupItems &items = j->second;
558                                 items.push_back(std::make_pair(id, i->second));
559                         }
560                 }
561                 return id;
562         }
563         virtual content_t allocateDummy(const std::string &name)
564         {
565                 assert(name != "");
566                 ContentFeatures f;
567                 f.name = name;
568                 return set(name, f);
569         }
570         virtual void updateAliases(IItemDefManager *idef)
571         {
572                 std::set<std::string> all = idef->getAll();
573                 m_name_id_mapping_with_aliases.clear();
574                 for(std::set<std::string>::iterator
575                                 i = all.begin(); i != all.end(); i++)
576                 {
577                         std::string name = *i;
578                         std::string convert_to = idef->getAlias(name);
579                         content_t id;
580                         if(m_name_id_mapping.getId(convert_to, id))
581                         {
582                                 m_name_id_mapping_with_aliases.insert(
583                                                 std::make_pair(name, id));
584                         }
585                 }
586         }
587         virtual void updateTextures(ITextureSource *tsrc)
588         {
589 #ifndef SERVER
590                 infostream<<"CNodeDefManager::updateTextures(): Updating "
591                                 <<"textures in node definitions"<<std::endl;
592
593                 bool new_style_water = g_settings->getBool("new_style_water");
594                 bool new_style_leaves = g_settings->getBool("new_style_leaves");
595                 bool opaque_water = g_settings->getBool("opaque_water");
596
597                 for(u32 i=0; i<m_content_features.size(); i++)
598                 {
599                         ContentFeatures *f = &m_content_features[i];
600
601                         // Figure out the actual tiles to use
602                         TileDef tiledef[6];
603                         for(u32 j=0; j<6; j++)
604                         {
605                                 tiledef[j] = f->tiledef[j];
606                                 if(tiledef[j].name == "")
607                                         tiledef[j].name = "unknown_node.png";
608                         }
609
610                         bool is_liquid = false;
611                         switch(f->drawtype){
612                         default:
613                         case NDT_NORMAL:
614                                 f->solidness = 2;
615                                 break;
616                         case NDT_AIRLIKE:
617                                 f->solidness = 0;
618                                 break;
619                         case NDT_LIQUID:
620                                 assert(f->liquid_type == LIQUID_SOURCE);
621                                 if(opaque_water)
622                                         f->alpha = 255;
623                                 if(new_style_water){
624                                         f->solidness = 0;
625                                 } else {
626                                         f->solidness = 1;
627                                         f->backface_culling = false;
628                                 }
629                                 is_liquid = true;
630                                 break;
631                         case NDT_FLOWINGLIQUID:
632                                 assert(f->liquid_type == LIQUID_FLOWING);
633                                 f->solidness = 0;
634                                 if(opaque_water)
635                                         f->alpha = 255;
636                                 is_liquid = true;
637                                 break;
638                         case NDT_GLASSLIKE:
639                                 f->solidness = 0;
640                                 f->visual_solidness = 1;
641                                 break;
642                         case NDT_GLASSLIKE_FRAMED:
643                                 f->solidness = 0;
644                                 f->visual_solidness = 1;
645                                 break;
646                         case NDT_ALLFACES:
647                                 f->solidness = 0;
648                                 f->visual_solidness = 1;
649                                 break;
650                         case NDT_ALLFACES_OPTIONAL:
651                                 if(new_style_leaves){
652                                         f->drawtype = NDT_ALLFACES;
653                                         f->solidness = 0;
654                                         f->visual_solidness = 1;
655                                 } else {
656                                         f->drawtype = NDT_NORMAL;
657                                         f->solidness = 2;
658                                         for(u32 i=0; i<6; i++){
659                                                 tiledef[i].name += std::string("^[noalpha");
660                                         }
661                                 }
662                                 break;
663                         case NDT_PLANTLIKE:
664                                 f->solidness = 0;
665                                 f->backface_culling = false;
666                                 break;
667                         case NDT_TORCHLIKE:
668                         case NDT_SIGNLIKE:
669                         case NDT_FENCELIKE:
670                         case NDT_RAILLIKE:
671                         case NDT_NODEBOX:
672                                 f->solidness = 0;
673                                 break;
674                         }
675
676                         u8 material_type;
677                         if (is_liquid)
678                                 material_type = (f->alpha == 255) ? TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
679                         else
680                                 material_type = (f->alpha == 255) ? TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
681
682                         // Tiles (fill in f->tiles[])
683                         for(u16 j=0; j<6; j++){
684                                 // Texture
685                                 f->tiles[j].texture = tsrc->getTexture(
686                                                 tiledef[j].name,
687                                                 &f->tiles[j].texture_id);
688                                 // Alpha
689                                 f->tiles[j].alpha = f->alpha;
690                                 // Material type
691                                 f->tiles[j].material_type = material_type;
692                                 // Material flags
693                                 f->tiles[j].material_flags = 0;
694                                 if(f->backface_culling)
695                                         f->tiles[j].material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
696                                 if(tiledef[j].animation.type == TAT_VERTICAL_FRAMES)
697                                         f->tiles[j].material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
698                                 // Animation parameters
699                                 if(f->tiles[j].material_flags &
700                                                 MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
701                                 {
702                                         // Get texture size to determine frame count by
703                                         // aspect ratio
704                                         v2u32 size = f->tiles[j].texture->getOriginalSize();
705                                         int frame_height = (float)size.X /
706                                                         (float)tiledef[j].animation.aspect_w *
707                                                         (float)tiledef[j].animation.aspect_h;
708                                         int frame_count = size.Y / frame_height;
709                                         int frame_length_ms = 1000.0 *
710                                                         tiledef[j].animation.length / frame_count;
711                                         f->tiles[j].animation_frame_count = frame_count;
712                                         f->tiles[j].animation_frame_length_ms = frame_length_ms;
713
714                                         // If there are no frames for an animation, switch
715                                         // animation off (so that having specified an animation
716                                         // for something but not using it in the texture pack
717                                         // gives no overhead)
718                                         if(frame_count == 1){
719                                                 f->tiles[j].material_flags &=
720                                                                 ~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
721                                         }
722                                 }
723                         }
724                         // Special tiles (fill in f->special_tiles[])
725                         for(u16 j=0; j<CF_SPECIAL_COUNT; j++){
726                                 // Texture
727                                 f->special_tiles[j].texture = tsrc->getTexture(
728                                                 f->tiledef_special[j].name,
729                                                 &f->special_tiles[j].texture_id);
730                                 // Alpha
731                                 f->special_tiles[j].alpha = f->alpha;
732                                 // Material type
733                                 f->special_tiles[j].material_type = material_type;
734                                 // Material flags
735                                 f->special_tiles[j].material_flags = 0;
736                                 if(f->tiledef_special[j].backface_culling)
737                                         f->special_tiles[j].material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
738                                 if(f->tiledef_special[j].animation.type == TAT_VERTICAL_FRAMES)
739                                         f->special_tiles[j].material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
740                                 // Animation parameters
741                                 if(f->special_tiles[j].material_flags &
742                                                 MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
743                                 {
744                                         // Get texture size to determine frame count by
745                                         // aspect ratio
746                                         v2u32 size = f->special_tiles[j].texture->getOriginalSize();
747                                         int frame_height = (float)size.X /
748                                                         (float)f->tiledef_special[j].animation.aspect_w *
749                                                         (float)f->tiledef_special[j].animation.aspect_h;
750                                         int frame_count = size.Y / frame_height;
751                                         int frame_length_ms = 1000.0 *
752                                                         f->tiledef_special[j].animation.length / frame_count;
753                                         f->special_tiles[j].animation_frame_count = frame_count;
754                                         f->special_tiles[j].animation_frame_length_ms = frame_length_ms;
755
756                                         // If there are no frames for an animation, switch
757                                         // animation off (so that having specified an animation
758                                         // for something but not using it in the texture pack
759                                         // gives no overhead)
760                                         if(frame_count == 1){
761                                                 f->special_tiles[j].material_flags &=
762                                                                 ~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
763                                         }
764                                 }
765                         }
766                 }
767 #endif
768         }
769         void serialize(std::ostream &os, u16 protocol_version)
770         {
771                 writeU8(os, 1); // version
772                 u16 count = 0;
773                 std::ostringstream os2(std::ios::binary);
774                 for(u32 i=0; i<m_content_features.size(); i++)
775                 {
776                         if(i == CONTENT_IGNORE || i == CONTENT_AIR
777                                         || i == CONTENT_UNKNOWN)
778                                 continue;
779                         ContentFeatures *f = &m_content_features[i];
780                         if(f->name == "")
781                                 continue;
782                         writeU16(os2, i);
783                         // Wrap it in a string to allow different lengths without
784                         // strict version incompatibilities
785                         std::ostringstream wrapper_os(std::ios::binary);
786                         f->serialize(wrapper_os, protocol_version);
787                         os2<<serializeString(wrapper_os.str());
788
789                         assert(count + 1 > count); // must not overflow
790                         count++;
791                 }
792                 writeU16(os, count);
793                 os<<serializeLongString(os2.str());
794         }
795         void deSerialize(std::istream &is)
796         {
797                 clear();
798                 int version = readU8(is);
799                 if(version != 1)
800                         throw SerializationError("unsupported NodeDefinitionManager version");
801                 u16 count = readU16(is);
802                 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
803                 ContentFeatures f;
804                 for(u16 n=0; n<count; n++){
805                         u16 i = readU16(is2);
806
807                         // Read it from the string wrapper
808                         std::string wrapper = deSerializeString(is2);
809                         std::istringstream wrapper_is(wrapper, std::ios::binary);
810                         f.deSerialize(wrapper_is);
811
812                         // Check error conditions
813                         if(i == CONTENT_IGNORE || i == CONTENT_AIR
814                                         || i == CONTENT_UNKNOWN){
815                                 infostream<<"NodeDefManager::deSerialize(): WARNING: "
816                                         <<"not changing builtin node "<<i
817                                         <<std::endl;
818                                 continue;
819                         }
820                         if(f.name == ""){
821                                 infostream<<"NodeDefManager::deSerialize(): WARNING: "
822                                         <<"received empty name"<<std::endl;
823                                 continue;
824                         }
825                         u16 existing_id;
826                         bool found = m_name_id_mapping.getId(f.name, existing_id);  // ignore aliases
827                         if(found && i != existing_id){
828                                 infostream<<"NodeDefManager::deSerialize(): WARNING: "
829                                         <<"already defined with different ID: "
830                                         <<f.name<<std::endl;
831                                 continue;
832                         }
833
834                         // All is ok, add node definition with the requested ID
835                         if(i >= m_content_features.size())
836                                 m_content_features.resize((u32)(i) + 1);
837                         m_content_features[i] = f;
838                         addNameIdMapping(i, f.name);
839                         verbosestream<<"deserialized "<<f.name<<std::endl;
840                 }
841         }
842 private:
843         void addNameIdMapping(content_t i, std::string name)
844         {
845                 m_name_id_mapping.set(i, name);
846                 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
847         }
848 private:
849         // Features indexed by id
850         std::vector<ContentFeatures> m_content_features;
851         // A mapping for fast converting back and forth between names and ids
852         NameIdMapping m_name_id_mapping;
853         // Like m_name_id_mapping, but only from names to ids, and includes
854         // item aliases too. Updated by updateAliases()
855         // Note: Not serialized.
856         std::map<std::string, content_t> m_name_id_mapping_with_aliases;
857         // A mapping from groups to a list of content_ts (and their levels)
858         // that belong to it.  Necessary for a direct lookup in getIds().
859         // Note: Not serialized.
860         std::map<std::string, GroupItems> m_group_to_items;
861         // Next possibly free id
862         content_t m_next_id;
863 };
864
865 IWritableNodeDefManager* createNodeDefManager()
866 {
867         return new CNodeDefManager();
868 }
869
870 /*
871         Serialization of old ContentFeatures formats
872 */
873
874 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version)
875 {
876         if(protocol_version == 13)
877         {
878                 writeU8(os, 5); // version
879                 os<<serializeString(name);
880                 writeU16(os, groups.size());
881                 for(ItemGroupList::const_iterator
882                                 i = groups.begin(); i != groups.end(); i++){
883                         os<<serializeString(i->first);
884                         writeS16(os, i->second);
885                 }
886                 writeU8(os, drawtype);
887                 writeF1000(os, visual_scale);
888                 writeU8(os, 6);
889                 for(u32 i=0; i<6; i++)
890                         tiledef[i].serialize(os, protocol_version);
891                 writeU8(os, CF_SPECIAL_COUNT);
892                 for(u32 i=0; i<CF_SPECIAL_COUNT; i++){
893                         tiledef_special[i].serialize(os, protocol_version);
894                 }
895                 writeU8(os, alpha);
896                 writeU8(os, post_effect_color.getAlpha());
897                 writeU8(os, post_effect_color.getRed());
898                 writeU8(os, post_effect_color.getGreen());
899                 writeU8(os, post_effect_color.getBlue());
900                 writeU8(os, param_type);
901                 writeU8(os, param_type_2);
902                 writeU8(os, is_ground_content);
903                 writeU8(os, light_propagates);
904                 writeU8(os, sunlight_propagates);
905                 writeU8(os, walkable);
906                 writeU8(os, pointable);
907                 writeU8(os, diggable);
908                 writeU8(os, climbable);
909                 writeU8(os, buildable_to);
910                 os<<serializeString(""); // legacy: used to be metadata_name
911                 writeU8(os, liquid_type);
912                 os<<serializeString(liquid_alternative_flowing);
913                 os<<serializeString(liquid_alternative_source);
914                 writeU8(os, liquid_viscosity);
915                 writeU8(os, light_source);
916                 writeU32(os, damage_per_second);
917                 node_box.serialize(os);
918                 selection_box.serialize(os);
919                 writeU8(os, legacy_facedir_simple);
920                 writeU8(os, legacy_wallmounted);
921                 serializeSimpleSoundSpec(sound_footstep, os);
922                 serializeSimpleSoundSpec(sound_dig, os);
923                 serializeSimpleSoundSpec(sound_dug, os);
924         }
925         else
926         {
927                 throw SerializationError("ContentFeatures::serialize(): Unsupported version requested");
928         }
929 }
930
931 void ContentFeatures::deSerializeOld(std::istream &is, int version)
932 {
933         if(version == 5) // In PROTOCOL_VERSION 13
934         {
935                 name = deSerializeString(is);
936                 groups.clear();
937                 u32 groups_size = readU16(is);
938                 for(u32 i=0; i<groups_size; i++){
939                         std::string name = deSerializeString(is);
940                         int value = readS16(is);
941                         groups[name] = value;
942                 }
943                 drawtype = (enum NodeDrawType)readU8(is);
944                 visual_scale = readF1000(is);
945                 if(readU8(is) != 6)
946                         throw SerializationError("unsupported tile count");
947                 for(u32 i=0; i<6; i++)
948                         tiledef[i].deSerialize(is);
949                 if(readU8(is) != CF_SPECIAL_COUNT)
950                         throw SerializationError("unsupported CF_SPECIAL_COUNT");
951                 for(u32 i=0; i<CF_SPECIAL_COUNT; i++)
952                         tiledef_special[i].deSerialize(is);
953                 alpha = readU8(is);
954                 post_effect_color.setAlpha(readU8(is));
955                 post_effect_color.setRed(readU8(is));
956                 post_effect_color.setGreen(readU8(is));
957                 post_effect_color.setBlue(readU8(is));
958                 param_type = (enum ContentParamType)readU8(is);
959                 param_type_2 = (enum ContentParamType2)readU8(is);
960                 is_ground_content = readU8(is);
961                 light_propagates = readU8(is);
962                 sunlight_propagates = readU8(is);
963                 walkable = readU8(is);
964                 pointable = readU8(is);
965                 diggable = readU8(is);
966                 climbable = readU8(is);
967                 buildable_to = readU8(is);
968                 deSerializeString(is); // legacy: used to be metadata_name
969                 liquid_type = (enum LiquidType)readU8(is);
970                 liquid_alternative_flowing = deSerializeString(is);
971                 liquid_alternative_source = deSerializeString(is);
972                 liquid_viscosity = readU8(is);
973                 light_source = readU8(is);
974                 damage_per_second = readU32(is);
975                 node_box.deSerialize(is);
976                 selection_box.deSerialize(is);
977                 legacy_facedir_simple = readU8(is);
978                 legacy_wallmounted = readU8(is);
979                 deSerializeSimpleSoundSpec(sound_footstep, is);
980                 deSerializeSimpleSoundSpec(sound_dig, is);
981                 deSerializeSimpleSoundSpec(sound_dug, is);
982         }
983         else
984         {
985                 throw SerializationError("unsupported ContentFeatures version");
986         }
987 }
988