Explorar o código

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

Rémi Verschelde %!s(int64=4) %!d(string=hai) anos
pai
achega
48fe8da245
Modificáronse 41 ficheiros con 328 adicións e 117 borrados
  1. 7 0
      core/image.cpp
  2. 3 1
      core/image.h
  3. 5 5
      doc/classes/CylinderMesh.xml
  4. 1 0
      doc/classes/File.xml
  5. 1 1
      doc/classes/Label.xml
  6. 4 2
      doc/classes/ProjectSettings.xml
  7. 20 22
      editor/animation_track_editor.cpp
  8. 5 9
      editor/editor_node.cpp
  9. 1 0
      editor/editor_node.h
  10. 1 1
      editor/plugins/canvas_item_editor_plugin.cpp
  11. 1 1
      editor/plugins/spatial_editor_plugin.cpp
  12. 46 8
      editor/plugins/sprite_frames_editor_plugin.cpp
  13. 1 0
      editor/plugins/sprite_frames_editor_plugin.h
  14. 7 3
      editor/project_manager.cpp
  15. 0 4
      editor/spatial_editor_gizmos.cpp
  16. 3 1
      modules/gdscript/language_server/gdscript_workspace.cpp
  17. 4 0
      modules/pvr/image_compress_pvrtc.cpp
  18. 3 2
      modules/visual_script/visual_script_property_selector.cpp
  19. 3 0
      modules/websocket/doc_classes/WebSocketServer.xml
  20. 6 3
      modules/websocket/websocket_client.cpp
  21. 13 0
      modules/websocket/websocket_server.cpp
  22. 4 0
      modules/websocket/websocket_server.h
  23. 1 0
      modules/websocket/wsl_client.cpp
  24. 3 3
      modules/websocket/wsl_server.cpp
  25. 1 3
      modules/websocket/wsl_server.h
  26. 1 1
      platform/android/export/export.cpp
  27. 2 3
      platform/android/java/app/build.gradle
  28. 2 2
      platform/android/java/app/config.gradle
  29. 1 2
      platform/android/java/build.gradle
  30. 3 3
      platform/android/java/gradle/wrapper/gradle-wrapper.properties
  31. 7 2
      platform/javascript/export/export.cpp
  32. 10 0
      platform/javascript/js/engine/config.js
  33. 3 0
      platform/javascript/js/libs/library_godot_os.js
  34. 6 0
      platform/osx/os_osx.mm
  35. 3 3
      platform/uwp/export/export.cpp
  36. 11 1
      platform/windows/export/export.cpp
  37. 1 1
      scene/gui/graph_node.cpp
  38. 116 24
      scene/gui/texture_progress.cpp
  39. 2 1
      scene/gui/texture_progress.h
  40. 13 2
      scene/resources/packed_scene.cpp
  41. 3 3
      scene/resources/primitive_meshes.cpp

+ 7 - 0
core/image.cpp

@@ -1740,6 +1740,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
 	ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + ".");
 	ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + ".");
 	ERR_FAIL_COND_MSG(write_lock.ptr(), "Cannot create image when it is locked.");
+	ERR_FAIL_INDEX_MSG(p_format, FORMAT_MAX, "Image format out of range, please see Image's Format enum.");
 
 	int mm = 0;
 	int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
@@ -1760,6 +1761,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
 	ERR_FAIL_COND_MSG(p_height <= 0, "Image height must be greater than 0.");
 	ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + ".");
 	ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + ".");
+	ERR_FAIL_INDEX_MSG(p_format, FORMAT_MAX, "Image format out of range, please see Image's Format enum.");
 
 	int mm;
 	int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
@@ -2113,6 +2115,8 @@ Error Image::decompress() {
 }
 
 Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_lossy_quality) {
+	ERR_FAIL_INDEX_V_MSG(p_mode, COMPRESS_MAX, ERR_INVALID_PARAMETER, "Invalid compress mode.");
+	ERR_FAIL_INDEX_V_MSG(p_source, COMPRESS_SOURCE_MAX, ERR_INVALID_PARAMETER, "Invalid compress source.");
 	switch (p_mode) {
 		case COMPRESS_S3TC: {
 			ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE);
@@ -2138,6 +2142,9 @@ Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_loss
 			ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE);
 			_image_compress_bptc_func(this, p_lossy_quality, p_source);
 		} break;
+		case COMPRESS_MAX: {
+			ERR_FAIL_V(ERR_INVALID_PARAMETER);
+		} break;
 	}
 
 	return OK;

+ 3 - 1
core/image.h

@@ -124,6 +124,7 @@ public:
 		COMPRESS_SOURCE_SRGB,
 		COMPRESS_SOURCE_NORMAL,
 		COMPRESS_SOURCE_LAYERED,
+		COMPRESS_SOURCE_MAX,
 	};
 
 	//some functions provided by something else
@@ -304,7 +305,8 @@ public:
 		COMPRESS_PVRTC4,
 		COMPRESS_ETC,
 		COMPRESS_ETC2,
-		COMPRESS_BPTC
+		COMPRESS_BPTC,
+		COMPRESS_MAX,
 	};
 
 	Error compress(CompressMode p_mode = COMPRESS_S3TC, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7);

+ 5 - 5
doc/classes/CylinderMesh.xml

@@ -4,7 +4,7 @@
 		Class representing a cylindrical [PrimitiveMesh].
 	</brief_description>
 	<description>
-		Class representing a cylindrical [PrimitiveMesh]. This class can be used to create cones by setting either the [member top_radius] or [member bottom_radius] properties to 0.0.
+		Class representing a cylindrical [PrimitiveMesh]. This class can be used to create cones by setting either the [member top_radius] or [member bottom_radius] properties to [code]0.0[/code].
 	</description>
 	<tutorials>
 	</tutorials>
@@ -12,19 +12,19 @@
 	</methods>
 	<members>
 		<member name="bottom_radius" type="float" setter="set_bottom_radius" getter="get_bottom_radius" default="1.0">
-			Bottom radius of the cylinder.
+			Bottom radius of the cylinder. If set to [code]0.0[/code], the bottom faces will not be generated, resulting in a conic shape.
 		</member>
 		<member name="height" type="float" setter="set_height" getter="get_height" default="2.0">
 			Full height of the cylinder.
 		</member>
 		<member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments" default="64">
-			Number of radial segments on the cylinder.
+			Number of radial segments on the cylinder. Higher values result in a more detailed cylinder/cone at the cost of performance.
 		</member>
 		<member name="rings" type="int" setter="set_rings" getter="get_rings" default="4">
-			Number of edge rings along the height of the cylinder.
+			Number of edge rings along the height of the cylinder. Changing [member rings] does not have any visual impact unless a shader or procedural mesh tool is used to alter the vertex data. Higher values result in more subdivisions, which can be used to create smoother-looking effects with shaders or procedural mesh tools (at the cost of performance). When not altering the vertex data using a shader or procedural mesh tool, [member rings] should be kept to its default value.
 		</member>
 		<member name="top_radius" type="float" setter="set_top_radius" getter="get_top_radius" default="1.0">
-			Top radius of the cylinder.
+			Top radius of the cylinder. If set to [code]0.0[/code], the top faces will not be generated, resulting in a conic shape.
 		</member>
 	</members>
 	<constants>

+ 1 - 0
doc/classes/File.xml

@@ -450,6 +450,7 @@
 			</argument>
 			<description>
 				Stores any Variant value in the file. If [code]full_objects[/code] is [code]true[/code], encoding objects is allowed (and can potentially include code).
+				[b]Note:[/b] Not all properties are included. Only properties that are configured with the [constant PROPERTY_USAGE_STORAGE] flag set will be serialized. You can add a new usage flag to a property by overriding the [method Object._get_property_list] method in your class. You can also check how property usage is configured by calling [method Object._get_property_list]. See [enum PropertyUsageFlags] for the possible usage flags.
 			</description>
 		</method>
 	</methods>

+ 1 - 1
doc/classes/Label.xml

@@ -48,7 +48,7 @@
 			If [code]true[/code], wraps the text inside the node's bounding rectangle. If you resize the node, it will change its height automatically to show all the text.
 		</member>
 		<member name="clip_text" type="bool" setter="set_clip_text" getter="is_clipping_text" default="false">
-			If [code]true[/code], the Label only shows the text that fits inside its bounding rectangle. It also lets you scale the node down freely.
+			If [code]true[/code], the Label only shows the text that fits inside its bounding rectangle and will clip text horizontally.
 		</member>
 		<member name="lines_skipped" type="int" setter="set_lines_skipped" getter="get_lines_skipped" default="0">
 			The node ignores the first [code]lines_skipped[/code] lines before it starts to display text.

+ 4 - 2
doc/classes/ProjectSettings.xml

@@ -245,10 +245,12 @@
 			Icon set in [code].ico[/code] format used on Windows to set the game's icon. This is done automatically on start by calling [method OS.set_native_icon].
 		</member>
 		<member name="application/run/disable_stderr" type="bool" setter="" getter="" default="false">
-			If [code]true[/code], disables printing to standard error in an exported build.
+			If [code]true[/code], disables printing to standard error. If [code]true[/code], this also hides error and warning messages printed by [method @GDScript.push_error] and [method @GDScript.push_warning]. See also [member application/run/disable_stdout].
+			Changes to this setting will only be applied upon restarting the application.
 		</member>
 		<member name="application/run/disable_stdout" type="bool" setter="" getter="" default="false">
-			If [code]true[/code], disables printing to standard output in an exported build.
+			If [code]true[/code], disables printing to standard output. This is equivalent to starting the editor or project with the [code]--quiet[/code] command line argument. See also [member application/run/disable_stderr].
+			Changes to this setting will only be applied upon restarting the application.
 		</member>
 		<member name="application/run/flush_stdout_on_print" type="bool" setter="" getter="" default="false">
 			If [code]true[/code], flushes the standard output stream every time a line is printed. This affects both terminal logging and file logging.

+ 20 - 22
editor/animation_track_editor.cpp

@@ -1175,31 +1175,29 @@ public:
 					p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
 				} break;
 				case Animation::TYPE_VALUE: {
-					if (!same_key_type) {
-						break;
-					}
+					if (same_key_type) {
+						Variant v = animation->track_get_key_value(first_track, first_key);
 
-					Variant v = animation->track_get_key_value(first_track, first_key);
-
-					if (hint.type != Variant::NIL) {
-						PropertyInfo pi = hint;
-						pi.name = "value";
-						p_list->push_back(pi);
-					} else {
-						PropertyHint hint = PROPERTY_HINT_NONE;
-						String hint_string;
-
-						if (v.get_type() == Variant::OBJECT) {
-							//could actually check the object property if exists..? yes i will!
-							Ref<Resource> res = v;
-							if (res.is_valid()) {
-								hint = PROPERTY_HINT_RESOURCE_TYPE;
-								hint_string = res->get_class();
+						if (hint.type != Variant::NIL) {
+							PropertyInfo pi = hint;
+							pi.name = "value";
+							p_list->push_back(pi);
+						} else {
+							PropertyHint hint = PROPERTY_HINT_NONE;
+							String hint_string;
+
+							if (v.get_type() == Variant::OBJECT) {
+								//could actually check the object property if exists..? yes i will!
+								Ref<Resource> res = v;
+								if (res.is_valid()) {
+									hint = PROPERTY_HINT_RESOURCE_TYPE;
+									hint_string = res->get_class();
+								}
 							}
-						}
 
-						if (v.get_type() != Variant::NIL) {
-							p_list->push_back(PropertyInfo(v.get_type(), "value", hint, hint_string));
+							if (v.get_type() != Variant::NIL) {
+								p_list->push_back(PropertyInfo(v.get_type(), "value", hint, hint_string));
+							}
 						}
 					}
 

+ 5 - 9
editor/editor_node.cpp

@@ -609,6 +609,7 @@ void EditorNode::_notification(int p_what) {
 			p->set_item_icon(p->get_item_index(HELP_DOCS), gui_base->get_icon("Instance", "EditorIcons"));
 			p->set_item_icon(p->get_item_index(HELP_QA), gui_base->get_icon("Instance", "EditorIcons"));
 			p->set_item_icon(p->get_item_index(HELP_REPORT_A_BUG), gui_base->get_icon("Instance", "EditorIcons"));
+			p->set_item_icon(p->get_item_index(HELP_SUGGEST_A_FEATURE), gui_base->get_icon("Instance", "EditorIcons"));
 			p->set_item_icon(p->get_item_index(HELP_SEND_DOCS_FEEDBACK), gui_base->get_icon("Instance", "EditorIcons"));
 			p->set_item_icon(p->get_item_index(HELP_COMMUNITY), gui_base->get_icon("Instance", "EditorIcons"));
 			p->set_item_icon(p->get_item_index(HELP_ABOUT), gui_base->get_icon("Godot", "EditorIcons"));
@@ -1516,15 +1517,6 @@ void EditorNode::_save_scene(String p_file, int idx) {
 		return;
 	}
 
-	// force creation of node path cache
-	// (hacky but needed for the tree to update properly)
-	Node *dummy_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE);
-	if (!dummy_scene) {
-		show_accept(TTR("Couldn't save scene. Likely dependencies (instances or inheritance) couldn't be satisfied."), TTR("OK"));
-		return;
-	}
-	memdelete(dummy_scene);
-
 	int flg = 0;
 	if (EditorSettings::get_singleton()->get("filesystem/on_save/compress_binary_resources")) {
 		flg |= ResourceSaver::FLAG_COMPRESS;
@@ -2803,6 +2795,9 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
 		case HELP_REPORT_A_BUG: {
 			OS::get_singleton()->shell_open("https://github.com/godotengine/godot/issues");
 		} break;
+		case HELP_SUGGEST_A_FEATURE: {
+			OS::get_singleton()->shell_open("https://github.com/godotengine/godot-proposals#readme");
+		} break;
 		case HELP_SEND_DOCS_FEEDBACK: {
 			OS::get_singleton()->shell_open("https://github.com/godotengine/godot-docs/issues");
 		} break;
@@ -6364,6 +6359,7 @@ EditorNode::EditorNode() {
 	p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/online_docs", TTR("Online Documentation")), HELP_DOCS);
 	p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/q&a", TTR("Questions & Answers")), HELP_QA);
 	p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG);
+	p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/suggest_a_feature", TTR("Suggest a Feature")), HELP_SUGGEST_A_FEATURE);
 	p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK);
 	p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/community", TTR("Community")), HELP_COMMUNITY);
 	p->add_separator();

+ 1 - 0
editor/editor_node.h

@@ -200,6 +200,7 @@ private:
 		HELP_DOCS,
 		HELP_QA,
 		HELP_REPORT_A_BUG,
+		HELP_SUGGEST_A_FEATURE,
 		HELP_SEND_DOCS_FEEDBACK,
 		HELP_COMMUNITY,
 		HELP_ABOUT,

+ 1 - 1
editor/plugins/canvas_item_editor_plugin.cpp

@@ -5844,7 +5844,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
 
 	p = view_menu->get_popup();
 	p->set_hide_on_checkable_item_selection(false);
-	p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_grid", TTR("Always Show Grid"), KEY_MASK_CTRL | KEY_G), SHOW_GRID);
+	p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_grid", TTR("Always Show Grid"), KEY_MASK_CMD | KEY_G), SHOW_GRID);
 	p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_helpers", TTR("Show Helpers"), KEY_H), SHOW_HELPERS);
 	p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_rulers", TTR("Show Rulers")), SHOW_RULERS);
 	p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_guides", TTR("Show Guides"), KEY_Y), SHOW_GUIDES);

+ 1 - 1
editor/plugins/spatial_editor_plugin.cpp

@@ -6345,7 +6345,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
 
 	p->add_separator();
 	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN);
-	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid")), MENU_VIEW_GRID);
+	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid"), KEY_MASK_CMD + KEY_G), MENU_VIEW_GRID);
 
 	p->add_separator();
 	p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("Settings...")), MENU_VIEW_CAMERA_SETTINGS);

+ 46 - 8
editor/plugins/sprite_frames_editor_plugin.cpp

@@ -103,17 +103,16 @@ void SpriteFramesEditor::_sheet_preview_draw() {
 	split_sheet_dialog->get_ok()->set_text(vformat(TTR("Add %d Frame(s)"), frames_selected.size()));
 }
 void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) {
-	Ref<InputEventMouseButton> mb = p_event;
-
+	const Ref<InputEventMouseButton> mb = p_event;
 	if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
-		Size2i size = split_sheet_preview->get_size();
-		int h = split_sheet_h->get_value();
-		int v = split_sheet_v->get_value();
+		const Size2i size = split_sheet_preview->get_size();
+		const int h = split_sheet_h->get_value();
+		const int v = split_sheet_v->get_value();
 
-		int x = CLAMP(int(mb->get_position().x) * h / size.width, 0, h - 1);
-		int y = CLAMP(int(mb->get_position().y) * v / size.height, 0, v - 1);
+		const int x = CLAMP(int(mb->get_position().x) * h / size.width, 0, h - 1);
+		const int y = CLAMP(int(mb->get_position().y) * v / size.height, 0, v - 1);
 
-		int idx = h * y + x;
+		const int idx = h * y + x;
 
 		if (mb->get_shift() && last_frame_selected >= 0) {
 			//select multiple
@@ -124,6 +123,9 @@ void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) {
 			}
 
 			for (int i = from; i <= to; i++) {
+				// Prevent double-toggling the same frame when moving the mouse when the mouse button is still held.
+				frames_toggled_by_mouse_hover.insert(idx);
+
 				if (mb->get_control()) {
 					frames_selected.erase(i);
 				} else {
@@ -131,6 +133,9 @@ void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) {
 				}
 			}
 		} else {
+			// Prevent double-toggling the same frame when moving the mouse when the mouse button is still held.
+			frames_toggled_by_mouse_hover.insert(idx);
+
 			if (frames_selected.has(idx)) {
 				frames_selected.erase(idx);
 			} else {
@@ -141,6 +146,39 @@ void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) {
 		last_frame_selected = idx;
 		split_sheet_preview->update();
 	}
+
+	if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+		frames_toggled_by_mouse_hover.clear();
+	}
+
+	const Ref<InputEventMouseMotion> mm = p_event;
+	if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_LEFT) {
+		// Select by holding down the mouse button on frames.
+		const Size2i size = split_sheet_preview->get_size();
+		const int h = split_sheet_h->get_value();
+		const int v = split_sheet_v->get_value();
+
+		const int x = CLAMP(int(mm->get_position().x) * h / size.width, 0, h - 1);
+		const int y = CLAMP(int(mm->get_position().y) * v / size.height, 0, v - 1);
+
+		const int idx = h * y + x;
+
+		if (!frames_toggled_by_mouse_hover.has(idx)) {
+			// Only allow toggling each tile once per mouse hold.
+			// Otherwise, the selection would constantly "flicker" in and out when moving the mouse cursor.
+			// The mouse button must be released before it can be toggled again.
+			frames_toggled_by_mouse_hover.insert(idx);
+
+			if (frames_selected.has(idx)) {
+				frames_selected.erase(idx);
+			} else {
+				frames_selected.insert(idx);
+			}
+
+			last_frame_selected = idx;
+			split_sheet_preview->update();
+		}
+	}
 }
 
 void SpriteFramesEditor::_sheet_scroll_input(const Ref<InputEvent> &p_event) {

+ 1 - 0
editor/plugins/sprite_frames_editor_plugin.h

@@ -86,6 +86,7 @@ class SpriteFramesEditor : public HSplitContainer {
 	ToolButton *split_sheet_zoom_in;
 	EditorFileDialog *file_split_sheet;
 	Set<int> frames_selected;
+	Set<int> frames_toggled_by_mouse_hover;
 	int last_frame_selected;
 
 	float scale_ratio;

+ 7 - 3
editor/project_manager.cpp

@@ -1848,9 +1848,6 @@ void ProjectManager::_unhandled_input(const Ref<InputEvent> &p_ev) {
 			case KEY_ENTER: {
 				_open_selected_projects_ask();
 			} break;
-			case KEY_DELETE: {
-				_erase_project();
-			} break;
 			case KEY_HOME: {
 				if (_project_list->get_project_count() > 0) {
 					_project_list->select_project(0);
@@ -2491,12 +2488,14 @@ ProjectManager::ProjectManager() {
 
 	Button *open = memnew(Button);
 	open->set_text(TTR("Edit"));
+	open->set_shortcut(ED_SHORTCUT("project_manager/edit_project", TTR("Edit Project"), KEY_MASK_CMD | KEY_E));
 	tree_vb->add_child(open);
 	open->connect("pressed", this, "_open_selected_projects_ask");
 	open_btn = open;
 
 	Button *run = memnew(Button);
 	run->set_text(TTR("Run"));
+	run->set_shortcut(ED_SHORTCUT("project_manager/run_project", TTR("Run Project"), KEY_MASK_CMD | KEY_R));
 	tree_vb->add_child(run);
 	run->connect("pressed", this, "_run_project");
 	run_btn = run;
@@ -2505,6 +2504,7 @@ ProjectManager::ProjectManager() {
 
 	Button *scan = memnew(Button);
 	scan->set_text(TTR("Scan"));
+	scan->set_shortcut(ED_SHORTCUT("project_manager/scan_projects", TTR("Scan Projects"), KEY_MASK_CMD | KEY_S));
 	tree_vb->add_child(scan);
 	scan->connect("pressed", this, "_scan_projects");
 
@@ -2520,22 +2520,26 @@ ProjectManager::ProjectManager() {
 
 	Button *create = memnew(Button);
 	create->set_text(TTR("New Project"));
+	create->set_shortcut(ED_SHORTCUT("project_manager/new_project", TTR("New Project"), KEY_MASK_CMD | KEY_N));
 	tree_vb->add_child(create);
 	create->connect("pressed", this, "_new_project");
 
 	Button *import = memnew(Button);
 	import->set_text(TTR("Import"));
+	import->set_shortcut(ED_SHORTCUT("project_manager/import_project", TTR("Import Project"), KEY_MASK_CMD | KEY_I));
 	tree_vb->add_child(import);
 	import->connect("pressed", this, "_import_project");
 
 	Button *rename = memnew(Button);
 	rename->set_text(TTR("Rename"));
+	rename->set_shortcut(ED_SHORTCUT("project_manager/rename_project", TTR("Rename Project"), KEY_F2));
 	tree_vb->add_child(rename);
 	rename->connect("pressed", this, "_rename_project");
 	rename_btn = rename;
 
 	Button *erase = memnew(Button);
 	erase->set_text(TTR("Remove"));
+	erase->set_shortcut(ED_SHORTCUT("project_manager/remove_project", TTR("Remove Project"), KEY_DELETE));
 	tree_vb->add_child(erase);
 	erase->connect("pressed", this, "_erase_project");
 	erase_btn = erase;

+ 0 - 4
editor/spatial_editor_gizmos.cpp

@@ -635,8 +635,6 @@ bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point,
 			r_normal = -p_camera->project_ray_normal(p_point);
 			return true;
 		}
-
-		return false;
 	}
 
 	if (collision_segments.size()) {
@@ -687,8 +685,6 @@ bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point,
 			r_normal = -p_camera->project_ray_normal(p_point);
 			return true;
 		}
-
-		return false;
 	}
 
 	if (collision_mesh.is_valid()) {

+ 3 - 1
modules/gdscript/language_server/gdscript_workspace.cpp

@@ -187,7 +187,9 @@ Array GDScriptWorkspace::symbol(const Dictionary &p_params) {
 			E->get()->get_symbols().symbol_tree_as_list(E->key(), script_symbols);
 			for (int i = 0; i < script_symbols.size(); ++i) {
 				if (query.is_subsequence_ofi(script_symbols[i].name)) {
-					arr.push_back(script_symbols[i].to_json());
+					lsp::DocumentedSymbolInformation symbol = script_symbols[i];
+					symbol.location.uri = get_file_uri(symbol.location.uri);
+					arr.push_back(symbol.to_json());
 				}
 			}
 		}

+ 4 - 0
modules/pvr/image_compress_pvrtc.cpp

@@ -43,6 +43,10 @@ static void _compress_pvrtc4(Image *p_img) {
 	if (!img->is_size_po2() || img->get_width() != img->get_height()) {
 		make_mipmaps = img->has_mipmaps();
 		img->resize_to_po2(true);
+		// Resizing can fail for some formats
+		if (!img->is_size_po2() || img->get_width() != img->get_height()) {
+			ERR_FAIL_MSG("Failed to resize the image for compression.");
+		}
 	}
 	img->convert(Image::FORMAT_RGBA8);
 	if (!img->has_mipmaps() && make_mipmaps) {

+ 3 - 2
modules/visual_script/visual_script_property_selector.cpp

@@ -466,10 +466,11 @@ void VisualScriptPropertySelector::_item_selected() {
 
 		at_class = ClassDB::get_parent_class_nocheck(at_class);
 	}
-	Map<String, DocData::ClassDoc>::Element *T = dd->class_list.find(class_type);
+	Vector<String> functions = name.rsplit("/", false);
+	at_class = functions.size() > 3 ? functions[functions.size() - 2] : class_type;
+	Map<String, DocData::ClassDoc>::Element *T = dd->class_list.find(at_class);
 	if (T) {
 		for (int i = 0; i < T->get().methods.size(); i++) {
-			Vector<String> functions = name.rsplit("/", false, 1);
 			if (T->get().methods[i].name == functions[functions.size() - 1]) {
 				text = T->get().methods[i].description;
 			}

+ 3 - 0
modules/websocket/doc_classes/WebSocketServer.xml

@@ -89,6 +89,9 @@
 		<member name="ca_chain" type="X509Certificate" setter="set_ca_chain" getter="get_ca_chain">
 			When using SSL (see [member private_key] and [member ssl_certificate]), you can set this to a valid [X509Certificate] to be provided as additional CA chain information during the SSL handshake.
 		</member>
+		<member name="handshake_timeout" type="float" setter="set_handshake_timeout" getter="get_handshake_timeout" default="3.0">
+			The time in seconds before a pending client (i.e. a client that has not yet finished the HTTP handshake) is considered stale and forcefully disconnected.
+		</member>
 		<member name="private_key" type="CryptoKey" setter="set_private_key" getter="get_private_key">
 			When set to a valid [CryptoKey] (along with [member ssl_certificate]) will cause the server to require SSL instead of regular TCP (i.e. the [code]wss://[/code] protocol).
 		</member>

+ 6 - 3
modules/websocket/websocket_client.cpp

@@ -43,9 +43,9 @@ Error WebSocketClient::connect_to_url(String p_url, const Vector<String> p_proto
 	_is_multiplayer = gd_mp_api;
 
 	String host = p_url;
-	String path = "/";
-	String scheme = "";
-	int port = 80;
+	String path;
+	String scheme;
+	int port = 0;
 	Error err = p_url.parse_url(scheme, host, port, path);
 	ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid URL: " + p_url);
 
@@ -56,6 +56,9 @@ Error WebSocketClient::connect_to_url(String p_url, const Vector<String> p_proto
 	if (port == 0) {
 		port = ssl ? 443 : 80;
 	}
+	if (path.empty()) {
+		path = "/";
+	}
 	return connect_to_host(host, path, port, ssl, p_protocols, p_custom_headers);
 }
 

+ 13 - 0
modules/websocket/websocket_server.cpp

@@ -65,6 +65,10 @@ void WebSocketServer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_ca_chain"), &WebSocketServer::set_ca_chain);
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ca_chain", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", 0), "set_ca_chain", "get_ca_chain");
 
+	ClassDB::bind_method(D_METHOD("get_handshake_timeout"), &WebSocketServer::get_handshake_timeout);
+	ClassDB::bind_method(D_METHOD("set_handshake_timeout", "timeout"), &WebSocketServer::set_handshake_timeout);
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "handshake_timeout"), "set_handshake_timeout", "get_handshake_timeout");
+
 	ADD_SIGNAL(MethodInfo("client_close_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason")));
 	ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::BOOL, "was_clean_close")));
 	ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol")));
@@ -108,6 +112,15 @@ void WebSocketServer::set_ca_chain(Ref<X509Certificate> p_ca_chain) {
 	ca_chain = p_ca_chain;
 }
 
+float WebSocketServer::get_handshake_timeout() const {
+	return handshake_timeout / 1000.0;
+}
+
+void WebSocketServer::set_handshake_timeout(float p_timeout) {
+	ERR_FAIL_COND(p_timeout <= 0.0);
+	handshake_timeout = p_timeout * 1000;
+}
+
 NetworkedMultiplayerPeer::ConnectionStatus WebSocketServer::get_connection_status() const {
 	if (is_listening()) {
 		return CONNECTION_CONNECTED;

+ 4 - 0
modules/websocket/websocket_server.h

@@ -48,6 +48,7 @@ protected:
 	Ref<CryptoKey> private_key;
 	Ref<X509Certificate> ssl_cert;
 	Ref<X509Certificate> ca_chain;
+	uint32_t handshake_timeout = 3000;
 
 public:
 	virtual void poll() = 0;
@@ -80,6 +81,9 @@ public:
 	Ref<X509Certificate> get_ca_chain() const;
 	void set_ca_chain(Ref<X509Certificate> p_ca_chain);
 
+	float get_handshake_timeout() const;
+	void set_handshake_timeout(float p_timeout);
+
 	virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0;
 
 	WebSocketServer();

+ 1 - 0
modules/websocket/wsl_client.cpp

@@ -158,6 +158,7 @@ bool WSLClient::_verify_headers(String &r_protocol) {
 
 Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocols, const Vector<String> p_custom_headers) {
 	ERR_FAIL_COND_V(_connection.is_valid(), ERR_ALREADY_IN_USE);
+	ERR_FAIL_COND_V(p_path.empty(), ERR_INVALID_PARAMETER);
 
 	_peer = Ref<WSLPeer>(memnew(WSLPeer));
 	IP_Address addr;

+ 3 - 3
modules/websocket/wsl_server.cpp

@@ -104,8 +104,8 @@ bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols) {
 	return true;
 }
 
-Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols) {
-	if (OS::get_singleton()->get_ticks_msec() - time > WSL_SERVER_TIMEOUT) {
+Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols, uint64_t p_timeout) {
+	if (OS::get_singleton()->get_ticks_msec() - time > p_timeout) {
 		return ERR_TIMEOUT;
 	}
 	if (use_ssl) {
@@ -197,7 +197,7 @@ void WSLServer::poll() {
 	List<Ref<PendingPeer>> remove_peers;
 	for (List<Ref<PendingPeer>>::Element *E = _pending.front(); E; E = E->next()) {
 		Ref<PendingPeer> ppeer = E->get();
-		Error err = ppeer->do_handshake(_protocols);
+		Error err = ppeer->do_handshake(_protocols, handshake_timeout);
 		if (err == ERR_BUSY) {
 			continue;
 		} else if (err != OK) {

+ 1 - 3
modules/websocket/wsl_server.h

@@ -40,8 +40,6 @@
 #include "core/io/stream_peer_tcp.h"
 #include "core/io/tcp_server.h"
 
-#define WSL_SERVER_TIMEOUT 1000
-
 class WSLServer : public WebSocketServer {
 	GDCIIMPL(WSLServer, WebSocketServer);
 
@@ -66,7 +64,7 @@ private:
 
 		PendingPeer();
 
-		Error do_handshake(const Vector<String> p_protocols);
+		Error do_handshake(const Vector<String> p_protocols, uint64_t p_timeout);
 	};
 
 	int _in_buf_size;

+ 1 - 1
platform/android/export/export.cpp

@@ -1910,7 +1910,7 @@ public:
 		err = OS::get_singleton()->execute(adb, args, true, nullptr, &output, &rv, true);
 		print_verbose(output);
 		if (err || rv != 0) {
-			EditorNode::add_io_error("Could not install to device.");
+			EditorNode::add_io_error("Could not install to device: " + output);
 			CLEANUP_AND_RETURN(ERR_CANT_CREATE);
 		}
 

+ 2 - 3
platform/android/java/app/build.gradle

@@ -9,7 +9,7 @@ buildscript {
 
     repositories {
         google()
-        jcenter()
+        mavenCentral()
 //CHUNK_BUILDSCRIPT_REPOSITORIES_BEGIN
 //CHUNK_BUILDSCRIPT_REPOSITORIES_END
     }
@@ -25,9 +25,8 @@ apply plugin: 'com.android.application'
 
 allprojects {
     repositories {
-        mavenCentral()
         google()
-        jcenter()
+        mavenCentral()
 //CHUNK_ALLPROJECTS_REPOSITORIES_BEGIN
 //CHUNK_ALLPROJECTS_REPOSITORIES_END
 

+ 2 - 2
platform/android/java/app/config.gradle

@@ -1,11 +1,11 @@
 ext.versions = [
-    androidGradlePlugin: '4.0.1',
+    androidGradlePlugin: '4.2.1',
     compileSdk         : 29,
     minSdk             : 18,
     targetSdk          : 29,
     buildTools         : '30.0.3',
     supportCoreUtils   : '1.0.0',
-    kotlinVersion      : '1.4.10',
+    kotlinVersion      : '1.5.10',
     v4Support          : '1.0.0',
     javaVersion        : 1.8,
     ndkVersion         : '21.4.7075529' // Also update 'platform/android/detect.py#get_project_ndk_version()' when this is updated.

+ 1 - 2
platform/android/java/build.gradle

@@ -5,7 +5,7 @@ buildscript {
 
     repositories {
         google()
-        jcenter()
+        mavenCentral()
     }
     dependencies {
         classpath libraries.androidGradlePlugin
@@ -16,7 +16,6 @@ buildscript {
 allprojects {
     repositories {
         google()
-        jcenter()
         mavenCentral()
     }
 }

+ 3 - 3
platform/android/java/gradle/wrapper/gradle-wrapper.properties

@@ -1,6 +1,6 @@
-#Mon Sep 02 02:44:30 PDT 2019
+#Wed Jun 23 23:42:22 PDT 2021
 distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
 distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
+zipStoreBase=GRADLE_USER_HOME

+ 7 - 2
platform/javascript/export/export.cpp

@@ -136,8 +136,11 @@ public:
 		// Wrong protocol
 		ERR_FAIL_COND_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", "Invalid method or HTTP version.");
 
-		const String req_file = req[1].get_file();
-		const String req_ext = req[1].get_extension();
+		const int query_index = req[1].find_char('?');
+		const String path = (query_index == -1) ? req[1] : req[1].substr(0, query_index);
+
+		const String req_file = path.get_file();
+		const String req_ext = path.get_extension();
 		const String cache_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("web");
 		const String filepath = cache_path.plus_file(req_file);
 
@@ -446,6 +449,7 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re
 	}
 	config["canvasResizePolicy"] = p_preset->get("html/canvas_resize_policy");
 	config["experimentalVK"] = p_preset->get("html/experimental_virtual_keyboard");
+	config["focusCanvas"] = p_preset->get("html/focus_canvas_on_start");
 	config["gdnativeLibs"] = libs;
 	config["executable"] = p_name;
 	config["args"] = args;
@@ -648,6 +652,7 @@ void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_op
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "*.html"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "html/canvas_resize_policy", PROPERTY_HINT_ENUM, "None,Project,Adaptive"), 2));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/focus_canvas_on_start"), true));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/experimental_virtual_keyboard"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "progressive_web_app/enabled"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/offline_page", PROPERTY_HINT_FILE, "*.html"), ""));

+ 10 - 0
platform/javascript/js/engine/config.js

@@ -90,6 +90,14 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
 		 * @default
 		 */
 		args: [],
+		/**
+		 * When enabled, the game canvas will automatically grab the focus when the engine starts.
+		 *
+		 * @memberof EngineConfig
+		 * @type {boolean}
+		 * @default
+		 */
+		focusCanvas: true,
 		/**
 		 * When enabled, this will turn on experimental virtual keyboard support on mobile.
 		 *
@@ -238,6 +246,7 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
 		this.persistentPaths = parse('persistentPaths', this.persistentPaths);
 		this.persistentDrops = parse('persistentDrops', this.persistentDrops);
 		this.experimentalVK = parse('experimentalVK', this.experimentalVK);
+		this.focusCanvas = parse('focusCanvas', this.focusCanvas);
 		this.gdnativeLibs = parse('gdnativeLibs', this.gdnativeLibs);
 		this.fileSizes = parse('fileSizes', this.fileSizes);
 		this.args = parse('args', this.args);
@@ -324,6 +333,7 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
 			'locale': locale,
 			'persistentDrops': this.persistentDrops,
 			'virtualKeyboard': this.experimentalVK,
+			'focusCanvas': this.focusCanvas,
 			'onExecute': this.onExecute,
 			'onExit': function (p_code) {
 				cleanup(); // We always need to call the cleanup callback to free memory.

+ 3 - 0
platform/javascript/js/libs/library_godot_os.js

@@ -72,6 +72,9 @@ const GodotConfig = {
 			GodotConfig.persistent_drops = !!p_opts['persistentDrops'];
 			GodotConfig.on_execute = p_opts['onExecute'];
 			GodotConfig.on_exit = p_opts['onExit'];
+			if (p_opts['focusCanvas']) {
+				GodotConfig.canvas.focus();
+			}
 		},
 
 		locate_file: function (file) {

+ 6 - 0
platform/osx/os_osx.mm

@@ -3295,6 +3295,12 @@ void OS_OSX::set_mouse_mode(MouseMode p_mode) {
 	ignore_warp = true;
 	warp_events.clear();
 	mouse_mode = p_mode;
+
+	if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
+		CursorShape p_shape = cursor_shape;
+		cursor_shape = OS::CURSOR_MAX;
+		set_cursor_shape(p_shape);
+	}
 }
 
 OS::MouseMode OS_OSX::get_mouse_mode() const {

+ 3 - 3
platform/uwp/export/export.cpp

@@ -1056,19 +1056,19 @@ public:
 		// Capabilities
 		const char **basic = uwp_capabilities;
 		while (*basic) {
-			r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*basic).camelcase_to_underscore(false)), false));
+			r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*basic)), false));
 			basic++;
 		}
 
 		const char **uap = uwp_uap_capabilities;
 		while (*uap) {
-			r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*uap).camelcase_to_underscore(false)), false));
+			r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*uap)), false));
 			uap++;
 		}
 
 		const char **device = uwp_device_capabilities;
 		while (*device) {
-			r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*device).camelcase_to_underscore(false)), false));
+			r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*device)), false));
 			device++;
 		}
 	}

+ 11 - 1
platform/windows/export/export.cpp

@@ -310,7 +310,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
 	args.push_back(p_path);
 #ifndef WINDOWS_ENABLED
 	args.push_back("-out");
-	args.push_back(p_path);
+	args.push_back(p_path + "_signed");
 #endif
 
 	String str;
@@ -326,6 +326,16 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
 		return FAILED;
 	}
 
+#ifndef WINDOWS_ENABLED
+	DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
+
+	err = tmp_dir->remove(p_path);
+	ERR_FAIL_COND_V(err != OK, err);
+
+	err = tmp_dir->rename(p_path + "_signed", p_path);
+	ERR_FAIL_COND_V(err != OK, err);
+#endif
+
 	return OK;
 }
 

+ 1 - 1
scene/gui/graph_node.cpp

@@ -575,7 +575,7 @@ void GraphNode::_connpos_update() {
 			continue;
 		}
 
-		Size2i size = c->get_combined_minimum_size();
+		Size2i size = c->get_rect().size;
 
 		int y = sb->get_margin(MARGIN_TOP) + vofs;
 		int h = size.y;

+ 116 - 24
scene/gui/texture_progress.cpp

@@ -221,43 +221,87 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F
 		double width_texture = 0.0;
 		double first_section_size = 0.0;
 		double last_section_size = 0.0;
-		switch (mode) {
-			case FILL_LEFT_TO_RIGHT:
-			case FILL_RIGHT_TO_LEFT: {
+		switch (p_mode) {
+			case FILL_LEFT_TO_RIGHT: {
 				width_total = dst_rect.size.x;
 				width_texture = texture_size.x;
 				first_section_size = topleft.x;
 				last_section_size = bottomright.x;
 			} break;
-			case FILL_TOP_TO_BOTTOM:
-			case FILL_BOTTOM_TO_TOP: {
+			case FILL_RIGHT_TO_LEFT: {
+				width_total = dst_rect.size.x;
+				width_texture = texture_size.x;
+				// In contrast to `FILL_LEFT_TO_RIGHT`, `first_section_size` and `last_section_size` should switch value.
+				first_section_size = bottomright.x;
+				last_section_size = topleft.x;
+			} break;
+			case FILL_TOP_TO_BOTTOM: {
 				width_total = dst_rect.size.y;
 				width_texture = texture_size.y;
 				first_section_size = topleft.y;
 				last_section_size = bottomright.y;
 			} break;
+			case FILL_BOTTOM_TO_TOP: {
+				width_total = dst_rect.size.y;
+				width_texture = texture_size.y;
+				// Similar to `FILL_RIGHT_TO_LEFT`.
+				first_section_size = bottomright.y;
+				last_section_size = topleft.y;
+			} break;
 			case FILL_BILINEAR_LEFT_AND_RIGHT: {
-				// TODO: Implement
+				width_total = dst_rect.size.x;
+				width_texture = texture_size.x;
+				first_section_size = topleft.x;
+				last_section_size = bottomright.x;
 			} break;
 			case FILL_BILINEAR_TOP_AND_BOTTOM: {
-				// TODO: Implement
+				width_total = dst_rect.size.y;
+				width_texture = texture_size.y;
+				first_section_size = topleft.y;
+				last_section_size = bottomright.y;
 			} break;
 			case FILL_CLOCKWISE:
 			case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE:
 			case FILL_COUNTER_CLOCKWISE: {
-				// Those modes are circular, not relevant for nine patch
+				// Those modes are circular, not relevant for nine patch.
 			} break;
+			case FILL_MODE_MAX:
+				break;
 		}
 
 		double width_filled = width_total * p_ratio;
 		double middle_section_size = MAX(0.0, width_texture - first_section_size - last_section_size);
 
-		middle_section_size *= MIN(1.0, (MAX(0.0, width_filled - first_section_size) / MAX(1.0, width_total - first_section_size - last_section_size)));
-		last_section_size = MAX(0.0, last_section_size - (width_total - width_filled));
-		first_section_size = MIN(first_section_size, width_filled);
-		width_texture = MIN(width_texture, first_section_size + middle_section_size + last_section_size);
+		// Maximum middle texture size.
+		double max_middle_texture_size = middle_section_size;
+
+		// Maximum real middle texture size.
+		double max_middle_real_size = MAX(0.0, width_total - (first_section_size + last_section_size));
+
+		switch (p_mode) {
+			case FILL_BILINEAR_LEFT_AND_RIGHT:
+			case FILL_BILINEAR_TOP_AND_BOTTOM: {
+				last_section_size = MAX(0.0, last_section_size - (width_total - width_filled) * 0.5);
+				first_section_size = MAX(0.0, first_section_size - (width_total - width_filled) * 0.5);
+
+				// When `width_filled` increases, `middle_section_size` only increases when either of `first_section_size` and `last_section_size` is zero.
+				// Also, it should always be smaller than or equal to `(width_total - (first_section_size + last_section_size))`.
+				double real_middle_size = width_filled - first_section_size - last_section_size;
+				middle_section_size *= MIN(max_middle_real_size, real_middle_size) / max_middle_real_size;
+
+				width_texture = MIN(width_texture, first_section_size + middle_section_size + last_section_size);
+			} break;
+			case FILL_MODE_MAX:
+				break;
+			default: {
+				middle_section_size *= MIN(1.0, (MAX(0.0, width_filled - first_section_size) / MAX(1.0, width_total - first_section_size - last_section_size)));
+				last_section_size = MAX(0.0, last_section_size - (width_total - width_filled));
+				first_section_size = MIN(first_section_size, width_filled);
+				width_texture = MIN(width_texture, first_section_size + middle_section_size + last_section_size);
+			}
+		}
 
-		switch (mode) {
+		switch (p_mode) {
 			case FILL_LEFT_TO_RIGHT: {
 				src_rect.size.x = width_texture;
 				dst_rect.size.x = width_filled;
@@ -287,16 +331,32 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F
 				bottomright.y = first_section_size;
 			} break;
 			case FILL_BILINEAR_LEFT_AND_RIGHT: {
-				// TODO: Implement
+				double center_mapped_from_real_width = (width_total * 0.5 - topleft.x) / max_middle_real_size * max_middle_texture_size + topleft.x;
+				double drift_from_unscaled_center = (src_rect.size.x * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.x - topleft.x);
+				src_rect.position.x += center_mapped_from_real_width + drift_from_unscaled_center - width_texture * 0.5;
+				src_rect.size.x = width_texture;
+				dst_rect.position.x += (width_total - width_filled) * 0.5;
+				dst_rect.size.x = width_filled;
+				topleft.x = first_section_size;
+				bottomright.x = last_section_size;
 			} break;
 			case FILL_BILINEAR_TOP_AND_BOTTOM: {
-				// TODO: Implement
+				double center_mapped_from_real_width = (width_total * 0.5 - topleft.y) / max_middle_real_size * max_middle_texture_size + topleft.y;
+				double drift_from_unscaled_center = (src_rect.size.y * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.y - topleft.y);
+				src_rect.position.y += center_mapped_from_real_width + drift_from_unscaled_center - width_texture * 0.5;
+				src_rect.size.y = width_texture;
+				dst_rect.position.y += (width_total - width_filled) * 0.5;
+				dst_rect.size.y = width_filled;
+				topleft.y = first_section_size;
+				bottomright.y = last_section_size;
 			} break;
 			case FILL_CLOCKWISE:
 			case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE:
 			case FILL_COUNTER_CLOCKWISE: {
-				// Those modes are circular, not relevant for nine patch
+				// Those modes are circular, not relevant for nine patch.
 			} break;
+			case FILL_MODE_MAX:
+				break;
 		}
 	}
 
@@ -310,19 +370,34 @@ void TextureProgress::_notification(int p_what) {
 	const float corners[12] = { -0.125, -0.375, -0.625, -0.875, 0.125, 0.375, 0.625, 0.875, 1.125, 1.375, 1.625, 1.875 };
 	switch (p_what) {
 		case NOTIFICATION_DRAW: {
-			if (nine_patch_stretch && (mode == FILL_LEFT_TO_RIGHT || mode == FILL_RIGHT_TO_LEFT || mode == FILL_TOP_TO_BOTTOM || mode == FILL_BOTTOM_TO_TOP)) {
+			if (nine_patch_stretch && (mode == FILL_LEFT_TO_RIGHT || mode == FILL_RIGHT_TO_LEFT || mode == FILL_TOP_TO_BOTTOM || mode == FILL_BOTTOM_TO_TOP || mode == FILL_BILINEAR_LEFT_AND_RIGHT || mode == FILL_BILINEAR_TOP_AND_BOTTOM)) {
 				if (under.is_valid()) {
-					draw_nine_patch_stretched(under, FILL_LEFT_TO_RIGHT, 1.0, tint_under);
+					draw_nine_patch_stretched(under, mode, 1.0, tint_under);
 				}
 				if (progress.is_valid()) {
 					draw_nine_patch_stretched(progress, mode, get_as_ratio(), tint_progress);
 				}
 				if (over.is_valid()) {
-					draw_nine_patch_stretched(over, FILL_LEFT_TO_RIGHT, 1.0, tint_over);
+					draw_nine_patch_stretched(over, mode, 1.0, tint_over);
 				}
 			} else {
 				if (under.is_valid()) {
-					draw_texture(under, Point2(), tint_under);
+					switch (mode) {
+						case FILL_CLOCKWISE:
+						case FILL_COUNTER_CLOCKWISE:
+						case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: {
+							if (nine_patch_stretch) {
+								Rect2 region = Rect2(Point2(), get_size());
+								draw_texture_rect(under, region, false, tint_under);
+							} else {
+								draw_texture(under, Point2(), tint_under);
+							}
+						} break;
+						case FILL_MODE_MAX:
+							break;
+						default:
+							draw_texture(under, Point2(), tint_under);
+					}
 				}
 				if (progress.is_valid()) {
 					Size2 s = progress->get_size();
@@ -353,7 +428,7 @@ void TextureProgress::_notification(int p_what) {
 							float val = get_as_ratio() * rad_max_degrees / 360;
 							if (val == 1) {
 								Rect2 region = Rect2(Point2(), s);
-								draw_texture_rect_region(progress, region, region, tint_progress);
+								draw_texture_rect(progress, region, false, tint_progress);
 							} else if (val != 0) {
 								Array pts;
 								float direction = mode == FILL_COUNTER_CLOCKWISE ? -1 : 1;
@@ -416,12 +491,29 @@ void TextureProgress::_notification(int p_what) {
 							Rect2 region = Rect2(Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio()));
 							draw_texture_rect_region(progress, region, region, tint_progress);
 						} break;
+						case FILL_MODE_MAX:
+							break;
 						default:
 							draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress);
 					}
 				}
 				if (over.is_valid()) {
-					draw_texture(over, Point2(), tint_over);
+					switch (mode) {
+						case FILL_CLOCKWISE:
+						case FILL_COUNTER_CLOCKWISE:
+						case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: {
+							if (nine_patch_stretch) {
+								Rect2 region = Rect2(Point2(), get_size());
+								draw_texture_rect(over, region, false, tint_over);
+							} else {
+								draw_texture(over, Point2(), tint_over);
+							}
+						} break;
+						case FILL_MODE_MAX:
+							break;
+						default:
+							draw_texture(over, Point2(), tint_over);
+					}
 				}
 			}
 
@@ -430,7 +522,7 @@ void TextureProgress::_notification(int p_what) {
 }
 
 void TextureProgress::set_fill_mode(int p_fill) {
-	ERR_FAIL_INDEX(p_fill, 9);
+	ERR_FAIL_INDEX(p_fill, FILL_MODE_MAX);
 	mode = (FillMode)p_fill;
 	update();
 }
@@ -513,7 +605,7 @@ void TextureProgress::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_under", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_under_texture", "get_under_texture");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_over_texture", "get_over_texture");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_progress_texture", "get_progress_texture");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom), Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom),Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode");
 	ADD_GROUP("Tint", "tint_");
 	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under"), "set_tint_under", "get_tint_under");
 	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over"), "set_tint_over", "get_tint_over");

+ 2 - 1
scene/gui/texture_progress.h

@@ -54,7 +54,8 @@ public:
 		FILL_COUNTER_CLOCKWISE,
 		FILL_BILINEAR_LEFT_AND_RIGHT,
 		FILL_BILINEAR_TOP_AND_BOTTOM,
-		FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE
+		FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE,
+		FILL_MODE_MAX,
 	};
 
 	void set_fill_mode(int p_fill);

+ 13 - 2
scene/resources/packed_scene.cpp

@@ -471,7 +471,9 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
 	StringName type = p_node->get_class();
 
 	Ref<Script> script = p_node->get_script();
-	if (script.is_valid()) {
+	if (Engine::get_singleton()->is_editor_hint() && script.is_valid()) {
+		// Should be called in the editor only and not at runtime,
+		// otherwise it can cause problems because of missing instance state support.
 		script->update_exports();
 	}
 
@@ -893,6 +895,13 @@ Error SceneState::pack(Node *p_scene) {
 		node_paths.write[E->get()] = scene->get_path_to(E->key());
 	}
 
+	if (Engine::get_singleton()->is_editor_hint()) {
+		// Build node path cache
+		for (Map<Node *, int>::Element *E = node_map.front(); E; E = E->next()) {
+			node_path_cache[scene->get_path_to(E->key())] = E->get();
+		}
+	}
+
 	return OK;
 }
 
@@ -927,10 +936,12 @@ Ref<SceneState> SceneState::_get_base_scene_state() const {
 }
 
 int SceneState::find_node_by_path(const NodePath &p_node) const {
+	ERR_FAIL_COND_V_MSG(node_path_cache.size() == 0, -1, "This operation requires the node cache to have been built.");
+
 	if (!node_path_cache.has(p_node)) {
 		if (_get_base_scene_state().is_valid()) {
 			int idx = _get_base_scene_state()->find_node_by_path(p_node);
-			if (idx >= 0) {
+			if (idx != -1) {
 				int rkey = _find_base_scene_node_remap_key(idx);
 				if (rkey == -1) {
 					rkey = nodes.size() + base_scene_node_remap.size();

+ 3 - 3
scene/resources/primitive_meshes.cpp

@@ -886,9 +886,9 @@ void CylinderMesh::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CylinderMesh::set_rings);
 	ClassDB::bind_method(D_METHOD("get_rings"), &CylinderMesh::get_rings);
 
-	ADD_PROPERTY(PropertyInfo(Variant::REAL, "top_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_top_radius", "get_top_radius");
-	ADD_PROPERTY(PropertyInfo(Variant::REAL, "bottom_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_bottom_radius", "get_bottom_radius");
-	ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_height", "get_height");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "top_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_top_radius", "get_top_radius");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "bottom_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_bottom_radius", "get_bottom_radius");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_height", "get_height");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_rings", "get_rings");
 }