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