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