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