Browse Source

Merge pull request #48924 from akien-mga/3.x-cherrypicks

Cherry-picks for the 3.x branch (future 3.4) - 4th batch
Rémi Verschelde 4 years ago
parent
commit
7cd5967e99

+ 65 - 34
core/io/xml_parser.cpp

@@ -132,7 +132,7 @@ void XMLParser::_parse_closing_xml_element() {
 	++P;
 	const char *pBeginClose = P;
 
-	while (*P != '>') {
+	while (*P && *P != '>') {
 		++P;
 	}
 
@@ -140,7 +140,10 @@ void XMLParser::_parse_closing_xml_element() {
 #ifdef DEBUG_XML
 	print_line("XML CLOSE: " + node_name);
 #endif
-	++P;
+
+	if (*P) {
+		++P;
+	}
 }
 
 void XMLParser::_ignore_definition() {
@@ -148,11 +151,14 @@ void XMLParser::_ignore_definition() {
 
 	char *F = P;
 	// move until end marked with '>' reached
-	while (*P != '>') {
+	while (*P && *P != '>') {
 		++P;
 	}
 	node_name.parse_utf8(F, P - F);
-	++P;
+
+	if (*P) {
+		++P;
+	}
 }
 
 bool XMLParser::_parse_cdata() {
@@ -170,6 +176,7 @@ bool XMLParser::_parse_cdata() {
 	}
 
 	if (!*P) {
+		node_name = "";
 		return true;
 	}
 
@@ -188,10 +195,9 @@ bool XMLParser::_parse_cdata() {
 	}
 
 	if (cDataEnd) {
-		node_name = String::utf8(cDataBegin, (int)(cDataEnd - cDataBegin));
-	} else {
-		node_name = "";
+		cDataEnd = P;
 	}
+	node_name = String::utf8(cDataBegin, (int)(cDataEnd - cDataBegin));
 #ifdef DEBUG_XML
 	print_line("XML CDATA: " + node_name);
 #endif
@@ -203,24 +209,45 @@ void XMLParser::_parse_comment() {
 	node_type = NODE_COMMENT;
 	P += 1;
 
-	char *pCommentBegin = P;
+	char *pEndOfInput = data + length;
+	char *pCommentBegin;
+	char *pCommentEnd;
 
-	int count = 1;
-
-	// move until end of comment reached
-	while (count) {
-		if (*P == '>') {
-			--count;
-		} else if (*P == '<') {
-			++count;
+	if (P + 1 < pEndOfInput && P[0] == '-' && P[1] == '-') {
+		// Comment, use '-->' as end.
+		pCommentBegin = P + 2;
+		for (pCommentEnd = pCommentBegin; pCommentEnd + 2 < pEndOfInput; pCommentEnd++) {
+			if (pCommentEnd[0] == '-' && pCommentEnd[1] == '-' && pCommentEnd[2] == '>') {
+				break;
+			}
+		}
+		if (pCommentEnd + 2 < pEndOfInput) {
+			P = pCommentEnd + 3;
+		} else {
+			P = pCommentEnd = pEndOfInput;
+		}
+	} else {
+		// Like document type definition, match angle brackets.
+		pCommentBegin = P;
+
+		int count = 1;
+		while (*P && count) {
+			if (*P == '>') {
+				--count;
+			} else if (*P == '<') {
+				++count;
+			}
+			++P;
 		}
 
-		++P;
+		if (count) {
+			pCommentEnd = P;
+		} else {
+			pCommentEnd = P - 1;
+		}
 	}
 
-	P -= 3;
-	node_name = String::utf8(pCommentBegin + 2, (int)(P - pCommentBegin - 2));
-	P += 3;
+	node_name = String::utf8(pCommentBegin, (int)(pCommentEnd - pCommentBegin));
 #ifdef DEBUG_XML
 	print_line("XML COMMENT: " + node_name);
 #endif
@@ -235,14 +262,14 @@ void XMLParser::_parse_opening_xml_element() {
 	const char *startName = P;
 
 	// find end of element
-	while (*P != '>' && !_is_white_space(*P)) {
+	while (*P && *P != '>' && !_is_white_space(*P)) {
 		++P;
 	}
 
 	const char *endName = P;
 
 	// find attributes
-	while (*P != '>') {
+	while (*P && *P != '>') {
 		if (_is_white_space(*P)) {
 			++P;
 		} else {
@@ -252,10 +279,14 @@ void XMLParser::_parse_opening_xml_element() {
 				// read the attribute names
 				const char *attributeNameBegin = P;
 
-				while (!_is_white_space(*P) && *P != '=') {
+				while (*P && !_is_white_space(*P) && *P != '=') {
 					++P;
 				}
 
+				if (!*P) {
+					break;
+				}
+
 				const char *attributeNameEnd = P;
 				++P;
 
@@ -266,7 +297,7 @@ void XMLParser::_parse_opening_xml_element() {
 				}
 
 				if (!*P) { // malformatted xml file
-					return;
+					break;
 				}
 
 				const char attributeQuoteChar = *P;
@@ -278,12 +309,10 @@ void XMLParser::_parse_opening_xml_element() {
 					++P;
 				}
 
-				if (!*P) { // malformatted xml file
-					return;
-				}
-
 				const char *attributeValueEnd = P;
-				++P;
+				if (*P) {
+					++P;
+				}
 
 				Attribute attr;
 				attr.name = String::utf8(attributeNameBegin,
@@ -315,7 +344,9 @@ void XMLParser::_parse_opening_xml_element() {
 	print_line("XML OPEN: " + node_name);
 #endif
 
-	++P;
+	if (*P) {
+		++P;
+	}
 }
 
 void XMLParser::_parse_current_node() {
@@ -327,10 +358,6 @@ void XMLParser::_parse_current_node() {
 		++P;
 	}
 
-	if (!*P) {
-		return;
-	}
-
 	if (P - start > 0) {
 		// we found some text, store it
 		if (_set_text(start, P)) {
@@ -338,6 +365,10 @@ void XMLParser::_parse_current_node() {
 		}
 	}
 
+	if (!*P) {
+		return;
+	}
+
 	++P;
 
 	// based on current token, parse and report next element

+ 5 - 23
core/math/math_funcs.h

@@ -255,8 +255,8 @@ public:
 	static _ALWAYS_INLINE_ double db2linear(double p_db) { return Math::exp(p_db * 0.11512925464970228420089957273422); }
 	static _ALWAYS_INLINE_ float db2linear(float p_db) { return Math::exp(p_db * 0.11512925464970228420089957273422); }
 
-	static _ALWAYS_INLINE_ double round(double p_val) { return (p_val >= 0) ? Math::floor(p_val + 0.5) : -Math::floor(-p_val + 0.5); }
-	static _ALWAYS_INLINE_ float round(float p_val) { return (p_val >= 0) ? Math::floor(p_val + 0.5) : -Math::floor(-p_val + 0.5); }
+	static _ALWAYS_INLINE_ double round(double p_val) { return ::round(p_val); }
+	static _ALWAYS_INLINE_ float round(float p_val) { return ::roundf(p_val); }
 
 	static _ALWAYS_INLINE_ int64_t wrapi(int64_t value, int64_t min, int64_t max) {
 		int64_t range = max - min;
@@ -376,28 +376,10 @@ public:
 		return u.d;
 	}
 
-	//this function should be as fast as possible and rounding mode should not matter
+	// This function should be as fast as possible and rounding mode should not matter.
 	static _ALWAYS_INLINE_ int fast_ftoi(float a) {
-		static int b;
-
-#if (defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0603) || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP // windows 8 phone?
-		b = (int)((a > 0.0) ? (a + 0.5) : (a - 0.5));
-
-#elif defined(_MSC_VER) && _MSC_VER < 1800
-		__asm fld a __asm fistp b
-		/*#elif defined( __GNUC__ ) && ( defined( __i386__ ) || defined( __x86_64__ ) )
-		// use AT&T inline assembly style, document that
-		// we use memory as output (=m) and input (m)
-		__asm__ __volatile__ (
-		"flds %1        \n\t"
-		"fistpl %0      \n\t"
-		: "=m" (b)
-		: "m" (a));*/
-
-#else
-		b = lrintf(a); //assuming everything but msvc 2012 or earlier has lrint
-#endif
-		return b;
+		// Assuming every supported compiler has `lrint()`.
+		return lrintf(a);
 	}
 
 	static _ALWAYS_INLINE_ uint32_t halfbits_to_floatbits(uint16_t h) {

+ 5 - 5
editor/connections_dialog.cpp

@@ -65,8 +65,8 @@ public:
 	bool _set(const StringName &p_name, const Variant &p_value) {
 		String name = p_name;
 
-		if (name.begins_with("bind/")) {
-			int which = name.get_slice("/", 1).to_int() - 1;
+		if (name.begins_with("bind/argument_")) {
+			int which = name.get_slice("_", 1).to_int() - 1;
 			ERR_FAIL_INDEX_V(which, params.size(), false);
 			params.write[which] = p_value;
 		} else {
@@ -79,8 +79,8 @@ public:
 	bool _get(const StringName &p_name, Variant &r_ret) const {
 		String name = p_name;
 
-		if (name.begins_with("bind/")) {
-			int which = name.get_slice("/", 1).to_int() - 1;
+		if (name.begins_with("bind/argument_")) {
+			int which = name.get_slice("_", 1).to_int() - 1;
 			ERR_FAIL_INDEX_V(which, params.size(), false);
 			r_ret = params[which];
 		} else {
@@ -92,7 +92,7 @@ public:
 
 	void _get_property_list(List<PropertyInfo> *p_list) const {
 		for (int i = 0; i < params.size(); i++) {
-			p_list->push_back(PropertyInfo(params[i].get_type(), "bind/" + itos(i + 1)));
+			p_list->push_back(PropertyInfo(params[i].get_type(), "bind/argument_" + itos(i + 1)));
 		}
 	}
 

+ 35 - 20
editor/editor_profiler.cpp

@@ -613,23 +613,35 @@ Vector<Vector<String>> EditorProfiler::get_data_as_csv() const {
 		return res;
 	}
 
-	// signatures
-	Vector<String> signatures;
-	const Vector<EditorProfiler::Metric::Category> &categories = frame_metrics[0].categories;
-
-	for (int j = 0; j < categories.size(); j++) {
-		const EditorProfiler::Metric::Category &c = categories[j];
-		signatures.push_back(c.signature);
-
-		for (int k = 0; k < c.items.size(); k++) {
-			signatures.push_back(c.items[k].signature);
+	// Different metrics may contain different number of categories.
+	Set<StringName> possible_signatures;
+	for (int i = 0; i < frame_metrics.size(); i++) {
+		const Metric &m = frame_metrics[i];
+		if (!m.valid) {
+			continue;
+		}
+		for (Map<StringName, Metric::Category *>::Element *E = m.category_ptrs.front(); E; E = E->next()) {
+			possible_signatures.insert(E->key());
+		}
+		for (Map<StringName, Metric::Category::Item *>::Element *E = m.item_ptrs.front(); E; E = E->next()) {
+			possible_signatures.insert(E->key());
 		}
 	}
+
+	// Generate CSV header and cache indices.
+	Map<StringName, int> sig_map;
+	Vector<String> signatures;
+	signatures.resize(possible_signatures.size());
+	int sig_index = 0;
+	for (const Set<StringName>::Element *E = possible_signatures.front(); E; E = E->next()) {
+		signatures.write[sig_index] = E->get();
+		sig_map[E->get()] = sig_index;
+		sig_index++;
+	}
 	res.push_back(signatures);
 
 	// values
 	Vector<String> values;
-	values.resize(signatures.size());
 
 	int index = last_metric;
 
@@ -640,20 +652,23 @@ Vector<Vector<String>> EditorProfiler::get_data_as_csv() const {
 			index = 0;
 		}
 
-		if (!frame_metrics[index].valid) {
+		const Metric &m = frame_metrics[index];
+
+		if (!m.valid) {
 			continue;
 		}
-		int it = 0;
-		const Vector<EditorProfiler::Metric::Category> &frame_cat = frame_metrics[index].categories;
 
-		for (int j = 0; j < frame_cat.size(); j++) {
-			const EditorProfiler::Metric::Category &c = frame_cat[j];
-			values.write[it++] = String::num_real(c.total_time);
+		// Don't keep old values since there may be empty cells.
+		values.clear();
+		values.resize(possible_signatures.size());
 
-			for (int k = 0; k < c.items.size(); k++) {
-				values.write[it++] = String::num_real(c.items[k].total);
-			}
+		for (Map<StringName, Metric::Category *>::Element *E = m.category_ptrs.front(); E; E = E->next()) {
+			values.write[sig_map[E->key()]] = String::num_real(E->value()->total_time);
 		}
+		for (Map<StringName, Metric::Category::Item *>::Element *E = m.item_ptrs.front(); E; E = E->next()) {
+			values.write[sig_map[E->key()]] = String::num_real(E->value()->total);
+		}
+
 		res.push_back(values);
 	}
 

+ 4 - 2
editor/find_in_files.cpp

@@ -707,8 +707,10 @@ void FindInFilesPanel::draw_result_text(Object *item_obj, Rect2 rect) {
 	match_rect.position.y += 1 * EDSCALE;
 	match_rect.size.y -= 2 * EDSCALE;
 
-	_results_display->draw_rect(match_rect, Color(0, 0, 0, 0.5));
-	// Text is drawn by Tree already
+	// Use the inverted accent color to help match rectangles stand out even on the currently selected line.
+	_results_display->draw_rect(match_rect, get_color("accent_color", "Editor").inverted() * Color(1, 1, 1, 0.5));
+
+	// Text is drawn by Tree already.
 }
 
 void FindInFilesPanel::_on_item_edited() {

+ 12 - 4
editor/import/editor_scene_importer_gltf.cpp

@@ -1451,8 +1451,11 @@ Error EditorSceneImporterGLTF::_parse_images(GLTFState &state, const String &p_b
 			}
 		}
 
-		ERR_FAIL_COND_V_MSG(img.is_null(), ERR_FILE_CORRUPT,
-				vformat("glTF: Couldn't load image index '%d' with its given mimetype: %s.", i, mimetype));
+		if (img.is_null()) {
+			ERR_PRINT(vformat("glTF: Couldn't load image index '%d' with its given mimetype: %s.", i, mimetype));
+			state.images.push_back(Ref<Texture>());
+			continue;
+		}
 
 		Ref<ImageTexture> t;
 		t.instance();
@@ -2356,6 +2359,9 @@ bool EditorSceneImporterGLTF::_skins_are_same(const Ref<Skin> &skin_a, const Ref
 		if (skin_a->get_bind_bone(i) != skin_b->get_bind_bone(i)) {
 			return false;
 		}
+		if (skin_a->get_bind_name(i) != skin_b->get_bind_name(i)) {
+			return false;
+		}
 
 		Transform a_xform = skin_a->get_bind_pose(i);
 		Transform b_xform = skin_b->get_bind_pose(i);
@@ -3135,13 +3141,15 @@ void EditorSceneImporterGLTF::_process_mesh_instances(GLTFState &state, Spatial
 			const GLTFSkinIndex skin_i = node->skin;
 
 			Map<GLTFNodeIndex, Node *>::Element *mi_element = state.scene_nodes.find(node_i);
+			ERR_CONTINUE_MSG(mi_element == nullptr, vformat("Unable to find node %d", node_i));
+
 			MeshInstance *mi = Object::cast_to<MeshInstance>(mi_element->get());
-			ERR_FAIL_COND(mi == nullptr);
+			ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to MeshInstance", node_i, mi_element->get()->get_class_name()));
 
 			const GLTFSkeletonIndex skel_i = state.skins[node->skin].skeleton;
 			const GLTFSkeleton &gltf_skeleton = state.skeletons[skel_i];
 			Skeleton *skeleton = gltf_skeleton.godot_skeleton;
-			ERR_FAIL_COND(skeleton == nullptr);
+			ERR_CONTINUE_MSG(skeleton == nullptr, vformat("Unable to find Skeleton for node %d skin %d", node_i, skin_i));
 
 			mi->get_parent()->remove_child(mi);
 			skeleton->add_child(mi);

+ 2 - 2
editor/plugins/spatial_editor_plugin.cpp

@@ -80,10 +80,10 @@ void ViewportRotationControl::_notification(int p_what) {
 		axis_menu_options.clear();
 		axis_menu_options.push_back(SpatialEditorViewport::VIEW_RIGHT);
 		axis_menu_options.push_back(SpatialEditorViewport::VIEW_TOP);
-		axis_menu_options.push_back(SpatialEditorViewport::VIEW_FRONT);
+		axis_menu_options.push_back(SpatialEditorViewport::VIEW_REAR);
 		axis_menu_options.push_back(SpatialEditorViewport::VIEW_LEFT);
 		axis_menu_options.push_back(SpatialEditorViewport::VIEW_BOTTOM);
-		axis_menu_options.push_back(SpatialEditorViewport::VIEW_REAR);
+		axis_menu_options.push_back(SpatialEditorViewport::VIEW_FRONT);
 
 		axis_colors.clear();
 		axis_colors.push_back(get_color("axis_x_color", "Editor"));

+ 12 - 0
misc/dist/osx/editor.entitlements

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.device.audio-input</key>
+ <true/>
+ <key>com.apple.security.device.camera</key>
+ <true/>
+ <key>com.apple.security.cs.disable-library-validation</key>
+ <true/>
+</dict>
+</plist>

+ 18 - 0
misc/dist/osx/editor_mono.entitlements

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.cs.allow-dyld-environment-variables</key>
+ <true/>
+ <key>com.apple.security.cs.allow-jit</key>
+ <true/>
+ <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
+ <true/>
+ <key>com.apple.security.cs.disable-library-validation</key>
+ <true/>
+ <key>com.apple.security.device.audio-input</key>
+ <true/>
+ <key>com.apple.security.device.camera</key>
+ <true/>
+</dict>
+</plist>

+ 25 - 11
platform/osx/os_osx.mm

@@ -2238,31 +2238,45 @@ MainLoop *OS_OSX::get_main_loop() const {
 }
 
 String OS_OSX::get_config_path() const {
+	// The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on macOS as well.
 	if (has_environment("XDG_CONFIG_HOME")) {
-		return get_environment("XDG_CONFIG_HOME");
-	} else if (has_environment("HOME")) {
+		if (get_environment("XDG_CONFIG_HOME").is_abs_path()) {
+			return get_environment("XDG_CONFIG_HOME");
+		} else {
+			WARN_PRINT_ONCE("`XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `$HOME/Library/Application Support` or `.` per the XDG Base Directory specification.");
+		}
+	}
+	if (has_environment("HOME")) {
 		return get_environment("HOME").plus_file("Library/Application Support");
-	} else {
-		return ".";
 	}
+	return ".";
 }
 
 String OS_OSX::get_data_path() const {
+	// The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on macOS as well.
 	if (has_environment("XDG_DATA_HOME")) {
-		return get_environment("XDG_DATA_HOME");
-	} else {
-		return get_config_path();
+		if (get_environment("XDG_DATA_HOME").is_abs_path()) {
+			return get_environment("XDG_DATA_HOME");
+		} else {
+			WARN_PRINT_ONCE("`XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `get_config_path()` per the XDG Base Directory specification.");
+		}
 	}
+	return get_config_path();
 }
 
 String OS_OSX::get_cache_path() const {
+	// The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on macOS as well.
 	if (has_environment("XDG_CACHE_HOME")) {
-		return get_environment("XDG_CACHE_HOME");
-	} else if (has_environment("HOME")) {
+		if (get_environment("XDG_CACHE_HOME").is_abs_path()) {
+			return get_environment("XDG_CACHE_HOME");
+		} else {
+			WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `$HOME/Libary/Caches` or `get_config_path()` per the XDG Base Directory specification.");
+		}
+	}
+	if (has_environment("HOME")) {
 		return get_environment("HOME").plus_file("Library/Caches");
-	} else {
-		return get_config_path();
 	}
+	return get_config_path();
 }
 
 String OS_OSX::get_bundle_resource_dir() const {

+ 26 - 12
platform/windows/os_windows.cpp

@@ -3316,31 +3316,45 @@ MainLoop *OS_Windows::get_main_loop() const {
 }
 
 String OS_Windows::get_config_path() const {
-	if (has_environment("XDG_CONFIG_HOME")) { // unlikely, but after all why not?
-		return get_environment("XDG_CONFIG_HOME");
-	} else if (has_environment("APPDATA")) {
+	// The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
+	if (has_environment("XDG_CONFIG_HOME")) {
+		if (get_environment("XDG_CONFIG_HOME").is_abs_path()) {
+			return get_environment("XDG_CONFIG_HOME");
+		} else {
+			WARN_PRINT_ONCE("`XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `%APPDATA%` or `.` per the XDG Base Directory specification.");
+		}
+	}
+	if (has_environment("APPDATA")) {
 		return get_environment("APPDATA");
-	} else {
-		return ".";
 	}
+	return ".";
 }
 
 String OS_Windows::get_data_path() const {
+	// The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
 	if (has_environment("XDG_DATA_HOME")) {
-		return get_environment("XDG_DATA_HOME");
-	} else {
-		return get_config_path();
+		if (get_environment("XDG_DATA_HOME").is_abs_path()) {
+			return get_environment("XDG_DATA_HOME");
+		} else {
+			WARN_PRINT_ONCE("`XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `get_config_path()` per the XDG Base Directory specification.");
+		}
 	}
+	return get_config_path();
 }
 
 String OS_Windows::get_cache_path() const {
+	// The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
 	if (has_environment("XDG_CACHE_HOME")) {
-		return get_environment("XDG_CACHE_HOME");
-	} else if (has_environment("TEMP")) {
+		if (get_environment("XDG_CACHE_HOME").is_abs_path()) {
+			return get_environment("XDG_CACHE_HOME");
+		} else {
+			WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `%TEMP%` or `get_config_path()` per the XDG Base Directory specification.");
+		}
+	}
+	if (has_environment("TEMP")) {
 		return get_environment("TEMP");
-	} else {
-		return get_config_path();
 	}
+	return get_config_path();
 }
 
 // Get properly capitalized engine name for system paths

+ 24 - 12
platform/x11/os_x11.cpp

@@ -3208,32 +3208,44 @@ bool OS_X11::_check_internal_feature_support(const String &p_feature) {
 
 String OS_X11::get_config_path() const {
 	if (has_environment("XDG_CONFIG_HOME")) {
-		return get_environment("XDG_CONFIG_HOME");
-	} else if (has_environment("HOME")) {
+		if (get_environment("XDG_CONFIG_HOME").is_abs_path()) {
+			return get_environment("XDG_CONFIG_HOME");
+		} else {
+			WARN_PRINT_ONCE("`XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `$HOME/.config` or `.` per the XDG Base Directory specification.");
+		}
+	}
+	if (has_environment("HOME")) {
 		return get_environment("HOME").plus_file(".config");
-	} else {
-		return ".";
 	}
+	return ".";
 }
 
 String OS_X11::get_data_path() const {
 	if (has_environment("XDG_DATA_HOME")) {
-		return get_environment("XDG_DATA_HOME");
-	} else if (has_environment("HOME")) {
+		if (get_environment("XDG_DATA_HOME").is_abs_path()) {
+			return get_environment("XDG_DATA_HOME");
+		} else {
+			WARN_PRINT_ONCE("`XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `$HOME/.local/share` or `get_config_path()` per the XDG Base Directory specification.");
+		}
+	}
+	if (has_environment("HOME")) {
 		return get_environment("HOME").plus_file(".local/share");
-	} else {
-		return get_config_path();
 	}
+	return get_config_path();
 }
 
 String OS_X11::get_cache_path() const {
 	if (has_environment("XDG_CACHE_HOME")) {
-		return get_environment("XDG_CACHE_HOME");
-	} else if (has_environment("HOME")) {
+		if (get_environment("XDG_CACHE_HOME").is_abs_path()) {
+			return get_environment("XDG_CACHE_HOME");
+		} else {
+			WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `$HOME/.cache` or `get_config_path()` per the XDG Base Directory specification.");
+		}
+	}
+	if (has_environment("HOME")) {
 		return get_environment("HOME").plus_file(".cache");
-	} else {
-		return get_config_path();
 	}
+	return get_config_path();
 }
 
 String OS_X11::get_system_dir(SystemDir p_dir) const {

+ 3 - 3
thirdparty/misc/triangulator.cpp

@@ -1166,7 +1166,7 @@ int TriangulatorPartition::MonotonePartition(List<TriangulatorPoly> *inpolys, Li
 				newedge.p1 = v->p;
 				newedge.p2 = v->p;
 				edgeIter = edgeTree.lower_bound(newedge);
-				if(edgeIter == edgeTree.front()) {
+				if(edgeIter == nullptr || edgeIter == edgeTree.front()) {
 					error = true;
 					break;
 				}
@@ -1202,7 +1202,7 @@ int TriangulatorPartition::MonotonePartition(List<TriangulatorPoly> *inpolys, Li
 				newedge.p1 = v->p;
 				newedge.p2 = v->p;
 				edgeIter = edgeTree.lower_bound(newedge);
-				if(edgeIter == edgeTree.front()) {
+				if(edgeIter == nullptr || edgeIter == edgeTree.front()) {
 					error = true;
 					break;
 				}
@@ -1241,7 +1241,7 @@ int TriangulatorPartition::MonotonePartition(List<TriangulatorPoly> *inpolys, Li
 					newedge.p1 = v->p;
 					newedge.p2 = v->p;
 					edgeIter = edgeTree.lower_bound(newedge);
-					if(edgeIter == edgeTree.front()) {
+					if(edgeIter == nullptr || edgeIter == edgeTree.front()) {
 						error = true;
 						break;
 					}