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 "itemdef.h"
23 #ifndef SERVER
24 #include "client/mesh.h"
25 #include "client/shader.h"
26 #include "client/client.h"
27 #include "client/renderingengine.h"
28 #include "client/tile.h"
29 #include <IMeshManipulator.h>
30 #endif
31 #include "log.h"
32 #include "settings.h"
33 #include "nameidmapping.h"
34 #include "util/numeric.h"
35 #include "util/serialize.h"
36 #include "exceptions.h"
37 #include "debug.h"
38 #include "gamedef.h"
39 #include "mapnode.h"
40 #include <fstream> // Used in applyTextureOverrides()
41 #include <algorithm>
42 #include <cmath>
43 
44 /*
45 	NodeBox
46 */
47 
reset()48 void NodeBox::reset()
49 {
50 	type = NODEBOX_REGULAR;
51 	// default is empty
52 	fixed.clear();
53 	// default is sign/ladder-like
54 	wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
55 	wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
56 	wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
57 	// no default for other parts
58 	connect_top.clear();
59 	connect_bottom.clear();
60 	connect_front.clear();
61 	connect_left.clear();
62 	connect_back.clear();
63 	connect_right.clear();
64 	disconnected_top.clear();
65 	disconnected_bottom.clear();
66 	disconnected_front.clear();
67 	disconnected_left.clear();
68 	disconnected_back.clear();
69 	disconnected_right.clear();
70 	disconnected.clear();
71 	disconnected_sides.clear();
72 }
73 
serialize(std::ostream & os,u16 protocol_version) const74 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
75 {
76 	// Protocol >= 36
77 	const u8 version = 6;
78 	writeU8(os, version);
79 
80 	switch (type) {
81 	case NODEBOX_LEVELED:
82 	case NODEBOX_FIXED:
83 		writeU8(os, type);
84 
85 		writeU16(os, fixed.size());
86 		for (const aabb3f &nodebox : fixed) {
87 			writeV3F32(os, nodebox.MinEdge);
88 			writeV3F32(os, nodebox.MaxEdge);
89 		}
90 		break;
91 	case NODEBOX_WALLMOUNTED:
92 		writeU8(os, type);
93 
94 		writeV3F32(os, wall_top.MinEdge);
95 		writeV3F32(os, wall_top.MaxEdge);
96 		writeV3F32(os, wall_bottom.MinEdge);
97 		writeV3F32(os, wall_bottom.MaxEdge);
98 		writeV3F32(os, wall_side.MinEdge);
99 		writeV3F32(os, wall_side.MaxEdge);
100 		break;
101 	case NODEBOX_CONNECTED:
102 		writeU8(os, type);
103 
104 #define WRITEBOX(box) \
105 		writeU16(os, (box).size()); \
106 		for (const aabb3f &i: (box)) { \
107 			writeV3F32(os, i.MinEdge); \
108 			writeV3F32(os, i.MaxEdge); \
109 		};
110 
111 		WRITEBOX(fixed);
112 		WRITEBOX(connect_top);
113 		WRITEBOX(connect_bottom);
114 		WRITEBOX(connect_front);
115 		WRITEBOX(connect_left);
116 		WRITEBOX(connect_back);
117 		WRITEBOX(connect_right);
118 		WRITEBOX(disconnected_top);
119 		WRITEBOX(disconnected_bottom);
120 		WRITEBOX(disconnected_front);
121 		WRITEBOX(disconnected_left);
122 		WRITEBOX(disconnected_back);
123 		WRITEBOX(disconnected_right);
124 		WRITEBOX(disconnected);
125 		WRITEBOX(disconnected_sides);
126 		break;
127 	default:
128 		writeU8(os, type);
129 		break;
130 	}
131 }
132 
deSerialize(std::istream & is)133 void NodeBox::deSerialize(std::istream &is)
134 {
135 	int version = readU8(is);
136 	if (version < 6)
137 		throw SerializationError("unsupported NodeBox version");
138 
139 	reset();
140 
141 	type = (enum NodeBoxType)readU8(is);
142 
143 	if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
144 	{
145 		u16 fixed_count = readU16(is);
146 		while(fixed_count--)
147 		{
148 			aabb3f box;
149 			box.MinEdge = readV3F32(is);
150 			box.MaxEdge = readV3F32(is);
151 			fixed.push_back(box);
152 		}
153 	}
154 	else if(type == NODEBOX_WALLMOUNTED)
155 	{
156 		wall_top.MinEdge = readV3F32(is);
157 		wall_top.MaxEdge = readV3F32(is);
158 		wall_bottom.MinEdge = readV3F32(is);
159 		wall_bottom.MaxEdge = readV3F32(is);
160 		wall_side.MinEdge = readV3F32(is);
161 		wall_side.MaxEdge = readV3F32(is);
162 	}
163 	else if (type == NODEBOX_CONNECTED)
164 	{
165 #define READBOXES(box) { \
166 		count = readU16(is); \
167 		(box).reserve(count); \
168 		while (count--) { \
169 			v3f min = readV3F32(is); \
170 			v3f max = readV3F32(is); \
171 			(box).emplace_back(min, max); }; }
172 
173 		u16 count;
174 
175 		READBOXES(fixed);
176 		READBOXES(connect_top);
177 		READBOXES(connect_bottom);
178 		READBOXES(connect_front);
179 		READBOXES(connect_left);
180 		READBOXES(connect_back);
181 		READBOXES(connect_right);
182 		READBOXES(disconnected_top);
183 		READBOXES(disconnected_bottom);
184 		READBOXES(disconnected_front);
185 		READBOXES(disconnected_left);
186 		READBOXES(disconnected_back);
187 		READBOXES(disconnected_right);
188 		READBOXES(disconnected);
189 		READBOXES(disconnected_sides);
190 	}
191 }
192 
193 /*
194 	TileDef
195 */
196 
197 #define TILE_FLAG_BACKFACE_CULLING	(1 << 0)
198 #define TILE_FLAG_TILEABLE_HORIZONTAL	(1 << 1)
199 #define TILE_FLAG_TILEABLE_VERTICAL	(1 << 2)
200 #define TILE_FLAG_HAS_COLOR	(1 << 3)
201 #define TILE_FLAG_HAS_SCALE	(1 << 4)
202 #define TILE_FLAG_HAS_ALIGN_STYLE	(1 << 5)
203 
serialize(std::ostream & os,u16 protocol_version) const204 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
205 {
206 	// protocol_version >= 36
207 	u8 version = 6;
208 	writeU8(os, version);
209 
210 	os << serializeString16(name);
211 	animation.serialize(os, version);
212 	bool has_scale = scale > 0;
213 	u16 flags = 0;
214 	if (backface_culling)
215 		flags |= TILE_FLAG_BACKFACE_CULLING;
216 	if (tileable_horizontal)
217 		flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
218 	if (tileable_vertical)
219 		flags |= TILE_FLAG_TILEABLE_VERTICAL;
220 	if (has_color)
221 		flags |= TILE_FLAG_HAS_COLOR;
222 	if (has_scale)
223 		flags |= TILE_FLAG_HAS_SCALE;
224 	if (align_style != ALIGN_STYLE_NODE)
225 		flags |= TILE_FLAG_HAS_ALIGN_STYLE;
226 	writeU16(os, flags);
227 	if (has_color) {
228 		writeU8(os, color.getRed());
229 		writeU8(os, color.getGreen());
230 		writeU8(os, color.getBlue());
231 	}
232 	if (has_scale)
233 		writeU8(os, scale);
234 	if (align_style != ALIGN_STYLE_NODE)
235 		writeU8(os, align_style);
236 }
237 
deSerialize(std::istream & is,u8 contentfeatures_version,NodeDrawType drawtype)238 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
239 	NodeDrawType drawtype)
240 {
241 	int version = readU8(is);
242 	if (version < 6)
243 		throw SerializationError("unsupported TileDef version");
244 	name = deSerializeString16(is);
245 	animation.deSerialize(is, version);
246 	u16 flags = readU16(is);
247 	backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
248 	tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
249 	tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
250 	has_color = flags & TILE_FLAG_HAS_COLOR;
251 	bool has_scale = flags & TILE_FLAG_HAS_SCALE;
252 	bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
253 	if (has_color) {
254 		color.setRed(readU8(is));
255 		color.setGreen(readU8(is));
256 		color.setBlue(readU8(is));
257 	}
258 	scale = has_scale ? readU8(is) : 0;
259 	if (has_align_style)
260 		align_style = static_cast<AlignStyle>(readU8(is));
261 	else
262 		align_style = ALIGN_STYLE_NODE;
263 }
264 
readSettings()265 void TextureSettings::readSettings()
266 {
267 	connected_glass                = g_settings->getBool("connected_glass");
268 	opaque_water                   = g_settings->getBool("opaque_water");
269 	bool smooth_lighting           = g_settings->getBool("smooth_lighting");
270 	enable_mesh_cache              = g_settings->getBool("enable_mesh_cache");
271 	enable_minimap                 = g_settings->getBool("enable_minimap");
272 	node_texture_size              = g_settings->getU16("texture_min_size");
273 	std::string leaves_style_str   = g_settings->get("leaves_style");
274 	std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
275 	std::string autoscale_mode_str = g_settings->get("autoscale_mode");
276 
277 	// Mesh cache is not supported in combination with smooth lighting
278 	if (smooth_lighting)
279 		enable_mesh_cache = false;
280 
281 	if (leaves_style_str == "fancy") {
282 		leaves_style = LEAVES_FANCY;
283 	} else if (leaves_style_str == "simple") {
284 		leaves_style = LEAVES_SIMPLE;
285 	} else {
286 		leaves_style = LEAVES_OPAQUE;
287 	}
288 
289 	if (world_aligned_mode_str == "enable")
290 		world_aligned_mode = WORLDALIGN_ENABLE;
291 	else if (world_aligned_mode_str == "force_solid")
292 		world_aligned_mode = WORLDALIGN_FORCE;
293 	else if (world_aligned_mode_str == "force_nodebox")
294 		world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
295 	else
296 		world_aligned_mode = WORLDALIGN_DISABLE;
297 
298 	if (autoscale_mode_str == "enable")
299 		autoscale_mode = AUTOSCALE_ENABLE;
300 	else if (autoscale_mode_str == "force")
301 		autoscale_mode = AUTOSCALE_FORCE;
302 	else
303 		autoscale_mode = AUTOSCALE_DISABLE;
304 }
305 
306 /*
307 	ContentFeatures
308 */
309 
ContentFeatures()310 ContentFeatures::ContentFeatures()
311 {
312 	reset();
313 }
314 
~ContentFeatures()315 ContentFeatures::~ContentFeatures()
316 {
317 #ifndef SERVER
318 	for (u16 j = 0; j < 6; j++) {
319 		delete tiles[j].layers[0].frames;
320 		delete tiles[j].layers[1].frames;
321 	}
322 	for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
323 		delete special_tiles[j].layers[0].frames;
324 #endif
325 }
326 
reset()327 void ContentFeatures::reset()
328 {
329 	/*
330 		Cached stuff
331 	*/
332 #ifndef SERVER
333 	solidness = 2;
334 	visual_solidness = 0;
335 	backface_culling = true;
336 
337 #endif
338 	has_on_construct = false;
339 	has_on_destruct = false;
340 	has_after_destruct = false;
341 	/*
342 		Actual data
343 
344 		NOTE: Most of this is always overridden by the default values given
345 		      in builtin.lua
346 	*/
347 	name = "";
348 	groups.clear();
349 	// Unknown nodes can be dug
350 	groups["dig_immediate"] = 2;
351 	drawtype = NDT_NORMAL;
352 	mesh = "";
353 #ifndef SERVER
354 	for (auto &i : mesh_ptr)
355 		i = NULL;
356 	minimap_color = video::SColor(0, 0, 0, 0);
357 #endif
358 	visual_scale = 1.0;
359 	for (auto &i : tiledef)
360 		i = TileDef();
361 	for (auto &j : tiledef_special)
362 		j = TileDef();
363 	alpha = ALPHAMODE_OPAQUE;
364 	post_effect_color = video::SColor(0, 0, 0, 0);
365 	param_type = CPT_NONE;
366 	param_type_2 = CPT2_NONE;
367 	is_ground_content = false;
368 	light_propagates = false;
369 	sunlight_propagates = false;
370 	walkable = true;
371 	pointable = true;
372 	diggable = true;
373 	climbable = false;
374 	buildable_to = false;
375 	floodable = false;
376 	rightclickable = true;
377 	leveled = 0;
378 	leveled_max = LEVELED_MAX;
379 	liquid_type = LIQUID_NONE;
380 	liquid_alternative_flowing = "";
381 	liquid_alternative_flowing_id = CONTENT_IGNORE;
382 	liquid_alternative_source = "";
383 	liquid_alternative_source_id = CONTENT_IGNORE;
384 	liquid_viscosity = 0;
385 	liquid_renewable = true;
386 	liquid_range = LIQUID_LEVEL_MAX+1;
387 	drowning = 0;
388 	light_source = 0;
389 	damage_per_second = 0;
390 	node_box = NodeBox();
391 	selection_box = NodeBox();
392 	collision_box = NodeBox();
393 	waving = 0;
394 	legacy_facedir_simple = false;
395 	legacy_wallmounted = false;
396 	sound_footstep = SimpleSoundSpec();
397 	sound_dig = SimpleSoundSpec("__group");
398 	sound_dug = SimpleSoundSpec();
399 	connects_to.clear();
400 	connects_to_ids.clear();
401 	connect_sides = 0;
402 	color = video::SColor(0xFFFFFFFF);
403 	palette_name = "";
404 	palette = NULL;
405 	node_dig_prediction = "air";
406 }
407 
setAlphaFromLegacy(u8 legacy_alpha)408 void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha)
409 {
410 	// No special handling for nodebox/mesh here as it doesn't make sense to
411 	// throw warnings when the server is too old to support the "correct" way
412 	switch (drawtype) {
413 	case NDT_NORMAL:
414 		alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_CLIP;
415 		break;
416 	case NDT_LIQUID:
417 	case NDT_FLOWINGLIQUID:
418 		alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_BLEND;
419 		break;
420 	default:
421 		alpha = legacy_alpha == 255 ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
422 		break;
423 	}
424 }
425 
getAlphaForLegacy() const426 u8 ContentFeatures::getAlphaForLegacy() const
427 {
428 	// This is so simple only because 255 and 0 mean wildly different things
429 	// depending on drawtype...
430 	return alpha == ALPHAMODE_OPAQUE ? 255 : 0;
431 }
432 
serialize(std::ostream & os,u16 protocol_version) const433 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
434 {
435 	const u8 version = CONTENTFEATURES_VERSION;
436 	writeU8(os, version);
437 
438 	// general
439 	os << serializeString16(name);
440 	writeU16(os, groups.size());
441 	for (const auto &group : groups) {
442 		os << serializeString16(group.first);
443 		writeS16(os, group.second);
444 	}
445 	writeU8(os, param_type);
446 	writeU8(os, param_type_2);
447 
448 	// visual
449 	writeU8(os, drawtype);
450 	os << serializeString16(mesh);
451 	writeF32(os, visual_scale);
452 	writeU8(os, 6);
453 	for (const TileDef &td : tiledef)
454 		td.serialize(os, protocol_version);
455 	for (const TileDef &td : tiledef_overlay)
456 		td.serialize(os, protocol_version);
457 	writeU8(os, CF_SPECIAL_COUNT);
458 	for (const TileDef &td : tiledef_special) {
459 		td.serialize(os, protocol_version);
460 	}
461 	writeU8(os, getAlphaForLegacy());
462 	writeU8(os, color.getRed());
463 	writeU8(os, color.getGreen());
464 	writeU8(os, color.getBlue());
465 	os << serializeString16(palette_name);
466 	writeU8(os, waving);
467 	writeU8(os, connect_sides);
468 	writeU16(os, connects_to_ids.size());
469 	for (u16 connects_to_id : connects_to_ids)
470 		writeU16(os, connects_to_id);
471 	writeARGB8(os, post_effect_color);
472 	writeU8(os, leveled);
473 
474 	// lighting
475 	writeU8(os, light_propagates);
476 	writeU8(os, sunlight_propagates);
477 	writeU8(os, light_source);
478 
479 	// map generation
480 	writeU8(os, is_ground_content);
481 
482 	// interaction
483 	writeU8(os, walkable);
484 	writeU8(os, pointable);
485 	writeU8(os, diggable);
486 	writeU8(os, climbable);
487 	writeU8(os, buildable_to);
488 	writeU8(os, rightclickable);
489 	writeU32(os, damage_per_second);
490 
491 	// liquid
492 	writeU8(os, liquid_type);
493 	os << serializeString16(liquid_alternative_flowing);
494 	os << serializeString16(liquid_alternative_source);
495 	writeU8(os, liquid_viscosity);
496 	writeU8(os, liquid_renewable);
497 	writeU8(os, liquid_range);
498 	writeU8(os, drowning);
499 	writeU8(os, floodable);
500 
501 	// node boxes
502 	node_box.serialize(os, protocol_version);
503 	selection_box.serialize(os, protocol_version);
504 	collision_box.serialize(os, protocol_version);
505 
506 	// sound
507 	sound_footstep.serialize(os, version);
508 	sound_dig.serialize(os, version);
509 	sound_dug.serialize(os, version);
510 
511 	// legacy
512 	writeU8(os, legacy_facedir_simple);
513 	writeU8(os, legacy_wallmounted);
514 
515 	os << serializeString16(node_dig_prediction);
516 	writeU8(os, leveled_max);
517 	writeU8(os, alpha);
518 }
519 
deSerialize(std::istream & is)520 void ContentFeatures::deSerialize(std::istream &is)
521 {
522 	// version detection
523 	const u8 version = readU8(is);
524 	if (version < CONTENTFEATURES_VERSION)
525 		throw SerializationError("unsupported ContentFeatures version");
526 
527 	// general
528 	name = deSerializeString16(is);
529 	groups.clear();
530 	u32 groups_size = readU16(is);
531 	for (u32 i = 0; i < groups_size; i++) {
532 		std::string name = deSerializeString16(is);
533 		int value = readS16(is);
534 		groups[name] = value;
535 	}
536 	param_type = (enum ContentParamType) readU8(is);
537 	param_type_2 = (enum ContentParamType2) readU8(is);
538 
539 	// visual
540 	drawtype = (enum NodeDrawType) readU8(is);
541 	mesh = deSerializeString16(is);
542 	visual_scale = readF32(is);
543 	if (readU8(is) != 6)
544 		throw SerializationError("unsupported tile count");
545 	for (TileDef &td : tiledef)
546 		td.deSerialize(is, version, drawtype);
547 	for (TileDef &td : tiledef_overlay)
548 		td.deSerialize(is, version, drawtype);
549 	if (readU8(is) != CF_SPECIAL_COUNT)
550 		throw SerializationError("unsupported CF_SPECIAL_COUNT");
551 	for (TileDef &td : tiledef_special)
552 		td.deSerialize(is, version, drawtype);
553 	setAlphaFromLegacy(readU8(is));
554 	color.setRed(readU8(is));
555 	color.setGreen(readU8(is));
556 	color.setBlue(readU8(is));
557 	palette_name = deSerializeString16(is);
558 	waving = readU8(is);
559 	connect_sides = readU8(is);
560 	u16 connects_to_size = readU16(is);
561 	connects_to_ids.clear();
562 	for (u16 i = 0; i < connects_to_size; i++)
563 		connects_to_ids.push_back(readU16(is));
564 	post_effect_color = readARGB8(is);
565 	leveled = readU8(is);
566 
567 	// lighting-related
568 	light_propagates = readU8(is);
569 	sunlight_propagates = readU8(is);
570 	light_source = readU8(is);
571 	light_source = MYMIN(light_source, LIGHT_MAX);
572 
573 	// map generation
574 	is_ground_content = readU8(is);
575 
576 	// interaction
577 	walkable = readU8(is);
578 	pointable = readU8(is);
579 	diggable = readU8(is);
580 	climbable = readU8(is);
581 	buildable_to = readU8(is);
582 	rightclickable = readU8(is);
583 	damage_per_second = readU32(is);
584 
585 	// liquid
586 	liquid_type = (enum LiquidType) readU8(is);
587 	liquid_alternative_flowing = deSerializeString16(is);
588 	liquid_alternative_source = deSerializeString16(is);
589 	liquid_viscosity = readU8(is);
590 	liquid_renewable = readU8(is);
591 	liquid_range = readU8(is);
592 	drowning = readU8(is);
593 	floodable = readU8(is);
594 
595 	// node boxes
596 	node_box.deSerialize(is);
597 	selection_box.deSerialize(is);
598 	collision_box.deSerialize(is);
599 
600 	// sounds
601 	sound_footstep.deSerialize(is, version);
602 	sound_dig.deSerialize(is, version);
603 	sound_dug.deSerialize(is, version);
604 
605 	// read legacy properties
606 	legacy_facedir_simple = readU8(is);
607 	legacy_wallmounted = readU8(is);
608 
609 	try {
610 		node_dig_prediction = deSerializeString16(is);
611 
612 		u8 tmp = readU8(is);
613 		if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
614 			throw SerializationError("");
615 		leveled_max = tmp;
616 
617 		tmp = readU8(is);
618 		if (is.eof())
619 			throw SerializationError("");
620 		alpha = static_cast<enum AlphaMode>(tmp);
621 	} catch(SerializationError &e) {};
622 }
623 
624 #ifndef SERVER
fillTileAttribs(ITextureSource * tsrc,TileLayer * layer,const TileSpec & tile,const TileDef & tiledef,video::SColor color,u8 material_type,u32 shader_id,bool backface_culling,const TextureSettings & tsettings)625 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
626 		const TileSpec &tile, const TileDef &tiledef, video::SColor color,
627 		u8 material_type, u32 shader_id, bool backface_culling,
628 		const TextureSettings &tsettings)
629 {
630 	layer->shader_id     = shader_id;
631 	layer->texture       = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
632 	layer->material_type = material_type;
633 
634 	bool has_scale = tiledef.scale > 0;
635 	bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
636 		(tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
637 	if (use_autoscale) {
638 		auto texture_size = layer->texture->getOriginalSize();
639 		float base_size = tsettings.node_texture_size;
640 		float size = std::fmin(texture_size.Width, texture_size.Height);
641 		layer->scale = std::fmax(base_size, size) / base_size;
642 	} else if (has_scale) {
643 		layer->scale = tiledef.scale;
644 	} else {
645 		layer->scale = 1;
646 	}
647 	if (!tile.world_aligned)
648 		layer->scale = 1;
649 
650 	layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
651 
652 	// Material flags
653 	layer->material_flags = 0;
654 	if (backface_culling)
655 		layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
656 	if (tiledef.animation.type != TAT_NONE)
657 		layer->material_flags |= MATERIAL_FLAG_ANIMATION;
658 	if (tiledef.tileable_horizontal)
659 		layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
660 	if (tiledef.tileable_vertical)
661 		layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
662 
663 	// Color
664 	layer->has_color = tiledef.has_color;
665 	if (tiledef.has_color)
666 		layer->color = tiledef.color;
667 	else
668 		layer->color = color;
669 
670 	// Animation parameters
671 	int frame_count = 1;
672 	if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
673 		int frame_length_ms;
674 		tiledef.animation.determineParams(layer->texture->getOriginalSize(),
675 				&frame_count, &frame_length_ms, NULL);
676 		layer->animation_frame_count = frame_count;
677 		layer->animation_frame_length_ms = frame_length_ms;
678 	}
679 
680 	if (frame_count == 1) {
681 		layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
682 	} else {
683 		std::ostringstream os(std::ios::binary);
684 		if (!layer->frames) {
685 			layer->frames = new std::vector<FrameSpec>();
686 		}
687 		layer->frames->resize(frame_count);
688 
689 		for (int i = 0; i < frame_count; i++) {
690 
691 			FrameSpec frame;
692 
693 			os.str("");
694 			os << tiledef.name;
695 			tiledef.animation.getTextureModifer(os,
696 					layer->texture->getOriginalSize(), i);
697 
698 			frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
699 			if (layer->normal_texture)
700 				frame.normal_texture = tsrc->getNormalTexture(os.str());
701 			frame.flags_texture = layer->flags_texture;
702 			(*layer->frames)[i] = frame;
703 		}
704 	}
705 }
706 
textureAlphaCheck(ITextureSource * tsrc,const TileDef * tiles,int length)707 bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
708 {
709 	video::IVideoDriver *driver = RenderingEngine::get_video_driver();
710 	static thread_local bool long_warning_printed = false;
711 	std::set<std::string> seen;
712 
713 	for (int i = 0; i < length; i++) {
714 		if (seen.find(tiles[i].name) != seen.end())
715 			continue;
716 		seen.insert(tiles[i].name);
717 
718 		// Load the texture and see if there's any transparent pixels
719 		video::ITexture *texture = tsrc->getTexture(tiles[i].name);
720 		video::IImage *image = driver->createImage(texture,
721 			core::position2d<s32>(0, 0), texture->getOriginalSize());
722 		if (!image)
723 			continue;
724 		core::dimension2d<u32> dim = image->getDimension();
725 		bool ok = true;
726 		for (u16 x = 0; x < dim.Width; x++) {
727 			for (u16 y = 0; y < dim.Height; y++) {
728 				if (image->getPixel(x, y).getAlpha() < 255) {
729 					ok = false;
730 					goto break_loop;
731 				}
732 			}
733 		}
734 
735 break_loop:
736 		image->drop();
737 		if (ok)
738 			continue;
739 		warningstream << "Texture \"" << tiles[i].name << "\" of "
740 			<< name << " has transparency, assuming "
741 			"use_texture_alpha = \"clip\"." << std::endl;
742 		if (!long_warning_printed) {
743 			warningstream << "  This warning can be a false-positive if "
744 				"unused pixels in the texture are transparent. However if "
745 				"it is meant to be transparent, you *MUST* update the "
746 				"nodedef and set use_texture_alpha = \"clip\"! This "
747 				"compatibility code will be removed in a few releases."
748 				<< std::endl;
749 			long_warning_printed = true;
750 		}
751 		return true;
752 	}
753 	return false;
754 }
755 
isWorldAligned(AlignStyle style,WorldAlignMode mode,NodeDrawType drawtype)756 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
757 {
758 	if (style == ALIGN_STYLE_WORLD)
759 		return true;
760 	if (mode == WORLDALIGN_DISABLE)
761 		return false;
762 	if (style == ALIGN_STYLE_USER_DEFINED)
763 		return true;
764 	if (drawtype == NDT_NORMAL)
765 		return mode >= WORLDALIGN_FORCE;
766 	if (drawtype == NDT_NODEBOX)
767 		return mode >= WORLDALIGN_FORCE_NODEBOX;
768 	return false;
769 }
770 
updateTextures(ITextureSource * tsrc,IShaderSource * shdsrc,scene::IMeshManipulator * meshmanip,Client * client,const TextureSettings & tsettings)771 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
772 	scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
773 {
774 	// minimap pixel color - the average color of a texture
775 	if (tsettings.enable_minimap && !tiledef[0].name.empty())
776 		minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
777 
778 	// Figure out the actual tiles to use
779 	TileDef tdef[6];
780 	for (u32 j = 0; j < 6; j++) {
781 		tdef[j] = tiledef[j];
782 		if (tdef[j].name.empty())
783 			tdef[j].name = "unknown_node.png";
784 	}
785 	// also the overlay tiles
786 	TileDef tdef_overlay[6];
787 	for (u32 j = 0; j < 6; j++)
788 		tdef_overlay[j] = tiledef_overlay[j];
789 	// also the special tiles
790 	TileDef tdef_spec[6];
791 	for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
792 		tdef_spec[j] = tiledef_special[j];
793 
794 	bool is_liquid = false;
795 
796 	if (alpha == ALPHAMODE_LEGACY_COMPAT) {
797 		// Before working with the alpha mode, resolve any legacy kludges
798 		alpha = textureAlphaCheck(tsrc, tdef, 6) ? ALPHAMODE_CLIP : ALPHAMODE_OPAQUE;
799 	}
800 
801 	MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
802 		TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
803 		TILE_MATERIAL_ALPHA);
804 
805 	switch (drawtype) {
806 	default:
807 	case NDT_NORMAL:
808 		solidness = 2;
809 		break;
810 	case NDT_AIRLIKE:
811 		solidness = 0;
812 		break;
813 	case NDT_LIQUID:
814 		if (tsettings.opaque_water)
815 			alpha = ALPHAMODE_OPAQUE;
816 		solidness = 1;
817 		is_liquid = true;
818 		break;
819 	case NDT_FLOWINGLIQUID:
820 		solidness = 0;
821 		if (tsettings.opaque_water)
822 			alpha = ALPHAMODE_OPAQUE;
823 		is_liquid = true;
824 		break;
825 	case NDT_GLASSLIKE:
826 		solidness = 0;
827 		visual_solidness = 1;
828 		break;
829 	case NDT_GLASSLIKE_FRAMED:
830 		solidness = 0;
831 		visual_solidness = 1;
832 		break;
833 	case NDT_GLASSLIKE_FRAMED_OPTIONAL:
834 		solidness = 0;
835 		visual_solidness = 1;
836 		drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
837 		break;
838 	case NDT_ALLFACES:
839 		solidness = 0;
840 		visual_solidness = 1;
841 		break;
842 	case NDT_ALLFACES_OPTIONAL:
843 		if (tsettings.leaves_style == LEAVES_FANCY) {
844 			drawtype = NDT_ALLFACES;
845 			solidness = 0;
846 			visual_solidness = 1;
847 		} else if (tsettings.leaves_style == LEAVES_SIMPLE) {
848 			for (u32 j = 0; j < 6; j++) {
849 				if (!tdef_spec[j].name.empty())
850 					tdef[j].name = tdef_spec[j].name;
851 			}
852 			drawtype = NDT_GLASSLIKE;
853 			solidness = 0;
854 			visual_solidness = 1;
855 		} else {
856 			drawtype = NDT_NORMAL;
857 			solidness = 2;
858 			for (TileDef &td : tdef)
859 				td.name += std::string("^[noalpha");
860 		}
861 		if (waving >= 1)
862 			material_type = TILE_MATERIAL_WAVING_LEAVES;
863 		break;
864 	case NDT_PLANTLIKE:
865 		solidness = 0;
866 		if (waving >= 1)
867 			material_type = TILE_MATERIAL_WAVING_PLANTS;
868 		break;
869 	case NDT_FIRELIKE:
870 		solidness = 0;
871 		break;
872 	case NDT_MESH:
873 	case NDT_NODEBOX:
874 		solidness = 0;
875 		if (waving == 1) {
876 			material_type = TILE_MATERIAL_WAVING_PLANTS;
877 		} else if (waving == 2) {
878 			material_type = TILE_MATERIAL_WAVING_LEAVES;
879 		} else if (waving == 3) {
880 			material_type = alpha == ALPHAMODE_OPAQUE ?
881 				TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
882 				TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
883 		}
884 		break;
885 	case NDT_TORCHLIKE:
886 	case NDT_SIGNLIKE:
887 	case NDT_FENCELIKE:
888 	case NDT_RAILLIKE:
889 		solidness = 0;
890 		break;
891 	case NDT_PLANTLIKE_ROOTED:
892 		solidness = 2;
893 		break;
894 	}
895 
896 	if (is_liquid) {
897 		if (waving == 3) {
898 			material_type = alpha == ALPHAMODE_OPAQUE ?
899 				TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
900 				TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
901 		} else {
902 			material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
903 				TILE_MATERIAL_LIQUID_TRANSPARENT;
904 		}
905 	}
906 
907 	u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
908 
909 	MaterialType overlay_material = material_type;
910 	if (overlay_material == TILE_MATERIAL_OPAQUE)
911 		overlay_material = TILE_MATERIAL_BASIC;
912 	else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
913 		overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
914 
915 	u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
916 
917 	// Tiles (fill in f->tiles[])
918 	for (u16 j = 0; j < 6; j++) {
919 		tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
920 				tsettings.world_aligned_mode, drawtype);
921 		fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
922 				color, material_type, tile_shader,
923 				tdef[j].backface_culling, tsettings);
924 		if (!tdef_overlay[j].name.empty())
925 			fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
926 					color, overlay_material, overlay_shader,
927 					tdef[j].backface_culling, tsettings);
928 	}
929 
930 	MaterialType special_material = material_type;
931 	if (drawtype == NDT_PLANTLIKE_ROOTED) {
932 		if (waving == 1)
933 			special_material = TILE_MATERIAL_WAVING_PLANTS;
934 		else if (waving == 2)
935 			special_material = TILE_MATERIAL_WAVING_LEAVES;
936 	}
937 	u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
938 
939 	// Special tiles (fill in f->special_tiles[])
940 	for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
941 		fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
942 				color, special_material, special_shader,
943 				tdef_spec[j].backface_culling, tsettings);
944 
945 	if (param_type_2 == CPT2_COLOR ||
946 			param_type_2 == CPT2_COLORED_FACEDIR ||
947 			param_type_2 == CPT2_COLORED_WALLMOUNTED)
948 		palette = tsrc->getPalette(palette_name);
949 
950 	if (drawtype == NDT_MESH && !mesh.empty()) {
951 		// Meshnode drawtype
952 		// Read the mesh and apply scale
953 		mesh_ptr[0] = client->getMesh(mesh);
954 		if (mesh_ptr[0]){
955 			v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
956 			scaleMesh(mesh_ptr[0], scale);
957 			recalculateBoundingBox(mesh_ptr[0]);
958 			meshmanip->recalculateNormals(mesh_ptr[0], true, false);
959 		}
960 	}
961 
962 	//Cache 6dfacedir and wallmounted rotated clones of meshes
963 	if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
964 			(param_type_2 == CPT2_FACEDIR
965 			|| param_type_2 == CPT2_COLORED_FACEDIR)) {
966 		for (u16 j = 1; j < 24; j++) {
967 			mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
968 			rotateMeshBy6dFacedir(mesh_ptr[j], j);
969 			recalculateBoundingBox(mesh_ptr[j]);
970 			meshmanip->recalculateNormals(mesh_ptr[j], true, false);
971 		}
972 	} else if (tsettings.enable_mesh_cache && mesh_ptr[0]
973 			&& (param_type_2 == CPT2_WALLMOUNTED ||
974 			param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
975 		static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
976 		for (u16 j = 1; j < 6; j++) {
977 			mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
978 			rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
979 			recalculateBoundingBox(mesh_ptr[j]);
980 			meshmanip->recalculateNormals(mesh_ptr[j], true, false);
981 		}
982 		rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
983 		recalculateBoundingBox(mesh_ptr[0]);
984 		meshmanip->recalculateNormals(mesh_ptr[0], true, false);
985 	}
986 }
987 #endif
988 
989 /*
990 	NodeDefManager
991 */
992 
993 
994 
995 
NodeDefManager()996 NodeDefManager::NodeDefManager()
997 {
998 	clear();
999 }
1000 
1001 
~NodeDefManager()1002 NodeDefManager::~NodeDefManager()
1003 {
1004 #ifndef SERVER
1005 	for (ContentFeatures &f : m_content_features) {
1006 		for (auto &j : f.mesh_ptr) {
1007 			if (j)
1008 				j->drop();
1009 		}
1010 	}
1011 #endif
1012 }
1013 
1014 
clear()1015 void NodeDefManager::clear()
1016 {
1017 	m_content_features.clear();
1018 	m_name_id_mapping.clear();
1019 	m_name_id_mapping_with_aliases.clear();
1020 	m_group_to_items.clear();
1021 	m_next_id = 0;
1022 	m_selection_box_union.reset(0,0,0);
1023 	m_selection_box_int_union.reset(0,0,0);
1024 
1025 	resetNodeResolveState();
1026 
1027 	u32 initial_length = 0;
1028 	initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1029 	initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1030 	initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1031 	m_content_features.resize(initial_length);
1032 
1033 	// Set CONTENT_UNKNOWN
1034 	{
1035 		ContentFeatures f;
1036 		f.name = "unknown";
1037 		// Insert directly into containers
1038 		content_t c = CONTENT_UNKNOWN;
1039 		m_content_features[c] = f;
1040 		addNameIdMapping(c, f.name);
1041 	}
1042 
1043 	// Set CONTENT_AIR
1044 	{
1045 		ContentFeatures f;
1046 		f.name                = "air";
1047 		f.drawtype            = NDT_AIRLIKE;
1048 		f.param_type          = CPT_LIGHT;
1049 		f.light_propagates    = true;
1050 		f.sunlight_propagates = true;
1051 		f.walkable            = false;
1052 		f.pointable           = false;
1053 		f.diggable            = false;
1054 		f.buildable_to        = true;
1055 		f.floodable           = true;
1056 		f.is_ground_content   = true;
1057 		// Insert directly into containers
1058 		content_t c = CONTENT_AIR;
1059 		m_content_features[c] = f;
1060 		addNameIdMapping(c, f.name);
1061 	}
1062 
1063 	// Set CONTENT_IGNORE
1064 	{
1065 		ContentFeatures f;
1066 		f.name                = "ignore";
1067 		f.drawtype            = NDT_AIRLIKE;
1068 		f.param_type          = CPT_NONE;
1069 		f.light_propagates    = false;
1070 		f.sunlight_propagates = false;
1071 		f.walkable            = false;
1072 		f.pointable           = false;
1073 		f.diggable            = false;
1074 		f.buildable_to        = true; // A way to remove accidental CONTENT_IGNOREs
1075 		f.is_ground_content   = true;
1076 		// Insert directly into containers
1077 		content_t c = CONTENT_IGNORE;
1078 		m_content_features[c] = f;
1079 		addNameIdMapping(c, f.name);
1080 	}
1081 }
1082 
1083 
getId(const std::string & name,content_t & result) const1084 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1085 {
1086 	std::unordered_map<std::string, content_t>::const_iterator
1087 		i = m_name_id_mapping_with_aliases.find(name);
1088 	if(i == m_name_id_mapping_with_aliases.end())
1089 		return false;
1090 	result = i->second;
1091 	return true;
1092 }
1093 
1094 
getId(const std::string & name) const1095 content_t NodeDefManager::getId(const std::string &name) const
1096 {
1097 	content_t id = CONTENT_IGNORE;
1098 	getId(name, id);
1099 	return id;
1100 }
1101 
1102 
getIds(const std::string & name,std::vector<content_t> & result) const1103 bool NodeDefManager::getIds(const std::string &name,
1104 		std::vector<content_t> &result) const
1105 {
1106 	//TimeTaker t("getIds", NULL, PRECISION_MICRO);
1107 	if (name.substr(0,6) != "group:") {
1108 		content_t id = CONTENT_IGNORE;
1109 		bool exists = getId(name, id);
1110 		if (exists)
1111 			result.push_back(id);
1112 		return exists;
1113 	}
1114 	std::string group = name.substr(6);
1115 
1116 	std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1117 		i = m_group_to_items.find(group);
1118 	if (i == m_group_to_items.end())
1119 		return true;
1120 
1121 	const std::vector<content_t> &items = i->second;
1122 	result.insert(result.end(), items.begin(), items.end());
1123 	//printf("getIds: %dus\n", t.stop());
1124 	return true;
1125 }
1126 
1127 
get(const std::string & name) const1128 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1129 {
1130 	content_t id = CONTENT_UNKNOWN;
1131 	getId(name, id);
1132 	return get(id);
1133 }
1134 
1135 
1136 // returns CONTENT_IGNORE if no free ID found
allocateId()1137 content_t NodeDefManager::allocateId()
1138 {
1139 	for (content_t id = m_next_id;
1140 			id >= m_next_id; // overflow?
1141 			++id) {
1142 		while (id >= m_content_features.size()) {
1143 			m_content_features.emplace_back();
1144 		}
1145 		const ContentFeatures &f = m_content_features[id];
1146 		if (f.name.empty()) {
1147 			m_next_id = id + 1;
1148 			return id;
1149 		}
1150 	}
1151 	// If we arrive here, an overflow occurred in id.
1152 	// That means no ID was found
1153 	return CONTENT_IGNORE;
1154 }
1155 
1156 
1157 /*!
1158  * Returns the smallest box that contains all boxes
1159  * in the vector. Box_union is expanded.
1160  * @param[in]      boxes     the vector containing the boxes
1161  * @param[in, out] box_union the union of the arguments
1162  */
boxVectorUnion(const std::vector<aabb3f> & boxes,aabb3f * box_union)1163 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1164 {
1165 	for (const aabb3f &box : boxes) {
1166 		box_union->addInternalBox(box);
1167 	}
1168 }
1169 
1170 
1171 /*!
1172  * Returns a box that contains the nodebox in every case.
1173  * The argument node_union is expanded.
1174  * @param[in]      nodebox  the nodebox to be measured
1175  * @param[in]      features  used to decide whether the nodebox
1176  * can be rotated
1177  * @param[in, out] box_union the union of the arguments
1178  */
getNodeBoxUnion(const NodeBox & nodebox,const ContentFeatures & features,aabb3f * box_union)1179 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1180 	aabb3f *box_union)
1181 {
1182 	switch(nodebox.type) {
1183 		case NODEBOX_FIXED:
1184 		case NODEBOX_LEVELED: {
1185 			// Raw union
1186 			aabb3f half_processed(0, 0, 0, 0, 0, 0);
1187 			boxVectorUnion(nodebox.fixed, &half_processed);
1188 			// Set leveled boxes to maximal
1189 			if (nodebox.type == NODEBOX_LEVELED) {
1190 				half_processed.MaxEdge.Y = +BS / 2;
1191 			}
1192 			if (features.param_type_2 == CPT2_FACEDIR ||
1193 					features.param_type_2 == CPT2_COLORED_FACEDIR) {
1194 				// Get maximal coordinate
1195 				f32 coords[] = {
1196 					fabsf(half_processed.MinEdge.X),
1197 					fabsf(half_processed.MinEdge.Y),
1198 					fabsf(half_processed.MinEdge.Z),
1199 					fabsf(half_processed.MaxEdge.X),
1200 					fabsf(half_processed.MaxEdge.Y),
1201 					fabsf(half_processed.MaxEdge.Z) };
1202 				f32 max = 0;
1203 				for (float coord : coords) {
1204 					if (max < coord) {
1205 						max = coord;
1206 					}
1207 				}
1208 				// Add the union of all possible rotated boxes
1209 				box_union->addInternalPoint(-max, -max, -max);
1210 				box_union->addInternalPoint(+max, +max, +max);
1211 			} else {
1212 				box_union->addInternalBox(half_processed);
1213 			}
1214 			break;
1215 		}
1216 		case NODEBOX_WALLMOUNTED: {
1217 			// Add fix boxes
1218 			box_union->addInternalBox(nodebox.wall_top);
1219 			box_union->addInternalBox(nodebox.wall_bottom);
1220 			// Find maximal coordinate in the X-Z plane
1221 			f32 coords[] = {
1222 				fabsf(nodebox.wall_side.MinEdge.X),
1223 				fabsf(nodebox.wall_side.MinEdge.Z),
1224 				fabsf(nodebox.wall_side.MaxEdge.X),
1225 				fabsf(nodebox.wall_side.MaxEdge.Z) };
1226 			f32 max = 0;
1227 			for (float coord : coords) {
1228 				if (max < coord) {
1229 					max = coord;
1230 				}
1231 			}
1232 			// Add the union of all possible rotated boxes
1233 			box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1234 			box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1235 			break;
1236 		}
1237 		case NODEBOX_CONNECTED: {
1238 			// Add all possible connected boxes
1239 			boxVectorUnion(nodebox.fixed,               box_union);
1240 			boxVectorUnion(nodebox.connect_top,         box_union);
1241 			boxVectorUnion(nodebox.connect_bottom,      box_union);
1242 			boxVectorUnion(nodebox.connect_front,       box_union);
1243 			boxVectorUnion(nodebox.connect_left,        box_union);
1244 			boxVectorUnion(nodebox.connect_back,        box_union);
1245 			boxVectorUnion(nodebox.connect_right,       box_union);
1246 			boxVectorUnion(nodebox.disconnected_top,    box_union);
1247 			boxVectorUnion(nodebox.disconnected_bottom, box_union);
1248 			boxVectorUnion(nodebox.disconnected_front,  box_union);
1249 			boxVectorUnion(nodebox.disconnected_left,   box_union);
1250 			boxVectorUnion(nodebox.disconnected_back,   box_union);
1251 			boxVectorUnion(nodebox.disconnected_right,  box_union);
1252 			boxVectorUnion(nodebox.disconnected,        box_union);
1253 			boxVectorUnion(nodebox.disconnected_sides,  box_union);
1254 			break;
1255 		}
1256 		default: {
1257 			// NODEBOX_REGULAR
1258 			box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1259 			box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1260 		}
1261 	}
1262 }
1263 
1264 
fixSelectionBoxIntUnion()1265 inline void NodeDefManager::fixSelectionBoxIntUnion()
1266 {
1267 	m_selection_box_int_union.MinEdge.X = floorf(
1268 		m_selection_box_union.MinEdge.X / BS + 0.5f);
1269 	m_selection_box_int_union.MinEdge.Y = floorf(
1270 		m_selection_box_union.MinEdge.Y / BS + 0.5f);
1271 	m_selection_box_int_union.MinEdge.Z = floorf(
1272 		m_selection_box_union.MinEdge.Z / BS + 0.5f);
1273 	m_selection_box_int_union.MaxEdge.X = ceilf(
1274 		m_selection_box_union.MaxEdge.X / BS - 0.5f);
1275 	m_selection_box_int_union.MaxEdge.Y = ceilf(
1276 		m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1277 	m_selection_box_int_union.MaxEdge.Z = ceilf(
1278 		m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1279 }
1280 
1281 
eraseIdFromGroups(content_t id)1282 void NodeDefManager::eraseIdFromGroups(content_t id)
1283 {
1284 	// For all groups in m_group_to_items...
1285 	for (auto iter_groups = m_group_to_items.begin();
1286 			iter_groups != m_group_to_items.end();) {
1287 		// Get the group items vector.
1288 		std::vector<content_t> &items = iter_groups->second;
1289 
1290 		// Remove any occurence of the id in the group items vector.
1291 		items.erase(std::remove(items.begin(), items.end(), id), items.end());
1292 
1293 		// If group is empty, erase its vector from the map.
1294 		if (items.empty())
1295 			iter_groups = m_group_to_items.erase(iter_groups);
1296 		else
1297 			++iter_groups;
1298 	}
1299 }
1300 
1301 
1302 // IWritableNodeDefManager
set(const std::string & name,const ContentFeatures & def)1303 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1304 {
1305 	// Pre-conditions
1306 	assert(name != "");
1307 	assert(name != "ignore");
1308 	assert(name == def.name);
1309 
1310 	content_t id = CONTENT_IGNORE;
1311 	if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1312 		// Get new id
1313 		id = allocateId();
1314 		if (id == CONTENT_IGNORE) {
1315 			warningstream << "NodeDefManager: Absolute "
1316 				"limit reached" << std::endl;
1317 			return CONTENT_IGNORE;
1318 		}
1319 		assert(id != CONTENT_IGNORE);
1320 		addNameIdMapping(id, name);
1321 	}
1322 
1323 	// If there is already ContentFeatures registered for this id, clear old groups
1324 	if (id < m_content_features.size())
1325 		eraseIdFromGroups(id);
1326 
1327 	m_content_features[id] = def;
1328 	verbosestream << "NodeDefManager: registering content id \"" << id
1329 		<< "\": name=\"" << def.name << "\""<<std::endl;
1330 
1331 	getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1332 	fixSelectionBoxIntUnion();
1333 
1334 	// Add this content to the list of all groups it belongs to
1335 	for (const auto &group : def.groups) {
1336 		const std::string &group_name = group.first;
1337 		m_group_to_items[group_name].push_back(id);
1338 	}
1339 
1340 	return id;
1341 }
1342 
1343 
allocateDummy(const std::string & name)1344 content_t NodeDefManager::allocateDummy(const std::string &name)
1345 {
1346 	assert(name != "");	// Pre-condition
1347 	ContentFeatures f;
1348 	f.name = name;
1349 	return set(name, f);
1350 }
1351 
1352 
removeNode(const std::string & name)1353 void NodeDefManager::removeNode(const std::string &name)
1354 {
1355 	// Pre-condition
1356 	assert(name != "");
1357 
1358 	// Erase name from name ID mapping
1359 	content_t id = CONTENT_IGNORE;
1360 	if (m_name_id_mapping.getId(name, id)) {
1361 		m_name_id_mapping.eraseName(name);
1362 		m_name_id_mapping_with_aliases.erase(name);
1363 	}
1364 
1365 	eraseIdFromGroups(id);
1366 }
1367 
1368 
updateAliases(IItemDefManager * idef)1369 void NodeDefManager::updateAliases(IItemDefManager *idef)
1370 {
1371 	std::set<std::string> all;
1372 	idef->getAll(all);
1373 	m_name_id_mapping_with_aliases.clear();
1374 	for (const std::string &name : all) {
1375 		const std::string &convert_to = idef->getAlias(name);
1376 		content_t id;
1377 		if (m_name_id_mapping.getId(convert_to, id)) {
1378 			m_name_id_mapping_with_aliases.insert(
1379 				std::make_pair(name, id));
1380 		}
1381 	}
1382 }
1383 
applyTextureOverrides(const std::vector<TextureOverride> & overrides)1384 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1385 {
1386 	infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1387 		"overrides to textures" << std::endl;
1388 
1389 	for (const TextureOverride& texture_override : overrides) {
1390 		content_t id;
1391 		if (!getId(texture_override.id, id))
1392 			continue; // Ignore unknown node
1393 
1394 		ContentFeatures &nodedef = m_content_features[id];
1395 
1396 		// Override tiles
1397 		if (texture_override.hasTarget(OverrideTarget::TOP))
1398 			nodedef.tiledef[0].name = texture_override.texture;
1399 
1400 		if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1401 			nodedef.tiledef[1].name = texture_override.texture;
1402 
1403 		if (texture_override.hasTarget(OverrideTarget::RIGHT))
1404 			nodedef.tiledef[2].name = texture_override.texture;
1405 
1406 		if (texture_override.hasTarget(OverrideTarget::LEFT))
1407 			nodedef.tiledef[3].name = texture_override.texture;
1408 
1409 		if (texture_override.hasTarget(OverrideTarget::BACK))
1410 			nodedef.tiledef[4].name = texture_override.texture;
1411 
1412 		if (texture_override.hasTarget(OverrideTarget::FRONT))
1413 			nodedef.tiledef[5].name = texture_override.texture;
1414 
1415 
1416 		// Override special tiles, if applicable
1417 		if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1418 			nodedef.tiledef_special[0].name = texture_override.texture;
1419 
1420 		if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1421 			nodedef.tiledef_special[1].name = texture_override.texture;
1422 
1423 		if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1424 			nodedef.tiledef_special[2].name = texture_override.texture;
1425 
1426 		if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1427 			nodedef.tiledef_special[3].name = texture_override.texture;
1428 
1429 		if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1430 			nodedef.tiledef_special[4].name = texture_override.texture;
1431 
1432 		if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1433 			nodedef.tiledef_special[5].name = texture_override.texture;
1434 	}
1435 }
1436 
updateTextures(IGameDef * gamedef,void (* progress_callback)(void * progress_args,u32 progress,u32 max_progress),void * progress_callback_args)1437 void NodeDefManager::updateTextures(IGameDef *gamedef,
1438 	void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1439 	void *progress_callback_args)
1440 {
1441 #ifndef SERVER
1442 	infostream << "NodeDefManager::updateTextures(): Updating "
1443 		"textures in node definitions" << std::endl;
1444 
1445 	Client *client = (Client *)gamedef;
1446 	ITextureSource *tsrc = client->tsrc();
1447 	IShaderSource *shdsrc = client->getShaderSource();
1448 	scene::IMeshManipulator *meshmanip =
1449 		RenderingEngine::get_scene_manager()->getMeshManipulator();
1450 	TextureSettings tsettings;
1451 	tsettings.readSettings();
1452 
1453 	u32 size = m_content_features.size();
1454 
1455 	for (u32 i = 0; i < size; i++) {
1456 		ContentFeatures *f = &(m_content_features[i]);
1457 		f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1458 		progress_callback(progress_callback_args, i, size);
1459 	}
1460 #endif
1461 }
1462 
serialize(std::ostream & os,u16 protocol_version) const1463 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1464 {
1465 	writeU8(os, 1); // version
1466 	u16 count = 0;
1467 	std::ostringstream os2(std::ios::binary);
1468 	for (u32 i = 0; i < m_content_features.size(); i++) {
1469 		if (i == CONTENT_IGNORE || i == CONTENT_AIR
1470 				|| i == CONTENT_UNKNOWN)
1471 			continue;
1472 		const ContentFeatures *f = &m_content_features[i];
1473 		if (f->name.empty())
1474 			continue;
1475 		writeU16(os2, i);
1476 		// Wrap it in a string to allow different lengths without
1477 		// strict version incompatibilities
1478 		std::ostringstream wrapper_os(std::ios::binary);
1479 		f->serialize(wrapper_os, protocol_version);
1480 		os2<<serializeString16(wrapper_os.str());
1481 
1482 		// must not overflow
1483 		u16 next = count + 1;
1484 		FATAL_ERROR_IF(next < count, "Overflow");
1485 		count++;
1486 	}
1487 	writeU16(os, count);
1488 	os << serializeString32(os2.str());
1489 }
1490 
1491 
deSerialize(std::istream & is)1492 void NodeDefManager::deSerialize(std::istream &is)
1493 {
1494 	clear();
1495 	int version = readU8(is);
1496 	if (version != 1)
1497 		throw SerializationError("unsupported NodeDefinitionManager version");
1498 	u16 count = readU16(is);
1499 	std::istringstream is2(deSerializeString32(is), std::ios::binary);
1500 	ContentFeatures f;
1501 	for (u16 n = 0; n < count; n++) {
1502 		u16 i = readU16(is2);
1503 
1504 		// Read it from the string wrapper
1505 		std::string wrapper = deSerializeString16(is2);
1506 		std::istringstream wrapper_is(wrapper, std::ios::binary);
1507 		f.deSerialize(wrapper_is);
1508 
1509 		// Check error conditions
1510 		if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1511 			warningstream << "NodeDefManager::deSerialize(): "
1512 				"not changing builtin node " << i << std::endl;
1513 			continue;
1514 		}
1515 		if (f.name.empty()) {
1516 			warningstream << "NodeDefManager::deSerialize(): "
1517 				"received empty name" << std::endl;
1518 			continue;
1519 		}
1520 
1521 		// Ignore aliases
1522 		u16 existing_id;
1523 		if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1524 			warningstream << "NodeDefManager::deSerialize(): "
1525 				"already defined with different ID: " << f.name << std::endl;
1526 			continue;
1527 		}
1528 
1529 		// All is ok, add node definition with the requested ID
1530 		if (i >= m_content_features.size())
1531 			m_content_features.resize((u32)(i) + 1);
1532 		m_content_features[i] = f;
1533 		addNameIdMapping(i, f.name);
1534 		TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1535 
1536 		getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1537 		fixSelectionBoxIntUnion();
1538 	}
1539 
1540 	// Since liquid_alternative_flowing_id and liquid_alternative_source_id
1541 	// are not sent, resolve them client-side too.
1542 	resolveCrossrefs();
1543 }
1544 
1545 
addNameIdMapping(content_t i,std::string name)1546 void NodeDefManager::addNameIdMapping(content_t i, std::string name)
1547 {
1548 	m_name_id_mapping.set(i, name);
1549 	m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1550 }
1551 
1552 
createNodeDefManager()1553 NodeDefManager *createNodeDefManager()
1554 {
1555 	return new NodeDefManager();
1556 }
1557 
1558 
pendNodeResolve(NodeResolver * nr) const1559 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1560 {
1561 	nr->m_ndef = this;
1562 	if (m_node_registration_complete)
1563 		nr->nodeResolveInternal();
1564 	else
1565 		m_pending_resolve_callbacks.push_back(nr);
1566 }
1567 
1568 
cancelNodeResolveCallback(NodeResolver * nr) const1569 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1570 {
1571 	size_t len = m_pending_resolve_callbacks.size();
1572 	for (size_t i = 0; i != len; i++) {
1573 		if (nr != m_pending_resolve_callbacks[i])
1574 			continue;
1575 
1576 		len--;
1577 		m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1578 		m_pending_resolve_callbacks.resize(len);
1579 		return true;
1580 	}
1581 
1582 	return false;
1583 }
1584 
1585 
runNodeResolveCallbacks()1586 void NodeDefManager::runNodeResolveCallbacks()
1587 {
1588 	for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1589 		NodeResolver *nr = m_pending_resolve_callbacks[i];
1590 		nr->nodeResolveInternal();
1591 	}
1592 
1593 	m_pending_resolve_callbacks.clear();
1594 }
1595 
1596 
resetNodeResolveState()1597 void NodeDefManager::resetNodeResolveState()
1598 {
1599 	m_node_registration_complete = false;
1600 	m_pending_resolve_callbacks.clear();
1601 }
1602 
removeDupes(std::vector<content_t> & list)1603 static void removeDupes(std::vector<content_t> &list)
1604 {
1605 	std::sort(list.begin(), list.end());
1606 	auto new_end = std::unique(list.begin(), list.end());
1607 	list.erase(new_end, list.end());
1608 }
1609 
resolveCrossrefs()1610 void NodeDefManager::resolveCrossrefs()
1611 {
1612 	for (ContentFeatures &f : m_content_features) {
1613 		if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
1614 			f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1615 			f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1616 			continue;
1617 		}
1618 		if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1619 			continue;
1620 
1621 		for (const std::string &name : f.connects_to) {
1622 			getIds(name, f.connects_to_ids);
1623 		}
1624 		removeDupes(f.connects_to_ids);
1625 	}
1626 }
1627 
nodeboxConnects(MapNode from,MapNode to,u8 connect_face) const1628 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1629 	u8 connect_face) const
1630 {
1631 	const ContentFeatures &f1 = get(from);
1632 
1633 	if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1634 		return false;
1635 
1636 	// lookup target in connected set
1637 	if (!CONTAINS(f1.connects_to_ids, to.param0))
1638 		return false;
1639 
1640 	const ContentFeatures &f2 = get(to);
1641 
1642 	if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1643 		// ignores actually looking if back connection exists
1644 		return CONTAINS(f2.connects_to_ids, from.param0);
1645 
1646 	// does to node declare usable faces?
1647 	if (f2.connect_sides > 0) {
1648 		if ((f2.param_type_2 == CPT2_FACEDIR ||
1649 				f2.param_type_2 == CPT2_COLORED_FACEDIR)
1650 				&& (connect_face >= 4)) {
1651 			static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1652 				0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1653 				0, // 4 - back
1654 				8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1655 				0, // 8 - right
1656 				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1657 				0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1658 				0, // 16 - front
1659 				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1660 				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1661 				0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1662 				};
1663 			return (f2.connect_sides
1664 				& rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1665 		}
1666 		return (f2.connect_sides & connect_face);
1667 	}
1668 	// the target is just a regular node, so connect no matter back connection
1669 	return true;
1670 }
1671 
1672 ////
1673 //// NodeResolver
1674 ////
1675 
NodeResolver()1676 NodeResolver::NodeResolver()
1677 {
1678 	m_nodenames.reserve(16);
1679 	m_nnlistsizes.reserve(4);
1680 }
1681 
1682 
~NodeResolver()1683 NodeResolver::~NodeResolver()
1684 {
1685 	if (!m_resolve_done && m_ndef)
1686 		m_ndef->cancelNodeResolveCallback(this);
1687 }
1688 
1689 
cloneTo(NodeResolver * res) const1690 void NodeResolver::cloneTo(NodeResolver *res) const
1691 {
1692 	FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1693 		" after resolving has completed");
1694 	/* We don't actually do anything significant. Since the node resolving has
1695 	 * already completed, the class that called us will already have the
1696 	 * resolved IDs in its data structures (which it copies on its own) */
1697 	res->m_ndef = m_ndef;
1698 	res->m_resolve_done = true;
1699 }
1700 
1701 
nodeResolveInternal()1702 void NodeResolver::nodeResolveInternal()
1703 {
1704 	m_nodenames_idx   = 0;
1705 	m_nnlistsizes_idx = 0;
1706 
1707 	resolveNodeNames();
1708 	m_resolve_done = true;
1709 
1710 	m_nodenames.clear();
1711 	m_nnlistsizes.clear();
1712 }
1713 
1714 
getIdFromNrBacklog(content_t * result_out,const std::string & node_alt,content_t c_fallback,bool error_on_fallback)1715 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1716 	const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1717 {
1718 	if (m_nodenames_idx == m_nodenames.size()) {
1719 		*result_out = c_fallback;
1720 		errorstream << "NodeResolver: no more nodes in list" << std::endl;
1721 		return false;
1722 	}
1723 
1724 	content_t c;
1725 	std::string name = m_nodenames[m_nodenames_idx++];
1726 
1727 	bool success = m_ndef->getId(name, c);
1728 	if (!success && !node_alt.empty()) {
1729 		name = node_alt;
1730 		success = m_ndef->getId(name, c);
1731 	}
1732 
1733 	if (!success) {
1734 		if (error_on_fallback)
1735 			errorstream << "NodeResolver: failed to resolve node name '" << name
1736 				<< "'." << std::endl;
1737 		c = c_fallback;
1738 	}
1739 
1740 	*result_out = c;
1741 	return success;
1742 }
1743 
1744 
getIdsFromNrBacklog(std::vector<content_t> * result_out,bool all_required,content_t c_fallback)1745 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1746 	bool all_required, content_t c_fallback)
1747 {
1748 	bool success = true;
1749 
1750 	if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1751 		errorstream << "NodeResolver: no more node lists" << std::endl;
1752 		return false;
1753 	}
1754 
1755 	size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1756 
1757 	while (length--) {
1758 		if (m_nodenames_idx == m_nodenames.size()) {
1759 			errorstream << "NodeResolver: no more nodes in list" << std::endl;
1760 			return false;
1761 		}
1762 
1763 		content_t c;
1764 		std::string &name = m_nodenames[m_nodenames_idx++];
1765 
1766 		if (name.substr(0,6) != "group:") {
1767 			if (m_ndef->getId(name, c)) {
1768 				result_out->push_back(c);
1769 			} else if (all_required) {
1770 				errorstream << "NodeResolver: failed to resolve node name '"
1771 					<< name << "'." << std::endl;
1772 				result_out->push_back(c_fallback);
1773 				success = false;
1774 			}
1775 		} else {
1776 			m_ndef->getIds(name, *result_out);
1777 		}
1778 	}
1779 
1780 	return success;
1781 }
1782