1 /*
2 nodedef.cpp
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 */
5
6 /*
7 This file is part of Freeminer.
8
9 Freeminer is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Freeminer is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Freeminer. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "nodedef.h"
24
25 #include "main.h" // For g_settings
26 #include "itemdef.h"
27 #ifndef SERVER
28 #include "tile.h"
29 #include "mesh.h"
30 #include <IMeshManipulator.h>
31 #endif
32 #include "log.h"
33 #include "settings.h"
34 #include "nameidmapping.h"
35 #include "util/numeric.h"
36 #include "util/serialize.h"
37 //#include "profiler.h" // For TimeTaker
38 #include "shader.h"
39 #include "exceptions.h"
40 #include "debug.h"
41 #include "gamedef.h"
42
43 /*
44 NodeBox
45 */
46
reset()47 void NodeBox::reset()
48 {
49 type = NODEBOX_REGULAR;
50 // default is empty
51 fixed.clear();
52 // default is sign/ladder-like
53 wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
54 wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
55 wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
56 }
57
serialize(std::ostream & os,u16 protocol_version) const58 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
59 {
60 int version = protocol_version >= 21 ? 2 : 1;
61 writeU8(os, version);
62
63 if (version == 1 && type == NODEBOX_LEVELED)
64 writeU8(os, NODEBOX_FIXED);
65 else
66 writeU8(os, type);
67
68 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
69 {
70 writeU16(os, fixed.size());
71 for(std::vector<aabb3f>::const_iterator
72 i = fixed.begin();
73 i != fixed.end(); i++)
74 {
75 writeV3F1000(os, i->MinEdge);
76 writeV3F1000(os, i->MaxEdge);
77 }
78 }
79 else if(type == NODEBOX_WALLMOUNTED)
80 {
81 writeV3F1000(os, wall_top.MinEdge);
82 writeV3F1000(os, wall_top.MaxEdge);
83 writeV3F1000(os, wall_bottom.MinEdge);
84 writeV3F1000(os, wall_bottom.MaxEdge);
85 writeV3F1000(os, wall_side.MinEdge);
86 writeV3F1000(os, wall_side.MaxEdge);
87 }
88 }
89
deSerialize(std::istream & is)90 void NodeBox::deSerialize(std::istream &is)
91 {
92 int version = readU8(is);
93 if(version < 1 || version > 2)
94 throw SerializationError("unsupported NodeBox version");
95
96 reset();
97
98 type = (enum NodeBoxType)readU8(is);
99
100 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
101 {
102 u16 fixed_count = readU16(is);
103 while(fixed_count--)
104 {
105 aabb3f box;
106 box.MinEdge = readV3F1000(is);
107 box.MaxEdge = readV3F1000(is);
108 fixed.push_back(box);
109 }
110 }
111 else if(type == NODEBOX_WALLMOUNTED)
112 {
113 wall_top.MinEdge = readV3F1000(is);
114 wall_top.MaxEdge = readV3F1000(is);
115 wall_bottom.MinEdge = readV3F1000(is);
116 wall_bottom.MaxEdge = readV3F1000(is);
117 wall_side.MinEdge = readV3F1000(is);
118 wall_side.MaxEdge = readV3F1000(is);
119 }
120 }
121
122 /*
123 TileDef
124 */
125
serialize(std::ostream & os,u16 protocol_version) const126 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
127 {
128 if(protocol_version >= 17)
129 writeU8(os, 1);
130 else
131 writeU8(os, 0);
132 os<<serializeString(name);
133 writeU8(os, animation.type);
134 writeU16(os, animation.aspect_w);
135 writeU16(os, animation.aspect_h);
136 writeF1000(os, animation.length);
137 if(protocol_version >= 17)
138 writeU8(os, backface_culling);
139 }
140
deSerialize(std::istream & is)141 void TileDef::deSerialize(std::istream &is)
142 {
143 int version = readU8(is);
144 name = deSerializeString(is);
145 animation.type = (TileAnimationType)readU8(is);
146 animation.aspect_w = readU16(is);
147 animation.aspect_h = readU16(is);
148 animation.length = readF1000(is);
149 if(version >= 1)
150 backface_culling = readU8(is);
151 }
152
153 /*
154 SimpleSoundSpec serialization
155 */
156
serializeSimpleSoundSpec(const SimpleSoundSpec & ss,std::ostream & os)157 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
158 std::ostream &os)
159 {
160 os<<serializeString(ss.name);
161 writeF1000(os, ss.gain);
162 }
deSerializeSimpleSoundSpec(SimpleSoundSpec & ss,std::istream & is)163 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is)
164 {
165 ss.name = deSerializeString(is);
166 ss.gain = readF1000(is);
167 }
168
169 /*
170 ContentFeatures
171 */
172
ContentFeatures()173 ContentFeatures::ContentFeatures()
174 {
175 reset();
176 }
177
~ContentFeatures()178 ContentFeatures::~ContentFeatures()
179 {
180 }
181
reset()182 void ContentFeatures::reset()
183 {
184 /*
185 Cached stuff
186 */
187 //#ifndef SERVER
188 solidness = 2;
189 visual_solidness = 0;
190 backface_culling = true;
191 //#endif
192 has_on_construct = false;
193 has_on_destruct = false;
194 has_after_destruct = false;
195 has_on_activate = false;
196 has_on_deactivate = false;
197 /*
198 Actual data
199
200 NOTE: Most of this is always overridden by the default values given
201 in builtin.lua
202 */
203 name = "";
204 groups.clear();
205 // Unknown nodes can be dug
206 groups["dig_immediate"] = 2;
207 drawtype = NDT_NORMAL;
208 mesh = "";
209 #ifndef SERVER
210 for(u32 i = 0; i < 24; i++)
211 mesh_ptr[i] = NULL;
212 #endif
213 visual_scale = 1.0;
214 for(u32 i = 0; i < 6; i++)
215 tiledef[i] = TileDef();
216 for(u16 j = 0; j < CF_SPECIAL_COUNT; j++)
217 tiledef_special[j] = TileDef();
218 alpha = 255;
219 post_effect_color = video::SColor(0, 0, 0, 0);
220 param_type = CPT_NONE;
221 param_type_2 = CPT2_NONE;
222 is_ground_content = false;
223 light_propagates = false;
224 sunlight_propagates = false;
225 walkable = true;
226 pointable = true;
227 diggable = true;
228 climbable = false;
229 buildable_to = false;
230 rightclickable = true;
231 leveled = 0;
232 liquid_type = LIQUID_NONE;
233 liquid_alternative_flowing = "";
234 liquid_alternative_source = "";
235 liquid_viscosity = 0;
236 liquid_renewable = true;
237 freeze = "";
238 melt = "";
239 drowning = 0;
240 light_source = 0;
241 damage_per_second = 0;
242 node_box = NodeBox();
243 selection_box = NodeBox();
244 collision_box = NodeBox();
245 waving = 0;
246 legacy_facedir_simple = false;
247 legacy_wallmounted = false;
248 sound_footstep = SimpleSoundSpec();
249 sound_dig = SimpleSoundSpec("__group");
250 sound_dug = SimpleSoundSpec();
251
252 is_circuit_element = false;
253 is_wire = false;
254 is_wire_connector = false;
255 for(int i = 0; i < 6; ++i)
256 {
257 wire_connections[i] = 0;
258 }
259 for(int i = 0; i < 64; ++i)
260 {
261 circuit_element_func[i] = 0;
262 }
263 circuit_element_delay = 0;
264 }
265
serialize(std::ostream & os,u16 protocol_version)266 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version)
267 {
268 if(protocol_version < 24){
269 serializeOld(os, protocol_version);
270 return;
271 }
272
273 writeU8(os, 7); // version
274 os<<serializeString(name);
275 writeU16(os, groups.size());
276 for(ItemGroupList::const_iterator
277 i = groups.begin(); i != groups.end(); i++){
278 os<<serializeString(i->first);
279 writeS16(os, i->second);
280 }
281 writeU8(os, drawtype);
282 writeF1000(os, visual_scale);
283 writeU8(os, 6);
284 for(u32 i = 0; i < 6; i++)
285 tiledef[i].serialize(os, protocol_version);
286 writeU8(os, CF_SPECIAL_COUNT);
287 for(u32 i = 0; i < CF_SPECIAL_COUNT; i++){
288 tiledef_special[i].serialize(os, protocol_version);
289 }
290 writeU8(os, alpha);
291 writeU8(os, post_effect_color.getAlpha());
292 writeU8(os, post_effect_color.getRed());
293 writeU8(os, post_effect_color.getGreen());
294 writeU8(os, post_effect_color.getBlue());
295 writeU8(os, param_type);
296 writeU8(os, param_type_2);
297 writeU8(os, is_ground_content);
298 writeU8(os, light_propagates);
299 writeU8(os, sunlight_propagates);
300 writeU8(os, walkable);
301 writeU8(os, pointable);
302 writeU8(os, diggable);
303 writeU8(os, climbable);
304 writeU8(os, buildable_to);
305 os<<serializeString(""); // legacy: used to be metadata_name
306 writeU8(os, liquid_type);
307 os<<serializeString(liquid_alternative_flowing);
308 os<<serializeString(liquid_alternative_source);
309 writeU8(os, liquid_viscosity);
310 writeU8(os, liquid_renewable);
311 writeU8(os, light_source);
312 writeU32(os, damage_per_second);
313 node_box.serialize(os, protocol_version);
314 selection_box.serialize(os, protocol_version);
315 writeU8(os, legacy_facedir_simple);
316 writeU8(os, legacy_wallmounted);
317 serializeSimpleSoundSpec(sound_footstep, os);
318 serializeSimpleSoundSpec(sound_dig, os);
319 serializeSimpleSoundSpec(sound_dug, os);
320 writeU8(os, rightclickable);
321 writeU8(os, drowning);
322 writeU8(os, leveled);
323 writeU8(os, 0/*liquid_range*/);
324 writeU8(os, waving);
325 // Stuff below should be moved to correct place in a version that otherwise changes
326 // the protocol version
327 os<<serializeString(mesh);
328 collision_box.serialize(os, protocol_version);
329 }
330
deSerialize(std::istream & is)331 void ContentFeatures::deSerialize(std::istream &is)
332 {
333 int version = readU8(is);
334 if(version != 7){
335 deSerializeOld(is, version);
336 return;
337 }
338
339 name = deSerializeString(is);
340 groups.clear();
341 u32 groups_size = readU16(is);
342 for(u32 i = 0; i < groups_size; i++){
343 std::string name = deSerializeString(is);
344 int value = readS16(is);
345 groups[name] = value;
346 }
347 drawtype = (enum NodeDrawType)readU8(is);
348 visual_scale = readF1000(is);
349 if(readU8(is) != 6)
350 throw SerializationError("unsupported tile count");
351 for(u32 i = 0; i < 6; i++)
352 tiledef[i].deSerialize(is);
353 if(readU8(is) != CF_SPECIAL_COUNT)
354 throw SerializationError("unsupported CF_SPECIAL_COUNT");
355 for(u32 i = 0; i < CF_SPECIAL_COUNT; i++)
356 tiledef_special[i].deSerialize(is);
357 alpha = readU8(is);
358 post_effect_color.setAlpha(readU8(is));
359 post_effect_color.setRed(readU8(is));
360 post_effect_color.setGreen(readU8(is));
361 post_effect_color.setBlue(readU8(is));
362 param_type = (enum ContentParamType)readU8(is);
363 param_type_2 = (enum ContentParamType2)readU8(is);
364 is_ground_content = readU8(is);
365 light_propagates = readU8(is);
366 sunlight_propagates = readU8(is);
367 walkable = readU8(is);
368 pointable = readU8(is);
369 diggable = readU8(is);
370 climbable = readU8(is);
371 buildable_to = readU8(is);
372 deSerializeString(is); // legacy: used to be metadata_name
373 liquid_type = (enum LiquidType)readU8(is);
374 liquid_alternative_flowing = deSerializeString(is);
375 liquid_alternative_source = deSerializeString(is);
376 liquid_viscosity = readU8(is);
377 liquid_renewable = readU8(is);
378 light_source = readU8(is);
379 damage_per_second = readU32(is);
380 node_box.deSerialize(is);
381 selection_box.deSerialize(is);
382 legacy_facedir_simple = readU8(is);
383 legacy_wallmounted = readU8(is);
384 deSerializeSimpleSoundSpec(sound_footstep, is);
385 deSerializeSimpleSoundSpec(sound_dig, is);
386 deSerializeSimpleSoundSpec(sound_dug, is);
387 rightclickable = readU8(is);
388 drowning = readU8(is);
389 leveled = readU8(is);
390 /*liquid_range =*/ readU8(is);
391 waving = readU8(is);
392 // If you add anything here, insert it primarily inside the try-catch
393 // block to not need to increase the version.
394 try{
395 // Stuff below should be moved to correct place in a version that
396 // otherwise changes the protocol version
397 mesh = deSerializeString(is);
398 collision_box.deSerialize(is);
399 }catch(SerializationError &e) {};
400 }
401
402 /*
403 CNodeDefManager
404 */
405
406 class CNodeDefManager: public IWritableNodeDefManager {
407 public:
408 CNodeDefManager();
409 virtual ~CNodeDefManager();
410 void clear();
411 virtual IWritableNodeDefManager *clone();
412 inline virtual const ContentFeatures& get(content_t c) const;
413 inline virtual const ContentFeatures& get(const MapNode &n) const;
414 virtual bool getId(const std::string &name, content_t &result) const;
415 virtual content_t getId(const std::string &name) const;
416 virtual void getIds(const std::string &name, std::unordered_set<content_t> &result) const;
417 virtual void getIds(const std::string &name, FMBitset &result) const;
418 virtual const ContentFeatures& get(const std::string &name) const;
419 content_t allocateId();
420 virtual content_t set(const std::string &name, const ContentFeatures &def);
421 virtual content_t allocateDummy(const std::string &name);
422 virtual void updateAliases(IItemDefManager *idef);
423 virtual void updateTextures(IGameDef *gamedef);
424 void serialize(std::ostream &os, u16 protocol_version);
425 void deSerialize(std::istream &is);
426 virtual NodeResolver *getResolver();
427
428 private:
429 void addNameIdMapping(content_t i, std::string name);
430 #ifndef SERVER
431 void fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, TileDef *tiledef,
432 u32 shader_id, bool use_normal_texture, bool backface_culling,
433 u8 alpha, u8 material_type);
434 #endif
435
436 // Features indexed by id
437 std::vector<ContentFeatures> m_content_features;
438
439 // A mapping for fast converting back and forth between names and ids
440 NameIdMapping m_name_id_mapping;
441
442 // Like m_name_id_mapping, but only from names to ids, and includes
443 // item aliases too. Updated by updateAliases()
444 // Note: Not serialized.
445
446 std::map<std::string, content_t> m_name_id_mapping_with_aliases;
447
448 // A mapping from groups to a list of content_ts (and their levels)
449 // that belong to it. Necessary for a direct lookup in getIds().
450 // Note: Not serialized.
451 std::map<std::string, GroupItems> m_group_to_items;
452
453 // Next possibly free id
454 content_t m_next_id;
455
456 // NodeResolver to queue pending node resolutions
457 NodeResolver m_resolver;
458 };
459
460
CNodeDefManager()461 CNodeDefManager::CNodeDefManager() :
462 m_resolver(this)
463 {
464 clear();
465 }
466
467
~CNodeDefManager()468 CNodeDefManager::~CNodeDefManager()
469 {
470 #ifndef SERVER
471 for (u32 i = 0; i < m_content_features.size(); i++) {
472 ContentFeatures *f = &m_content_features[i];
473 for (u32 j = 0; j < 24; j++) {
474 if (f->mesh_ptr[j])
475 f->mesh_ptr[j]->drop();
476 }
477 }
478 #endif
479 }
480
481
clear()482 void CNodeDefManager::clear()
483 {
484 m_content_features.clear();
485 m_name_id_mapping.clear();
486 m_name_id_mapping_with_aliases.clear();
487 m_group_to_items.clear();
488 m_next_id = 0;
489
490 u32 initial_length = 0;
491 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
492 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
493 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
494 m_content_features.resize(initial_length);
495
496 // Set CONTENT_UNKNOWN
497 {
498 ContentFeatures f;
499 f.name = "unknown";
500 // Insert directly into containers
501 content_t c = CONTENT_UNKNOWN;
502 m_content_features[c] = f;
503 addNameIdMapping(c, f.name);
504 }
505
506 // Set CONTENT_AIR
507 {
508 ContentFeatures f;
509 f.name = "air";
510 f.drawtype = NDT_AIRLIKE;
511 f.param_type = CPT_LIGHT;
512 f.light_propagates = true;
513 f.sunlight_propagates = true;
514 f.walkable = false;
515 f.pointable = false;
516 f.diggable = false;
517 f.buildable_to = true;
518 f.is_ground_content = true;
519 #ifndef SERVER
520 f.color_avg = video::SColor(0,255,255,255);
521 #endif
522 // Insert directly into containers
523 content_t c = CONTENT_AIR;
524 m_content_features[c] = f;
525 addNameIdMapping(c, f.name);
526 }
527
528 // Set CONTENT_IGNORE
529 {
530 ContentFeatures f;
531 f.name = "ignore";
532 f.drawtype = NDT_AIRLIKE;
533 f.param_type = CPT_NONE;
534 f.light_propagates = false;
535 f.sunlight_propagates = false;
536 f.walkable = false;
537 f.pointable = false;
538 f.diggable = false;
539 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
540 f.is_ground_content = true;
541 #ifndef SERVER
542 f.color_avg = video::SColor(0,255,255,255);
543 #endif
544 // Insert directly into containers
545 content_t c = CONTENT_IGNORE;
546 m_content_features[c] = f;
547 addNameIdMapping(c, f.name);
548 }
549 }
550
551
clone()552 IWritableNodeDefManager *CNodeDefManager::clone()
553 {
554 CNodeDefManager *mgr = new CNodeDefManager();
555 *mgr = *this;
556 return mgr;
557 }
558
559
get(content_t c) const560 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
561 {
562 return c < m_content_features.size()
563 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
564 }
565
566
get(const MapNode & n) const567 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
568 {
569 return get(n.getContent());
570 }
571
572
getId(const std::string & name,content_t & result) const573 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
574 {
575 std::map<std::string, content_t>::const_iterator
576 i = m_name_id_mapping_with_aliases.find(name);
577 if(i == m_name_id_mapping_with_aliases.end())
578 return false;
579 result = i->second;
580 return true;
581 }
582
583
getId(const std::string & name) const584 content_t CNodeDefManager::getId(const std::string &name) const
585 {
586 content_t id = CONTENT_IGNORE;
587 getId(name, id);
588 return id;
589 }
590
591
getIds(const std::string & name,std::unordered_set<content_t> & result) const592 void CNodeDefManager::getIds(const std::string &name,
593 std::unordered_set<content_t> &result) const
594 {
595 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
596 if (name.substr(0,6) != "group:") {
597 content_t id = CONTENT_IGNORE;
598 if(getId(name, id))
599 result.insert(id);
600 return;
601 }
602 std::string group = name.substr(6);
603
604 std::map<std::string, GroupItems>::const_iterator
605 i = m_group_to_items.find(group);
606 if (i == m_group_to_items.end())
607 return;
608
609 const GroupItems &items = i->second;
610 for (GroupItems::const_iterator j = items.begin();
611 j != items.end(); ++j) {
612 if ((*j).second != 0)
613 result.insert((*j).first);
614 }
615 //printf("getIds: %dus\n", t.stop());
616 }
617
getIds(const std::string & name,FMBitset & result) const618 void CNodeDefManager::getIds(const std::string &name, FMBitset &result) const {
619 if(name.substr(0,6) != "group:"){
620 content_t id = CONTENT_IGNORE;
621 if(getId(name, id))
622 result.set(id, true);
623 return;
624 }
625 std::string group = name.substr(6);
626
627 std::map<std::string, GroupItems>::const_iterator
628 i = m_group_to_items.find(group);
629 if (i == m_group_to_items.end())
630 return;
631
632 const GroupItems &items = i->second;
633 for (GroupItems::const_iterator j = items.begin();
634 j != items.end(); ++j) {
635 if ((*j).second != 0)
636 result.set((*j).first, true);
637 }
638 }
639
640
get(const std::string & name) const641 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
642 {
643 content_t id = CONTENT_UNKNOWN;
644 getId(name, id);
645 return get(id);
646 }
647
648
649 // returns CONTENT_IGNORE if no free ID found
allocateId()650 content_t CNodeDefManager::allocateId()
651 {
652 for (content_t id = m_next_id;
653 id >= m_next_id; // overflow?
654 ++id) {
655 while (id >= m_content_features.size()) {
656 m_content_features.push_back(ContentFeatures());
657 }
658 const ContentFeatures &f = m_content_features[id];
659 if (f.name == "") {
660 m_next_id = id + 1;
661 return id;
662 }
663 }
664 // If we arrive here, an overflow occurred in id.
665 // That means no ID was found
666 return CONTENT_IGNORE;
667 }
668
669
670 // IWritableNodeDefManager
set(const std::string & name,const ContentFeatures & def)671 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
672 {
673 assert(name != "");
674 assert(name == def.name);
675
676 // Don't allow redefining ignore (but allow air and unknown)
677 if (name == "ignore") {
678 infostream << "NodeDefManager: WARNING: Ignoring "
679 "CONTENT_IGNORE redefinition"<<std::endl;
680 return CONTENT_IGNORE;
681 }
682
683 content_t id = CONTENT_IGNORE;
684 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
685 // Get new id
686 id = allocateId();
687 if (id == CONTENT_IGNORE) {
688 infostream << "NodeDefManager: WARNING: Absolute "
689 "limit reached" << std::endl;
690 return CONTENT_IGNORE;
691 }
692 assert(id != CONTENT_IGNORE);
693 addNameIdMapping(id, name);
694 }
695 m_content_features[id] = def;
696 verbosestream << "NodeDefManager: registering content id \"" << id
697 << "\": name=\"" << def.name << "\""<<std::endl;
698
699 // Add this content to the list of all groups it belongs to
700 // FIXME: This should remove a node from groups it no longer
701 // belongs to when a node is re-registered
702 for (ItemGroupList::const_iterator i = def.groups.begin();
703 i != def.groups.end(); ++i) {
704 std::string group_name = i->first;
705
706 std::map<std::string, GroupItems>::iterator
707 j = m_group_to_items.find(group_name);
708 if (j == m_group_to_items.end()) {
709 m_group_to_items[group_name].push_back(
710 std::make_pair(id, i->second));
711 } else {
712 GroupItems &items = j->second;
713 items.push_back(std::make_pair(id, i->second));
714 }
715 }
716 return id;
717 }
718
719
allocateDummy(const std::string & name)720 content_t CNodeDefManager::allocateDummy(const std::string &name)
721 {
722 assert(name != "");
723 ContentFeatures f;
724 f.name = name;
725 return set(name, f);
726 }
727
728
updateAliases(IItemDefManager * idef)729 void CNodeDefManager::updateAliases(IItemDefManager *idef)
730 {
731 std::set<std::string> all = idef->getAll();
732 m_name_id_mapping_with_aliases.clear();
733 for (std::set<std::string>::iterator
734 i = all.begin(); i != all.end(); i++) {
735 std::string name = *i;
736 std::string convert_to = idef->getAlias(name);
737 content_t id;
738 if (m_name_id_mapping.getId(convert_to, id)) {
739 m_name_id_mapping_with_aliases.insert(
740 std::make_pair(name, id));
741 }
742 }
743 }
744
745
updateTextures(IGameDef * gamedef)746 void CNodeDefManager::updateTextures(IGameDef *gamedef)
747 {
748 infostream << "CNodeDefManager::updateTextures(): Updating "
749 "textures in node definitions" << std::endl;
750
751 ITextureSource *tsrc = !gamedef ? nullptr : gamedef->tsrc();
752 IShaderSource *shdsrc = !gamedef ? nullptr : gamedef->getShaderSource();
753 scene::ISceneManager* smgr = !gamedef ? nullptr : gamedef->getSceneManager();
754 scene::IMeshManipulator* meshmanip = !smgr ? nullptr :smgr->getMeshManipulator();
755
756 bool new_style_water = g_settings->getBool("new_style_water");
757 bool new_style_leaves = g_settings->getBool("new_style_leaves");
758 bool connected_glass = g_settings->getBool("connected_glass");
759 bool opaque_water = g_settings->getBool("opaque_water");
760 bool enable_shaders = g_settings->getBool("enable_shaders");
761 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
762 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
763 bool enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
764
765 bool use_normal_texture = enable_shaders &&
766 (enable_bumpmapping || enable_parallax_occlusion);
767
768 for (u32 i = 0; i < m_content_features.size(); i++) {
769 ContentFeatures *f = &m_content_features[i];
770
771 // Figure out the actual tiles to use
772 TileDef tiledef[6];
773 for (u32 j = 0; j < 6; j++) {
774 tiledef[j] = f->tiledef[j];
775 if (tiledef[j].name == "")
776 tiledef[j].name = "unknown_node.png";
777 }
778
779 bool is_liquid = false;
780 bool is_water_surface = false;
781
782 u8 material_type = (f->alpha == 255) ?
783 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
784
785 switch (f->drawtype) {
786 default:
787 case NDT_NORMAL:
788 f->solidness = 2;
789 break;
790 case NDT_AIRLIKE:
791 f->solidness = 0;
792 break;
793 case NDT_LIQUID:
794 assert(f->liquid_type == LIQUID_SOURCE);
795 if (opaque_water)
796 f->alpha = 255;
797 if (new_style_water){
798 f->solidness = 0;
799 } else {
800 f->solidness = 1;
801 f->backface_culling = false;
802 }
803 is_liquid = true;
804 break;
805 case NDT_FLOWINGLIQUID:
806 assert(f->liquid_type == LIQUID_FLOWING);
807 f->solidness = 0;
808 if (opaque_water)
809 f->alpha = 255;
810 is_liquid = true;
811 break;
812 case NDT_GLASSLIKE:
813 f->solidness = 0;
814 f->visual_solidness = 1;
815 break;
816 case NDT_GLASSLIKE_FRAMED:
817 f->solidness = 0;
818 f->visual_solidness = 1;
819 break;
820 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
821 f->solidness = 0;
822 f->visual_solidness = 1;
823 f->drawtype = connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
824 break;
825 case NDT_ALLFACES:
826 f->solidness = 0;
827 f->visual_solidness = 1;
828 break;
829 case NDT_ALLFACES_OPTIONAL:
830 if (new_style_leaves) {
831 f->drawtype = NDT_ALLFACES;
832 f->solidness = 0;
833 f->visual_solidness = 1;
834 } else {
835 f->drawtype = NDT_NORMAL;
836 f->solidness = 2;
837 for (u32 i = 0; i < 6; i++)
838 tiledef[i].name += std::string("^[noalpha");
839 }
840 if (f->waving == 1)
841 material_type = TILE_MATERIAL_WAVING_LEAVES;
842 break;
843 case NDT_PLANTLIKE:
844 f->solidness = 0;
845 f->backface_culling = false;
846 if (f->waving == 1)
847 material_type = TILE_MATERIAL_WAVING_PLANTS;
848 break;
849 case NDT_FIRELIKE:
850 f->backface_culling = false;
851 f->solidness = 0;
852 break;
853 case NDT_MESH:
854 f->solidness = 0;
855 f->backface_culling = false;
856 break;
857 case NDT_TORCHLIKE:
858 case NDT_SIGNLIKE:
859 case NDT_FENCELIKE:
860 case NDT_RAILLIKE:
861 case NDT_NODEBOX:
862 f->solidness = 0;
863 break;
864 }
865
866 #ifndef SERVER
867
868 if (is_liquid) {
869 material_type = (f->alpha == 255) ?
870 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
871 if (f->name == "default:water_source")
872 is_water_surface = true;
873 }
874
875 u32 tile_shader[6];
876 if (shdsrc) {
877 for (u16 j = 0; j < 6; j++) {
878 tile_shader[j] = shdsrc->getShader("nodes_shader",
879 material_type, f->drawtype);
880 }
881
882 if (is_water_surface) {
883 tile_shader[0] = shdsrc->getShader("water_surface_shader",
884 material_type, f->drawtype);
885 }
886 }
887 if (tsrc) {
888 // Tiles (fill in f->tiles[])
889 for (u16 j = 0; j < 6; j++) {
890 fillTileAttribs(tsrc, &f->tiles[j], &tiledef[j], tile_shader[j],
891 use_normal_texture, f->backface_culling, f->alpha, material_type);
892 }
893
894 // Special tiles (fill in f->special_tiles[])
895 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
896 fillTileAttribs(tsrc, &f->special_tiles[j], &f->tiledef_special[j],
897 tile_shader[j], use_normal_texture,
898 f->tiledef_special[j].backface_culling, f->alpha, material_type);
899 }
900
901 if ((f->drawtype == NDT_MESH) && (f->mesh != "")) {
902 // Meshnode drawtype
903 // Read the mesh and apply scale
904 f->mesh_ptr[0] = gamedef->getMesh(f->mesh);
905 if (f->mesh_ptr[0]){
906 v3f scale = v3f(1.0, 1.0, 1.0) * BS * f->visual_scale;
907 scaleMesh(f->mesh_ptr[0], scale);
908 recalculateBoundingBox(f->mesh_ptr[0]);
909 }
910 } else if ((f->drawtype == NDT_NODEBOX) &&
911 ((f->node_box.type == NODEBOX_REGULAR) ||
912 (f->node_box.type == NODEBOX_FIXED)) &&
913 (!f->node_box.fixed.empty())) {
914 //Convert regular nodebox nodes to meshnodes
915 //Change the drawtype and apply scale
916 f->drawtype = NDT_MESH;
917 f->mesh_ptr[0] = convertNodeboxNodeToMesh(f);
918 v3f scale = v3f(1.0, 1.0, 1.0) * f->visual_scale;
919 scaleMesh(f->mesh_ptr[0], scale);
920 recalculateBoundingBox(f->mesh_ptr[0]);
921 }
922
923 //Cache 6dfacedir and wallmounted rotated clones of meshes
924 if (enable_mesh_cache && f->mesh_ptr[0] && (f->param_type_2 == CPT2_FACEDIR)) {
925 for (u16 j = 1; j < 24; j++) {
926 f->mesh_ptr[j] = cloneMesh(f->mesh_ptr[0]);
927 rotateMeshBy6dFacedir(f->mesh_ptr[j], j);
928 recalculateBoundingBox(f->mesh_ptr[j]);
929 meshmanip->recalculateNormals(f->mesh_ptr[j], true, false);
930 }
931 } else if (enable_mesh_cache && f->mesh_ptr[0] && (f->param_type_2 == CPT2_WALLMOUNTED)) {
932 static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2};
933 for (u16 j = 1; j < 6; j++) {
934 f->mesh_ptr[j] = cloneMesh(f->mesh_ptr[0]);
935 rotateMeshBy6dFacedir(f->mesh_ptr[j], wm_to_6d[j]);
936 recalculateBoundingBox(f->mesh_ptr[j]);
937 meshmanip->recalculateNormals(f->mesh_ptr[j], true, false);
938 }
939 rotateMeshBy6dFacedir(f->mesh_ptr[0], wm_to_6d[0]);
940 recalculateBoundingBox(f->mesh_ptr[0]);
941 meshmanip->recalculateNormals(f->mesh_ptr[0], true, false);
942 }
943 f->color_avg = tsrc->getTextureInfo(f->tiles[0].texture_id)->color; // TODO: make average
944 }
945 #endif
946 }
947 }
948
949
950 #ifndef SERVER
fillTileAttribs(ITextureSource * tsrc,TileSpec * tile,TileDef * tiledef,u32 shader_id,bool use_normal_texture,bool backface_culling,u8 alpha,u8 material_type)951 void CNodeDefManager::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
952 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
953 bool backface_culling, u8 alpha, u8 material_type)
954 {
955 tile->shader_id = shader_id;
956 tile->texture = tsrc->getTexture(tiledef->name, &tile->texture_id);
957 tile->alpha = alpha;
958 tile->material_type = material_type;
959
960 // Normal texture
961 if (use_normal_texture)
962 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
963
964 // Material flags
965 tile->material_flags = 0;
966 if (backface_culling)
967 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
968 if (tiledef->animation.type == TAT_VERTICAL_FRAMES)
969 tile->material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
970
971 // Animation parameters
972 int frame_count = 1;
973 if (tile->material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) {
974 // Get texture size to determine frame count by aspect ratio
975 v2u32 size = tile->texture->getOriginalSize();
976 int frame_height = (float)size.X /
977 (tiledef->animation.aspect_w ? (float)tiledef->animation.aspect_w : 1) *
978 (tiledef->animation.aspect_h ? (float)tiledef->animation.aspect_h : 1);
979 frame_count = size.Y / (frame_height ? frame_height : size.Y ? size.Y : 1);
980 int frame_length_ms = 1000.0 * tiledef->animation.length / frame_count;
981 tile->animation_frame_count = frame_count;
982 tile->animation_frame_length_ms = frame_length_ms;
983 }
984
985 if (frame_count == 1) {
986 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
987 } else {
988 std::ostringstream os(std::ios::binary);
989 for (int i = 0; i < frame_count; i++) {
990 FrameSpec frame;
991
992 os.str("");
993 os << tiledef->name << "^[verticalframe:"
994 << frame_count << ":" << i;
995
996 frame.texture = tsrc->getTexture(os.str(), &frame.texture_id);
997 if (tile->normal_texture)
998 frame.normal_texture = tsrc->getNormalTexture(os.str());
999 tile->frames[i] = frame;
1000 }
1001 }
1002 }
1003 #endif
1004
1005
serialize(std::ostream & os,u16 protocol_version)1006 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version)
1007 {
1008 writeU8(os, 1); // version
1009 u16 count = 0;
1010 std::ostringstream os2(std::ios::binary);
1011 for (u32 i = 0; i < m_content_features.size(); i++) {
1012 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1013 || i == CONTENT_UNKNOWN)
1014 continue;
1015 ContentFeatures *f = &m_content_features[i];
1016 if (f->name == "")
1017 continue;
1018 writeU16(os2, i);
1019 // Wrap it in a string to allow different lengths without
1020 // strict version incompatibilities
1021 std::ostringstream wrapper_os(std::ios::binary);
1022 f->serialize(wrapper_os, protocol_version);
1023 os2<<serializeString(wrapper_os.str());
1024
1025 assert(count + 1 > count); // must not overflow
1026 count++;
1027 }
1028 writeU16(os, count);
1029 os << serializeLongString(os2.str());
1030 }
1031
1032
deSerialize(std::istream & is)1033 void CNodeDefManager::deSerialize(std::istream &is)
1034 {
1035 clear();
1036 int version = readU8(is);
1037 if (version != 1)
1038 throw SerializationError("unsupported NodeDefinitionManager version");
1039 u16 count = readU16(is);
1040 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1041 ContentFeatures f;
1042 for (u16 n = 0; n < count; n++) {
1043 u16 i = readU16(is2);
1044
1045 // Read it from the string wrapper
1046 std::string wrapper = deSerializeString(is2);
1047 std::istringstream wrapper_is(wrapper, std::ios::binary);
1048 f.deSerialize(wrapper_is);
1049
1050 // Check error conditions
1051 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1052 infostream << "NodeDefManager::deSerialize(): WARNING: "
1053 "not changing builtin node " << i << std::endl;
1054 continue;
1055 }
1056 if (f.name == "") {
1057 infostream << "NodeDefManager::deSerialize(): WARNING: "
1058 "received empty name" << std::endl;
1059 continue;
1060 }
1061
1062 // Ignore aliases
1063 u16 existing_id;
1064 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1065 infostream << "NodeDefManager::deSerialize(): WARNING: "
1066 "already defined with different ID: " << f.name << std::endl;
1067 continue;
1068 }
1069
1070 // All is ok, add node definition with the requested ID
1071 if (i >= m_content_features.size())
1072 m_content_features.resize((u32)(i) + 1);
1073 m_content_features[i] = f;
1074 addNameIdMapping(i, f.name);
1075 verbosestream << "deserialized " << f.name << std::endl;
1076 }
1077 }
1078
1079
addNameIdMapping(content_t i,std::string name)1080 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1081 {
1082 m_name_id_mapping.set(i, name);
1083 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1084 }
1085
1086
getResolver()1087 NodeResolver *CNodeDefManager::getResolver()
1088 {
1089 return &m_resolver;
1090 }
1091
1092
createNodeDefManager()1093 IWritableNodeDefManager *createNodeDefManager()
1094 {
1095 return new CNodeDefManager();
1096 }
1097
1098
1099 //// Serialization of old ContentFeatures formats
serializeOld(std::ostream & os,u16 protocol_version)1100 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version)
1101 {
1102 if (protocol_version == 13)
1103 {
1104 writeU8(os, 5); // version
1105 os<<serializeString(name);
1106 writeU16(os, groups.size());
1107 for (ItemGroupList::const_iterator
1108 i = groups.begin(); i != groups.end(); i++) {
1109 os<<serializeString(i->first);
1110 writeS16(os, i->second);
1111 }
1112 writeU8(os, drawtype);
1113 writeF1000(os, visual_scale);
1114 writeU8(os, 6);
1115 for (u32 i = 0; i < 6; i++)
1116 tiledef[i].serialize(os, protocol_version);
1117 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1118 writeU8(os, 2);
1119 for (u32 i = 0; i < 2; i++)
1120 tiledef_special[i].serialize(os, protocol_version);
1121 writeU8(os, alpha);
1122 writeU8(os, post_effect_color.getAlpha());
1123 writeU8(os, post_effect_color.getRed());
1124 writeU8(os, post_effect_color.getGreen());
1125 writeU8(os, post_effect_color.getBlue());
1126 writeU8(os, param_type);
1127 writeU8(os, param_type_2);
1128 writeU8(os, is_ground_content);
1129 writeU8(os, light_propagates);
1130 writeU8(os, sunlight_propagates);
1131 writeU8(os, walkable);
1132 writeU8(os, pointable);
1133 writeU8(os, diggable);
1134 writeU8(os, climbable);
1135 writeU8(os, buildable_to);
1136 os<<serializeString(""); // legacy: used to be metadata_name
1137 writeU8(os, liquid_type);
1138 os<<serializeString(liquid_alternative_flowing);
1139 os<<serializeString(liquid_alternative_source);
1140 writeU8(os, liquid_viscosity);
1141 writeU8(os, light_source);
1142 writeU32(os, damage_per_second);
1143 node_box.serialize(os, protocol_version);
1144 selection_box.serialize(os, protocol_version);
1145 writeU8(os, legacy_facedir_simple);
1146 writeU8(os, legacy_wallmounted);
1147 serializeSimpleSoundSpec(sound_footstep, os);
1148 serializeSimpleSoundSpec(sound_dig, os);
1149 serializeSimpleSoundSpec(sound_dug, os);
1150 }
1151 else if (protocol_version > 13 && protocol_version < 24) {
1152 writeU8(os, 6); // version
1153 os<<serializeString(name);
1154 writeU16(os, groups.size());
1155 for (ItemGroupList::const_iterator
1156 i = groups.begin(); i != groups.end(); i++) {
1157 os<<serializeString(i->first);
1158 writeS16(os, i->second);
1159 }
1160 writeU8(os, drawtype);
1161 writeF1000(os, visual_scale);
1162 writeU8(os, 6);
1163 for (u32 i = 0; i < 6; i++)
1164 tiledef[i].serialize(os, protocol_version);
1165 //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24
1166 writeU8(os, 2);
1167 for (u32 i = 0; i < 2; i++)
1168 tiledef_special[i].serialize(os, protocol_version);
1169 writeU8(os, alpha);
1170 writeU8(os, post_effect_color.getAlpha());
1171 writeU8(os, post_effect_color.getRed());
1172 writeU8(os, post_effect_color.getGreen());
1173 writeU8(os, post_effect_color.getBlue());
1174 writeU8(os, param_type);
1175 writeU8(os, param_type_2);
1176 writeU8(os, is_ground_content);
1177 writeU8(os, light_propagates);
1178 writeU8(os, sunlight_propagates);
1179 writeU8(os, walkable);
1180 writeU8(os, pointable);
1181 writeU8(os, diggable);
1182 writeU8(os, climbable);
1183 writeU8(os, buildable_to);
1184 os<<serializeString(""); // legacy: used to be metadata_name
1185 writeU8(os, liquid_type);
1186 os<<serializeString(liquid_alternative_flowing);
1187 os<<serializeString(liquid_alternative_source);
1188 writeU8(os, liquid_viscosity);
1189 writeU8(os, liquid_renewable);
1190 writeU8(os, light_source);
1191 writeU32(os, damage_per_second);
1192 node_box.serialize(os, protocol_version);
1193 selection_box.serialize(os, protocol_version);
1194 writeU8(os, legacy_facedir_simple);
1195 writeU8(os, legacy_wallmounted);
1196 serializeSimpleSoundSpec(sound_footstep, os);
1197 serializeSimpleSoundSpec(sound_dig, os);
1198 serializeSimpleSoundSpec(sound_dug, os);
1199 writeU8(os, rightclickable);
1200 writeU8(os, drowning);
1201 writeU8(os, leveled);
1202 writeU8(os, 0 /*liquid_range*/);
1203 } else
1204 throw SerializationError("ContentFeatures::serialize(): "
1205 "Unsupported version requested");
1206 }
1207
1208
deSerializeOld(std::istream & is,int version)1209 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1210 {
1211 if (version == 5) // In PROTOCOL_VERSION 13
1212 {
1213 name = deSerializeString(is);
1214 groups.clear();
1215 u32 groups_size = readU16(is);
1216 for(u32 i=0; i<groups_size; i++){
1217 std::string name = deSerializeString(is);
1218 int value = readS16(is);
1219 groups[name] = value;
1220 }
1221 drawtype = (enum NodeDrawType)readU8(is);
1222 visual_scale = readF1000(is);
1223 if (readU8(is) != 6)
1224 throw SerializationError("unsupported tile count");
1225 for (u32 i = 0; i < 6; i++)
1226 tiledef[i].deSerialize(is);
1227 if (readU8(is) != CF_SPECIAL_COUNT)
1228 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1229 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1230 tiledef_special[i].deSerialize(is);
1231 alpha = readU8(is);
1232 post_effect_color.setAlpha(readU8(is));
1233 post_effect_color.setRed(readU8(is));
1234 post_effect_color.setGreen(readU8(is));
1235 post_effect_color.setBlue(readU8(is));
1236 param_type = (enum ContentParamType)readU8(is);
1237 param_type_2 = (enum ContentParamType2)readU8(is);
1238 is_ground_content = readU8(is);
1239 light_propagates = readU8(is);
1240 sunlight_propagates = readU8(is);
1241 walkable = readU8(is);
1242 pointable = readU8(is);
1243 diggable = readU8(is);
1244 climbable = readU8(is);
1245 buildable_to = readU8(is);
1246 deSerializeString(is); // legacy: used to be metadata_name
1247 liquid_type = (enum LiquidType)readU8(is);
1248 liquid_alternative_flowing = deSerializeString(is);
1249 liquid_alternative_source = deSerializeString(is);
1250 liquid_viscosity = readU8(is);
1251 light_source = readU8(is);
1252 damage_per_second = readU32(is);
1253 node_box.deSerialize(is);
1254 selection_box.deSerialize(is);
1255 legacy_facedir_simple = readU8(is);
1256 legacy_wallmounted = readU8(is);
1257 deSerializeSimpleSoundSpec(sound_footstep, is);
1258 deSerializeSimpleSoundSpec(sound_dig, is);
1259 deSerializeSimpleSoundSpec(sound_dug, is);
1260 } else if (version == 6) {
1261 name = deSerializeString(is);
1262 groups.clear();
1263 u32 groups_size = readU16(is);
1264 for (u32 i = 0; i < groups_size; i++) {
1265 std::string name = deSerializeString(is);
1266 int value = readS16(is);
1267 groups[name] = value;
1268 }
1269 drawtype = (enum NodeDrawType)readU8(is);
1270 visual_scale = readF1000(is);
1271 if (readU8(is) != 6)
1272 throw SerializationError("unsupported tile count");
1273 for (u32 i = 0; i < 6; i++)
1274 tiledef[i].deSerialize(is);
1275 // CF_SPECIAL_COUNT in version 6 = 2
1276 if (readU8(is) != 2)
1277 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1278 for (u32 i = 0; i < 2; i++)
1279 tiledef_special[i].deSerialize(is);
1280 alpha = readU8(is);
1281 post_effect_color.setAlpha(readU8(is));
1282 post_effect_color.setRed(readU8(is));
1283 post_effect_color.setGreen(readU8(is));
1284 post_effect_color.setBlue(readU8(is));
1285 param_type = (enum ContentParamType)readU8(is);
1286 param_type_2 = (enum ContentParamType2)readU8(is);
1287 is_ground_content = readU8(is);
1288 light_propagates = readU8(is);
1289 sunlight_propagates = readU8(is);
1290 walkable = readU8(is);
1291 pointable = readU8(is);
1292 diggable = readU8(is);
1293 climbable = readU8(is);
1294 buildable_to = readU8(is);
1295 deSerializeString(is); // legacy: used to be metadata_name
1296 liquid_type = (enum LiquidType)readU8(is);
1297 liquid_alternative_flowing = deSerializeString(is);
1298 liquid_alternative_source = deSerializeString(is);
1299 liquid_viscosity = readU8(is);
1300 liquid_renewable = readU8(is);
1301 light_source = readU8(is);
1302 damage_per_second = readU32(is);
1303 node_box.deSerialize(is);
1304 selection_box.deSerialize(is);
1305 legacy_facedir_simple = readU8(is);
1306 legacy_wallmounted = readU8(is);
1307 deSerializeSimpleSoundSpec(sound_footstep, is);
1308 deSerializeSimpleSoundSpec(sound_dig, is);
1309 deSerializeSimpleSoundSpec(sound_dug, is);
1310 rightclickable = readU8(is);
1311 drowning = readU8(is);
1312 leveled = readU8(is);
1313 /* liquid_range =*/ readU8(is);
1314 } else {
1315 throw SerializationError("unsupported ContentFeatures version");
1316 }
1317 }
1318
1319 /*
1320 NodeResolver
1321 */
1322
NodeResolver(INodeDefManager * ndef)1323 NodeResolver::NodeResolver(INodeDefManager *ndef)
1324 {
1325 m_ndef = ndef;
1326 m_is_node_registration_complete = false;
1327 }
1328
1329
~NodeResolver()1330 NodeResolver::~NodeResolver()
1331 {
1332 while (!m_pending_contents.empty()) {
1333 NodeResolveInfo *nri = m_pending_contents.front();
1334 m_pending_contents.pop_front();
1335 delete nri;
1336 }
1337 }
1338
1339
addNode(std::string n_wanted,std::string n_alt,content_t c_fallback,content_t * content)1340 int NodeResolver::addNode(std::string n_wanted, std::string n_alt,
1341 content_t c_fallback, content_t *content)
1342 {
1343 if (m_is_node_registration_complete) {
1344 if (m_ndef->getId(n_wanted, *content))
1345 return NR_STATUS_SUCCESS;
1346
1347 if (n_alt == "" || !m_ndef->getId(n_alt, *content)) {
1348 *content = c_fallback;
1349 return NR_STATUS_FAILURE;
1350 }
1351
1352 return NR_STATUS_SUCCESS;
1353 } else {
1354 NodeResolveInfo *nfi = new NodeResolveInfo;
1355 nfi->n_wanted = n_wanted;
1356 nfi->n_alt = n_alt;
1357 nfi->c_fallback = c_fallback;
1358 nfi->output = content;
1359
1360 m_pending_contents.push_back(nfi);
1361
1362 return NR_STATUS_PENDING;
1363 }
1364 }
1365
1366
addNodeList(const char * nodename,std::vector<content_t> * content_vec)1367 int NodeResolver::addNodeList(const char *nodename,
1368 std::vector<content_t> *content_vec)
1369 {
1370 if (m_is_node_registration_complete) {
1371 std::unordered_set<content_t> idset;
1372
1373 m_ndef->getIds(nodename, idset);
1374 for (auto it = idset.begin(); it != idset.end(); ++it)
1375 content_vec->push_back(*it);
1376
1377 return idset.size() ? NR_STATUS_SUCCESS : NR_STATUS_FAILURE;
1378 } else {
1379 m_pending_content_vecs.push_back(
1380 std::make_pair(std::string(nodename), content_vec));
1381 return NR_STATUS_PENDING;
1382 }
1383 }
1384
1385
cancelNode(content_t * content)1386 bool NodeResolver::cancelNode(content_t *content)
1387 {
1388 bool found = false;
1389
1390 std::list<NodeResolveInfo *>::iterator it = m_pending_contents.begin();
1391 while (it != m_pending_contents.end()) {
1392 NodeResolveInfo *nfi = *it;
1393 if (nfi->output == content) {
1394 it = m_pending_contents.erase(it);
1395 delete nfi;
1396 found = true;
1397 }
1398 }
1399
1400 return found;
1401 }
1402
1403
cancelNodeList(std::vector<content_t> * content_vec)1404 int NodeResolver::cancelNodeList(std::vector<content_t> *content_vec)
1405 {
1406 int num_canceled = 0;
1407
1408 std::list<std::pair<std::string, std::vector<content_t> *> >::iterator it;
1409 it = m_pending_content_vecs.begin();
1410 while (it != m_pending_content_vecs.end()) {
1411 if (it->second == content_vec) {
1412 it = m_pending_content_vecs.erase(it);
1413 num_canceled++;
1414 }
1415 }
1416
1417 return num_canceled;
1418 }
1419
1420
resolveNodes()1421 int NodeResolver::resolveNodes()
1422 {
1423 int num_failed = 0;
1424
1425 //// Resolve pending single node name -> content ID mappings
1426 while (!m_pending_contents.empty()) {
1427 NodeResolveInfo *nri = m_pending_contents.front();
1428 m_pending_contents.pop_front();
1429
1430 bool success = true;
1431 if (!m_ndef->getId(nri->n_wanted, *nri->output)) {
1432 success = (nri->n_alt != "") ?
1433 m_ndef->getId(nri->n_alt, *nri->output) : false;
1434 }
1435
1436 if (!success) {
1437 *nri->output = nri->c_fallback;
1438 num_failed++;
1439 errorstream << "NodeResolver::resolveNodes(): Failed to "
1440 "resolve '" << nri->n_wanted;
1441 if (nri->n_alt != "")
1442 errorstream << "' and '" << nri->n_alt;
1443 errorstream << "'" << std::endl;
1444 }
1445
1446 delete nri;
1447 }
1448
1449 //// Resolve pending node names and add to content_t vector
1450 while (!m_pending_content_vecs.empty()) {
1451 std::pair<std::string, std::vector<content_t> *> item =
1452 m_pending_content_vecs.front();
1453 m_pending_content_vecs.pop_front();
1454
1455 std::string &name = item.first;
1456 std::vector<content_t> *output = item.second;
1457
1458 std::unordered_set<content_t> idset;
1459
1460 m_ndef->getIds(name, idset);
1461 for (auto it = idset.begin(); it != idset.end(); ++it)
1462 output->push_back(*it);
1463
1464 if (idset.size() == 0) {
1465 num_failed++;
1466 errorstream << "NodeResolver::resolveNodes(): Failed to "
1467 "resolve '" << name << "'" << std::endl;
1468 }
1469 }
1470
1471 //// Mark node registration as complete so future resolve
1472 //// requests are satisfied immediately
1473 m_is_node_registration_complete = true;
1474
1475 return num_failed;
1476 }
1477