From 2dfb108a6a392f2201a1ad5ea015c7164b9f8237 Mon Sep 17 00:00:00 2001 From: Clement C Date: Thu, 20 Jul 2023 12:45:54 +0200 Subject: [PATCH 01/31] Fix crash when changing node type from PopMenu to ItemList (cherry picked from commit 999a1fffec85628c7eb1cf2e9808bf7e0b44f26c) --- scene/gui/popup_menu.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 1a6adca1217..07b7f0505e6 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -1952,8 +1952,13 @@ bool PopupMenu::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_item_icon(item_index); return true; } else if (property == "checkable") { - r_ret = this->items[item_index].checkable_type; - return true; + if (item_index >= 0 && item_index < items.size()) { + r_ret = items[item_index].checkable_type; + return true; + } else { + r_ret = Item::CHECKABLE_TYPE_NONE; + ERR_FAIL_V(true); + } } else if (property == "checked") { r_ret = is_item_checked(item_index); return true; From db7406e6a7a34d9a7a3a600855e4c43a6ee45e1c Mon Sep 17 00:00:00 2001 From: Fredia Huya-Kouadio Date: Thu, 20 Jul 2023 12:45:54 +0200 Subject: [PATCH 02/31] Fix issue causing the Android editor to crash when creating a new AudioStreamMicrophone Fixes https://github.com/godotengine/godot/issues/73801 (cherry picked from commit 8ca14183f0b6b42cdd6f523461dd3a900461b6a0) --- platform/android/audio_driver_opensl.cpp | 3 +- .../java/editor/src/main/AndroidManifest.xml | 2 + .../org/godotengine/editor/GodotEditor.kt | 17 ++++- .../java/org/godotengine/editor/GodotGame.kt | 5 ++ .../godotengine/editor/GodotProjectManager.kt | 7 +- .../godot/utils/PermissionsUtil.java | 70 +++++++++++++++---- 6 files changed, 87 insertions(+), 17 deletions(-) diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp index 5fc32132e3b..51e89c720d1 100644 --- a/platform/android/audio_driver_opensl.cpp +++ b/platform/android/audio_driver_opensl.cpp @@ -272,7 +272,8 @@ Error AudioDriverOpenSL::input_start() { return init_input_device(); } - return OK; + WARN_PRINT("Unable to start audio capture - No RECORD_AUDIO permission"); + return ERR_UNAUTHORIZED; } Error AudioDriverOpenSL::input_stop() { diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml index 80ef10b6a4b..2bb6b43503f 100644 --- a/platform/android/java/editor/src/main/AndroidManifest.xml +++ b/platform/android/java/editor/src/main/AndroidManifest.xml @@ -21,6 +21,8 @@ + + () override fun onCreate(savedInstanceState: Bundle?) { - PermissionsUtil.requestManifestPermissions(this) + // We exclude certain permissions from the set we request at startup, as they'll be + // requested on demand based on use-cases. + PermissionsUtil.requestManifestPermissions(this, setOf(Manifest.permission.RECORD_AUDIO)) val params = intent.getStringArrayExtra(COMMAND_LINE_PARAMS) updateCommandLineParams(params) @@ -98,6 +100,8 @@ open class GodotEditor : FullScreenGodotApp() { val longPressEnabled = enableLongPressGestures() val panScaleEnabled = enablePanAndScaleGestures() + checkForProjectPermissionsToEnable() + runOnUiThread { // Enable long press, panning and scaling gestures godotFragment?.renderView?.inputHandler?.apply { @@ -107,6 +111,17 @@ open class GodotEditor : FullScreenGodotApp() { } } + /** + * Check for project permissions to enable + */ + protected open fun checkForProjectPermissionsToEnable() { + // Check for RECORD_AUDIO permission + val audioInputEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("audio/driver/enable_input")); + if (audioInputEnabled) { + PermissionsUtil.requestPermission(Manifest.permission.RECORD_AUDIO, this) + } + } + private fun updateCommandLineParams(args: Array?) { // Update the list of command line params with the new args commandLineParams.clear() diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt index 104af0fcff2..aa4d02b5b25 100644 --- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt +++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt @@ -39,4 +39,9 @@ class GodotGame : GodotEditor() { override fun enableLongPressGestures() = false override fun enablePanAndScaleGestures() = false + + override fun checkForProjectPermissionsToEnable() { + // Nothing to do.. by the time we get here, the project permissions will have already + // been requested by the Editor window. + } } diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.kt index 62a9384f2ed..d0e4279eebe 100644 --- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.kt +++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.kt @@ -37,4 +37,9 @@ package org.godotengine.editor * Upon selection of a project, this activity (via its parent logic) starts the * [GodotEditor] activity. */ -class GodotProjectManager : GodotEditor() +class GodotProjectManager : GodotEditor() { + override fun checkForProjectPermissionsToEnable() { + // Nothing to do here.. we have yet to select a project to load. + } +} + diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java index e34c94975be..a94188c405f 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java @@ -42,10 +42,12 @@ import android.os.Environment; import android.provider.Settings; import android.util.Log; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import java.util.ArrayList; import java.util.List; +import java.util.Set; /** * This class includes utility functions for Android permissions related operations. @@ -58,6 +60,7 @@ public final class PermissionsUtil { static final int REQUEST_CAMERA_PERMISSION = 2; static final int REQUEST_VIBRATE_PERMISSION = 3; public static final int REQUEST_ALL_PERMISSION_REQ_CODE = 1001; + public static final int REQUEST_SINGLE_PERMISSION_REQ_CODE = 1002; public static final int REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE = 2002; private PermissionsUtil() { @@ -65,31 +68,57 @@ public final class PermissionsUtil { /** * Request a dangerous permission. name must be specified in this - * @param name the name of the requested permission. + * @param permissionName the name of the requested permission. * @param activity the caller activity for this method. * @return true/false. "true" if permission was granted otherwise returns "false". */ - public static boolean requestPermission(String name, Activity activity) { + public static boolean requestPermission(String permissionName, Activity activity) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // Not necessary, asked on install already return true; } - if (name.equals("RECORD_AUDIO") && ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { - activity.requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, REQUEST_RECORD_AUDIO_PERMISSION); - return false; - } + switch (permissionName) { + case "RECORD_AUDIO": + case Manifest.permission.RECORD_AUDIO: + if (ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + activity.requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, REQUEST_RECORD_AUDIO_PERMISSION); + return false; + } + return true; - if (name.equals("CAMERA") && ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { - activity.requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA_PERMISSION); - return false; - } + case "CAMERA": + case Manifest.permission.CAMERA: + if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + activity.requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA_PERMISSION); + return false; + } + return true; - if (name.equals("VIBRATE") && ContextCompat.checkSelfPermission(activity, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) { - activity.requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION); - return false; + case "VIBRATE": + case Manifest.permission.VIBRATE: + if (ContextCompat.checkSelfPermission(activity, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) { + activity.requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION); + return false; + } + return true; + + default: + // Check if the given permission is a dangerous permission + try { + PermissionInfo permissionInfo = getPermissionInfo(activity, permissionName); + int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel; + if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, permissionName) != PackageManager.PERMISSION_GRANTED) { + activity.requestPermissions(new String[] { permissionName }, REQUEST_SINGLE_PERMISSION_REQ_CODE); + return false; + } + } catch (PackageManager.NameNotFoundException e) { + // Unknown permission - return false as it can't be granted. + Log.w(TAG, "Unable to identify permission " + permissionName, e); + return false; + } + return true; } - return true; } /** @@ -98,6 +127,16 @@ public final class PermissionsUtil { * @return true/false. "true" if all permissions were granted otherwise returns "false". */ public static boolean requestManifestPermissions(Activity activity) { + return requestManifestPermissions(activity, null); + } + + /** + * Request dangerous permissions which are defined in the Android manifest file from the user. + * @param activity the caller activity for this method. + * @param excludes Set of permissions to exclude from the request + * @return true/false. "true" if all permissions were granted otherwise returns "false". + */ + public static boolean requestManifestPermissions(Activity activity, @Nullable Set excludes) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { return true; } @@ -115,6 +154,9 @@ public final class PermissionsUtil { List requestedPermissions = new ArrayList<>(); for (String manifestPermission : manifestPermissions) { + if (excludes != null && excludes.contains(manifestPermission)) { + continue; + } try { if (manifestPermission.equals(Manifest.permission.MANAGE_EXTERNAL_STORAGE)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) { From 1fcf58d72ee19b08ee8db50977e537e30828607c Mon Sep 17 00:00:00 2001 From: Aaron Franke Date: Mon, 7 Nov 2022 20:27:50 -0600 Subject: [PATCH 03/31] Make `Node::get_children()` public (cherry picked from commit 8eb9986dc5ed0fa3a6522401d3db5061d59e9718) --- scene/main/node.cpp | 24 ++++++++++++------------ scene/main/node.h | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 057fd15f1bc..074840f9731 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1251,6 +1251,17 @@ Node *Node::get_child(int p_index, bool p_include_internal) const { } } +TypedArray Node::get_children(bool p_include_internal) const { + TypedArray arr; + int cc = get_child_count(p_include_internal); + arr.resize(cc); + for (int i = 0; i < cc; i++) { + arr[i] = get_child(i, p_include_internal); + } + + return arr; +} + Node *Node::_get_child_by_name(const StringName &p_name) const { int cc = data.children.size(); Node *const *cd = data.children.ptr(); @@ -2660,17 +2671,6 @@ void Node::queue_free() { } } -TypedArray Node::_get_children(bool p_include_internal) const { - TypedArray arr; - int cc = get_child_count(p_include_internal); - arr.resize(cc); - for (int i = 0; i < cc; i++) { - arr[i] = get_child(i, p_include_internal); - } - - return arr; -} - void Node::set_import_path(const NodePath &p_import_path) { #ifdef TOOLS_ENABLED data.import_path = p_import_path; @@ -2824,7 +2824,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_child", "node"), &Node::remove_child); ClassDB::bind_method(D_METHOD("reparent", "new_parent", "keep_global_transform"), &Node::reparent, DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_child_count", "include_internal"), &Node::get_child_count, DEFVAL(false)); // Note that the default value bound for include_internal is false, while the method is declared with true. This is because internal nodes are irrelevant for GDSCript. - ClassDB::bind_method(D_METHOD("get_children", "include_internal"), &Node::_get_children, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_children", "include_internal"), &Node::get_children, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_child", "idx", "include_internal"), &Node::get_child, DEFVAL(false)); ClassDB::bind_method(D_METHOD("has_node", "path"), &Node::has_node); ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_node); diff --git a/scene/main/node.h b/scene/main/node.h index 4a606538a82..810c19d81f4 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -181,7 +181,6 @@ private: void _duplicate_signals(const Node *p_original, Node *p_copy) const; Node *_duplicate(int p_flags, HashMap *r_duplimap = nullptr) const; - TypedArray _get_children(bool p_include_internal = true) const; TypedArray _get_groups() const; Error _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); @@ -311,6 +310,7 @@ public: int get_child_count(bool p_include_internal = true) const; Node *get_child(int p_index, bool p_include_internal = true) const; + TypedArray get_children(bool p_include_internal = true) const; bool has_node(const NodePath &p_path) const; Node *get_node(const NodePath &p_path) const; Node *get_node_or_null(const NodePath &p_path) const; From a18fe8329843fc2ebdafe16907d3b09b669ebd29 Mon Sep 17 00:00:00 2001 From: Lyuma Date: Thu, 20 Jul 2023 12:45:54 +0200 Subject: [PATCH 04/31] Adjust BoneAttachment3D children/meshes during rest fixer Also simplifies equivalent matrix math which previously used ibm_diffs to calculate skinned mesh offsets. (cherry picked from commit 7b71061b3e01b986237f3cf0d0409c93cd8526ba) --- ...post_import_plugin_skeleton_rest_fixer.cpp | 82 ++++++++----------- 1 file changed, 35 insertions(+), 47 deletions(-) diff --git a/editor/import/post_import_plugin_skeleton_rest_fixer.cpp b/editor/import/post_import_plugin_skeleton_rest_fixer.cpp index 6214a2b70d5..82d9cfc2743 100644 --- a/editor/import/post_import_plugin_skeleton_rest_fixer.cpp +++ b/editor/import/post_import_plugin_skeleton_rest_fixer.cpp @@ -31,6 +31,7 @@ #include "post_import_plugin_skeleton_rest_fixer.h" #include "editor/import/scene_import_settings.h" +#include "scene/3d/bone_attachment_3d.h" #include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/skeleton_3d.h" #include "scene/animation/animation_player.h" @@ -105,42 +106,6 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory global_transform.origin = Vector3(); // Translation by a Node is not a bone animation, so the retargeted model should be at the origin. } - // Calc IBM difference. - LocalVector> ibm_diffs; - { - TypedArray nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D"); - while (nodes.size()) { - ImporterMeshInstance3D *mi = Object::cast_to(nodes.pop_back()); - ERR_CONTINUE(!mi); - - Ref skin = mi->get_skin(); - ERR_CONTINUE(!skin.is_valid()); - - Node *node = mi->get_node(mi->get_skeleton_path()); - ERR_CONTINUE(!node); - - Skeleton3D *mesh_skeleton = Object::cast_to(node); - if (!mesh_skeleton || mesh_skeleton != src_skeleton) { - continue; - } - - Vector ibm_diff; - ibm_diff.resize(src_skeleton->get_bone_count()); - Transform3D *ibm_diff_w = ibm_diff.ptrw(); - - int skin_len = skin->get_bind_count(); - for (int i = 0; i < skin_len; i++) { - StringName bn = skin->get_bind_name(i); - int bone_idx = src_skeleton->find_bone(bn); - if (bone_idx >= 0) { - ibm_diff_w[bone_idx] = global_transform * src_skeleton->get_bone_global_rest(bone_idx) * skin->get_bind_pose(i); - } - } - - ibm_diffs.push_back(ibm_diff); - } - } - // Apply node transforms. if (bool(p_options["retarget/rest_fixer/apply_node_transforms"])) { Vector3 scl = global_transform.basis.get_scale_local(); @@ -288,12 +253,11 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory Vector silhouette_diff; // Transform values to be ignored when overwrite axis. silhouette_diff.resize(src_skeleton->get_bone_count()); Transform3D *silhouette_diff_w = silhouette_diff.ptrw(); + LocalVector pre_silhouette_skeleton_global_rest; + for (int i = 0; i < src_skeleton->get_bone_count(); i++) { + pre_silhouette_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i)); + } if (bool(p_options["retarget/rest_fixer/fix_silhouette/enable"])) { - LocalVector old_skeleton_global_rest; - for (int i = 0; i < src_skeleton->get_bone_count(); i++) { - old_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i)); - } - Vector bones_to_process = prof_skeleton->get_parentless_bones(); while (bones_to_process.size() > 0) { int prof_idx = bones_to_process[0]; @@ -450,7 +414,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory // For skin modification in overwrite rest. for (int i = 0; i < src_skeleton->get_bone_count(); i++) { - silhouette_diff_w[i] = old_skeleton_global_rest[i] * src_skeleton->get_bone_global_rest(i).inverse(); + silhouette_diff_w[i] = pre_silhouette_skeleton_global_rest[i] * src_skeleton->get_bone_global_rest(i).affine_inverse(); } is_rest_changed = true; @@ -652,7 +616,9 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory ERR_CONTINUE(!mi); Ref skin = mi->get_skin(); - ERR_CONTINUE(!skin.is_valid()); + if (skin.is_null()) { + continue; + } Node *node = mi->get_node(mi->get_skeleton_path()); ERR_CONTINUE(!node); @@ -662,20 +628,42 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory continue; } - Vector ibm_diff = ibm_diffs[skin_idx]; - int skin_len = skin->get_bind_count(); for (int i = 0; i < skin_len; i++) { StringName bn = skin->get_bind_name(i); int bone_idx = src_skeleton->find_bone(bn); if (bone_idx >= 0) { - Transform3D new_rest = silhouette_diff[bone_idx] * src_skeleton->get_bone_global_rest(bone_idx); - skin->set_bind_pose(i, new_rest.inverse() * ibm_diff[bone_idx]); + Transform3D adjust_transform = src_skeleton->get_bone_global_rest(bone_idx).affine_inverse() * silhouette_diff[bone_idx].affine_inverse() * pre_silhouette_skeleton_global_rest[bone_idx]; + adjust_transform.scale(global_transform.basis.get_scale_local()); + skin->set_bind_pose(i, adjust_transform * skin->get_bind_pose(i)); } } skin_idx++; } + nodes = src_skeleton->get_children(); + while (nodes.size()) { + BoneAttachment3D *attachment = Object::cast_to(nodes.pop_back()); + if (attachment == nullptr) { + continue; + } + int bone_idx = attachment->get_bone_idx(); + if (bone_idx == -1) { + bone_idx = src_skeleton->find_bone(attachment->get_bone_name()); + } + ERR_CONTINUE(bone_idx < 0 || bone_idx >= src_skeleton->get_bone_count()); + Transform3D adjust_transform = src_skeleton->get_bone_global_rest(bone_idx).affine_inverse() * silhouette_diff[bone_idx].affine_inverse() * pre_silhouette_skeleton_global_rest[bone_idx]; + adjust_transform.scale(global_transform.basis.get_scale_local()); + + TypedArray child_nodes = attachment->get_children(); + while (child_nodes.size()) { + Node3D *child = Object::cast_to(child_nodes.pop_back()); + if (child == nullptr) { + continue; + } + child->set_transform(adjust_transform * child->get_transform()); + } + } } // Init skeleton pose to new rest. From 96eb16a3651b7347c0ab8998a915ca261436aa3f Mon Sep 17 00:00:00 2001 From: Lyuma Date: Thu, 20 Jul 2023 12:45:54 +0200 Subject: [PATCH 05/31] Avoid doubly mutating the same Skin in rest fixer Fixes a regression from #77123 that was caused by removal of ibm_diffs variable. This replaced idempotent code with code that applied an offset each time. If the same Skin was visited multiple times, this caused an incorrect result. (cherry picked from commit 5ae311e577b508629712fe25e34b03c6d942e3cb) --- editor/import/post_import_plugin_skeleton_rest_fixer.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/editor/import/post_import_plugin_skeleton_rest_fixer.cpp b/editor/import/post_import_plugin_skeleton_rest_fixer.cpp index 82d9cfc2743..bffc100faf1 100644 --- a/editor/import/post_import_plugin_skeleton_rest_fixer.cpp +++ b/editor/import/post_import_plugin_skeleton_rest_fixer.cpp @@ -609,8 +609,8 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory if (is_rest_changed) { // Fix skin. { + HashSet> mutated_skins; TypedArray nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D"); - int skin_idx = 0; while (nodes.size()) { ImporterMeshInstance3D *mi = Object::cast_to(nodes.pop_back()); ERR_CONTINUE(!mi); @@ -619,6 +619,10 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory if (skin.is_null()) { continue; } + if (mutated_skins.has(skin)) { + continue; + } + mutated_skins.insert(skin); Node *node = mi->get_node(mi->get_skeleton_path()); ERR_CONTINUE(!node); @@ -638,8 +642,6 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory skin->set_bind_pose(i, adjust_transform * skin->get_bind_pose(i)); } } - - skin_idx++; } nodes = src_skeleton->get_children(); while (nodes.size()) { From ca65d85d6ea6452d8bbe36c0c29e68b8b6f9db44 Mon Sep 17 00:00:00 2001 From: Lyuma Date: Thu, 25 May 2023 02:58:17 -0700 Subject: [PATCH 06/31] Fix for SkeletonIK3D interpolation and bone roll Fix bug in internal Basis::rotate_to_align function (also used with identity Basis in scene/resources/curve.cpp) Use ChainItem children rather than local bone rest to determine IK bone roll to match Godot 3.x behavior (cherry picked from commit 9aa46bf3f551296c7a19db03d373fbabebcd09da) --- core/math/basis.cpp | 2 +- scene/3d/skeleton_ik_3d.cpp | 22 +--------------------- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/core/math/basis.cpp b/core/math/basis.cpp index bfd902c7e28..4430ec8d6db 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -397,7 +397,7 @@ void Basis::rotate_to_align(Vector3 p_start_direction, Vector3 p_end_direction) real_t dot = p_start_direction.dot(p_end_direction); dot = CLAMP(dot, -1.0f, 1.0f); const real_t angle_rads = Math::acos(dot); - set_axis_angle(axis, angle_rads); + *this = Basis(axis, angle_rads) * (*this); } } diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index 75f54a925b4..b82620de80b 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -249,26 +249,6 @@ void FabrikInverseKinematic::make_goal(Task *p_task, const Transform3D &p_invers } } -static Vector3 get_bone_axis_forward_vector(Skeleton3D *skeleton, int p_bone) { - // If it is a child/leaf bone... - if (skeleton->get_bone_parent(p_bone) > 0) { - return skeleton->get_bone_rest(p_bone).origin.normalized(); - } - // If it has children... - Vector child_bones = skeleton->get_bone_children(p_bone); - if (child_bones.size() == 0) { - WARN_PRINT_ONCE("Cannot calculate forward direction for bone " + itos(p_bone)); - WARN_PRINT_ONCE("Assuming direction of (0, 1, 0) for bone"); - return Vector3(0, 1, 0); - } - Vector3 combined_child_dir = Vector3(0, 0, 0); - for (int i = 0; i < child_bones.size(); i++) { - combined_child_dir += skeleton->get_bone_rest(child_bones[i]).origin.normalized(); - } - combined_child_dir = combined_child_dir / child_bones.size(); - return combined_child_dir.normalized(); -} - void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position) { if (blending_delta <= 0.01f) { // Before skipping, make sure we undo the global pose overrides @@ -307,7 +287,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove new_bone_pose.origin = ci->current_pos; if (!ci->children.is_empty()) { - Vector3 forward_vector = get_bone_axis_forward_vector(p_task->skeleton, ci->bone); + Vector3 forward_vector = (ci->children[0].initial_transform.origin - ci->initial_transform.origin).normalized(); // Rotate the bone towards the next bone in the chain: new_bone_pose.basis.rotate_to_align(forward_vector, new_bone_pose.origin.direction_to(ci->children[0].current_pos)); From 9acd4cfdfc643a200fbd55209d805e0376c2ac34 Mon Sep 17 00:00:00 2001 From: kobewi Date: Tue, 6 Jun 2023 14:42:27 +0200 Subject: [PATCH 07/31] Draw materials in tile atlas view (cherry picked from commit 16ac217aa0a5a45881639a5446158ab4bd895bbc) --- editor/plugins/tiles/tile_atlas_view.cpp | 43 ++++++++++++++++++++++-- editor/plugins/tiles/tile_atlas_view.h | 5 +++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index 2b42e41e733..46056c011ee 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -244,13 +244,16 @@ void TileAtlasView::_draw_base_tiles() { for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i); + // Different materials need to be drawn with different CanvasItems. + RID ci_rid = _get_canvas_item_to_draw(tile_set_atlas_source->get_tile_data(atlas_coords, 0), base_tiles_draw, material_tiles_draw); + for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) { // Update the y to max value. Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame); Vector2 offset_pos = Rect2(base_frame_rect).get_center() + Vector2(tile_set_atlas_source->get_tile_data(atlas_coords, 0)->get_texture_origin()); // Draw the tile. - TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0, frame); + TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, 0, frame); } } @@ -286,6 +289,33 @@ void TileAtlasView::_draw_base_tiles() { } } +RID TileAtlasView::_get_canvas_item_to_draw(const TileData *p_for_data, const CanvasItem *p_base_item, HashMap, RID> &p_material_map) { + Ref mat = p_for_data->get_material(); + if (mat.is_null()) { + return p_base_item->get_canvas_item(); + } else if (p_material_map.has(mat)) { + return p_material_map[mat]; + } else { + RID ci_rid = RS::get_singleton()->canvas_item_create(); + RS::get_singleton()->canvas_item_set_parent(ci_rid, p_base_item->get_canvas_item()); + RS::get_singleton()->canvas_item_set_material(ci_rid, mat->get_rid()); + p_material_map[mat] = ci_rid; + return ci_rid; + } +} + +void TileAtlasView::_clear_material_canvas_items() { + for (KeyValue, RID> kv : material_tiles_draw) { + RS::get_singleton()->free(kv.value); + } + material_tiles_draw.clear(); + + for (KeyValue, RID> kv : material_alternatives_draw) { + RS::get_singleton()->free(kv.value); + } + material_alternatives_draw.clear(); +} + void TileAtlasView::_draw_base_tiles_texture_grid() { Ref texture = tile_set_atlas_source->get_texture(); if (texture.is_valid()) { @@ -370,6 +400,9 @@ void TileAtlasView::_draw_alternatives() { TileData *tile_data = tile_set_atlas_source->get_tile_data(atlas_coords, alternative_id); bool transposed = tile_data->get_transpose(); + // Different materials need to be drawn with different CanvasItems. + RID ci_rid = _get_canvas_item_to_draw(tile_data, alternatives_draw, material_alternatives_draw); + // Update the y to max value. Vector2i offset_pos; if (transposed) { @@ -381,7 +414,7 @@ void TileAtlasView::_draw_alternatives() { } // Draw the tile. - TileMap::draw_tile(alternatives_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, alternative_id); + TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, alternative_id); // Increment the x position. current_pos.x += transposed ? texture_region_size.y : texture_region_size.x; @@ -407,6 +440,8 @@ void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_ tile_set = p_tile_set; tile_set_atlas_source = p_tile_set_atlas_source; + _clear_material_canvas_items(); + if (!tile_set) { return; } @@ -690,3 +725,7 @@ TileAtlasView::TileAtlasView() { alternatives_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_alternatives)); alternative_tiles_drawing_root->add_child(alternatives_draw); } + +TileAtlasView::~TileAtlasView() { + _clear_material_canvas_items(); +} diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h index 4a7547f34bc..81d563525ed 100644 --- a/editor/plugins/tiles/tile_atlas_view.h +++ b/editor/plugins/tiles/tile_atlas_view.h @@ -89,7 +89,11 @@ private: Control *base_tiles_drawing_root = nullptr; Control *base_tiles_draw = nullptr; + HashMap, RID> material_tiles_draw; + HashMap, RID> material_alternatives_draw; void _draw_base_tiles(); + RID _get_canvas_item_to_draw(const TileData *p_for_data, const CanvasItem *p_base_item, HashMap, RID> &p_material_map); + void _clear_material_canvas_items(); Control *base_tiles_texture_grid = nullptr; void _draw_base_tiles_texture_grid(); @@ -157,6 +161,7 @@ public: void queue_redraw(); TileAtlasView(); + ~TileAtlasView(); }; #endif // TILE_ATLAS_VIEW_H From 33618f25fdd3b821081d85dd8a243d1ee69af840 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Wed, 7 Jun 2023 09:02:04 +0300 Subject: [PATCH 08/31] Fix SVG font rendering after ThorVG update. (cherry picked from commit 35b035457702d4db9163e6b79f4b17d5d1b39965) --- modules/text_server_adv/thorvg_svg_in_ot.cpp | 2 +- modules/text_server_fb/thorvg_svg_in_ot.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/text_server_adv/thorvg_svg_in_ot.cpp b/modules/text_server_adv/thorvg_svg_in_ot.cpp index 4fd4c7c1f69..a1a88c9a637 100644 --- a/modules/text_server_adv/thorvg_svg_in_ot.cpp +++ b/modules/text_server_adv/thorvg_svg_in_ot.cpp @@ -126,7 +126,7 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin xml_body += vformat("", parser->get_node_name()); } } - String temp_xml_str = "" + xml_body; + String temp_xml_str = "" + xml_body; CharString temp_xml = temp_xml_str.utf8(); std::unique_ptr picture = tvg::Picture::gen(); diff --git a/modules/text_server_fb/thorvg_svg_in_ot.cpp b/modules/text_server_fb/thorvg_svg_in_ot.cpp index 4fd4c7c1f69..a1a88c9a637 100644 --- a/modules/text_server_fb/thorvg_svg_in_ot.cpp +++ b/modules/text_server_fb/thorvg_svg_in_ot.cpp @@ -126,7 +126,7 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin xml_body += vformat("", parser->get_node_name()); } } - String temp_xml_str = "" + xml_body; + String temp_xml_str = "" + xml_body; CharString temp_xml = temp_xml_str.utf8(); std::unique_ptr picture = tvg::Picture::gen(); From c683fa99714e5c271febdbc595d1363960e36eb5 Mon Sep 17 00:00:00 2001 From: Ninni Pipping Date: Thu, 23 Mar 2023 16:19:20 +0100 Subject: [PATCH 09/31] Fix trim when importing WAV (cherry picked from commit ff127ba57e81a9c4679b2cb036b20e37d0954e0b) --- editor/import/resource_importer_wav.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp index 6d54c08031c..f3cb5bda323 100644 --- a/editor/import/resource_importer_wav.cpp +++ b/editor/import/resource_importer_wav.cpp @@ -386,7 +386,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s bool trim = p_options["edit/trim"]; - if (trim && (loop_mode != AudioStreamWAV::LOOP_DISABLED) && format_channels > 0) { + if (trim && (loop_mode == AudioStreamWAV::LOOP_DISABLED) && format_channels > 0) { int first = 0; int last = (frames / format_channels) - 1; bool found = false; From 131ce4acf6c96b6aaeb114e928e292b8f276cd5f Mon Sep 17 00:00:00 2001 From: Ninni Pipping Date: Wed, 15 Mar 2023 19:32:06 +0100 Subject: [PATCH 10/31] Fix type check in AnimationTrackKeyEdit for methods (cherry picked from commit ad769903b04f7c68aebc3aafb3addc41360bf64a) --- editor/animation_track_editor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index f86d239a0f5..89b9bf9ee3e 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -201,7 +201,7 @@ bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_valu if (t != args[idx].get_type()) { Callable::CallError err; - if (Variant::can_convert(args[idx].get_type(), t)) { + if (Variant::can_convert_strict(args[idx].get_type(), t)) { Variant old = args[idx]; Variant *ptrs[1] = { &old }; Variant::construct(t, args.write[idx], (const Variant **)ptrs, 1, err); @@ -786,7 +786,7 @@ bool AnimationMultiTrackKeyEdit::_set(const StringName &p_name, const Variant &p if (t != args[idx].get_type()) { Callable::CallError err; - if (Variant::can_convert(args[idx].get_type(), t)) { + if (Variant::can_convert_strict(args[idx].get_type(), t)) { Variant old = args[idx]; Variant *ptrs[1] = { &old }; Variant::construct(t, args.write[idx], (const Variant **)ptrs, 1, err); From 7f8de2df4c073330b2b755e46f9d5aa8880327cd Mon Sep 17 00:00:00 2001 From: Raffaele Picca Date: Sat, 27 Nov 2021 21:04:31 +0100 Subject: [PATCH 11/31] Correctly reset particle size and rotation in ParticlesProcessMaterial Co-authored-by: clayjhn (cherry picked from commit 353a4e1e0935f8323be68b26fb9853fbe0b2425f) --- scene/resources/particle_process_material.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp index 41edbcb726b..e22952ac56f 100644 --- a/scene/resources/particle_process_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -474,14 +474,20 @@ void ParticleProcessMaterial::_update_shader() { code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle code += " CUSTOM.y = 0.0;\n"; // phase code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n"; - code += " CUSTOM.z = (tex_anim_offset) * mix(anim_offset_min, anim_offset_max, anim_offset_rand);\n"; // animation offset (0-1) + code += " CUSTOM.z = (tex_anim_offset) * mix(anim_offset_min, anim_offset_max, anim_offset_rand);\n\n"; // animation offset (0-1) + + code += " if (RESTART_ROT_SCALE) {\n"; + code += " TRANSFORM[0].xyz = vec3(1.0, 0.0, 0.0);\n"; + code += " TRANSFORM[1].xyz = vec3(0.0, 1.0, 0.0);\n"; + code += " TRANSFORM[2].xyz = vec3(0.0, 0.0, 1.0);\n"; + code += " }\n\n"; code += " if (RESTART_POSITION) {\n"; switch (emission_shape) { case EMISSION_SHAPE_POINT: { //do none, identity (will later be multiplied by emission transform) - code += " TRANSFORM = mat4(vec4(1,0,0,0),vec4(0,1,0,0),vec4(0,0,1,0),vec4(0,0,0,1));\n"; + code += " TRANSFORM[3].xyz = vec3(0.0, 0.0, 0.0);\n"; } break; case EMISSION_SHAPE_SPHERE: { code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n"; From 0f2367969981100b1ac76629ee527440bc4ab565 Mon Sep 17 00:00:00 2001 From: Devan OBoyle Date: Thu, 1 Jun 2023 13:15:27 -0700 Subject: [PATCH 12/31] Skip error messages for buttons that don't exist (cherry picked from commit ed02d515e049488f5416f45a038756590718df2c) --- core/input/input.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/input/input.cpp b/core/input/input.cpp index 2b3e0b56e4f..5e8dd17465a 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -1354,8 +1354,9 @@ void Input::parse_mapping(String p_mapping) { String output = entry[idx].get_slice(":", 0).replace(" ", ""); String input = entry[idx].get_slice(":", 1).replace(" ", ""); - ERR_CONTINUE_MSG(output.length() < 1 || input.length() < 2, - vformat("Invalid device mapping entry \"%s\" in mapping:\n%s", entry[idx], p_mapping)); + if (output.length() < 1 || input.length() < 2) { + continue; + } if (output == "platform" || output == "hint") { continue; From fa8e3b200ebaffa8656ddc3fd98f9ca8fd71caf3 Mon Sep 17 00:00:00 2001 From: pattlebass <49322676+pattlebass@users.noreply.github.com> Date: Mon, 2 Jan 2023 15:15:36 +0200 Subject: [PATCH 13/31] Fix Range-derived nodes not redrawing When using set_value_no_signal(), Range-derived nodes wouldn't redraw. Also added a dedicated method to SpinBox to update its text. (cherry picked from commit 9500f8e69ae798c070c4daca9c46beaf8db18bd4) --- scene/gui/range.cpp | 23 +++++++++++++++++++++-- scene/gui/range.h | 2 ++ scene/gui/spin_box.cpp | 19 +++++++++++-------- scene/gui/spin_box.h | 2 +- 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp index bd081074ecb..97a1483204c 100644 --- a/scene/gui/range.cpp +++ b/scene/gui/range.cpp @@ -74,16 +74,26 @@ void Range::Shared::emit_changed(const char *p_what) { } } +void Range::Shared::redraw_owners() { + for (Range *E : owners) { + Range *r = E; + if (!r->is_inside_tree()) { + continue; + } + r->queue_redraw(); + } +} + void Range::set_value(double p_val) { double prev_val = shared->val; - set_value_no_signal(p_val); + _set_value_no_signal(p_val); if (shared->val != prev_val) { shared->emit_value_changed(); } } -void Range::set_value_no_signal(double p_val) { +void Range::_set_value_no_signal(double p_val) { if (shared->step > 0) { p_val = Math::round((p_val - shared->min) / shared->step) * shared->step + shared->min; } @@ -107,6 +117,15 @@ void Range::set_value_no_signal(double p_val) { shared->val = p_val; } +void Range::set_value_no_signal(double p_val) { + double prev_val = shared->val; + _set_value_no_signal(p_val); + + if (shared->val != prev_val) { + shared->redraw_owners(); + } +} + void Range::set_min(double p_min) { if (shared->min == p_min) { return; diff --git a/scene/gui/range.h b/scene/gui/range.h index bf71fcc1c96..9b4f0707e6f 100644 --- a/scene/gui/range.h +++ b/scene/gui/range.h @@ -48,6 +48,7 @@ class Range : public Control { HashSet owners; void emit_value_changed(); void emit_changed(const char *p_what = ""); + void redraw_owners(); }; Shared *shared = nullptr; @@ -59,6 +60,7 @@ class Range : public Control { void _value_changed_notify(); void _changed_notify(const char *p_what = ""); + void _set_value_no_signal(double p_val); protected: virtual void _value_changed(double p_value); diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index f99b2edd542..e4c7be33f0e 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -39,7 +39,7 @@ Size2 SpinBox::get_minimum_size() const { return ms; } -void SpinBox::_value_changed(double p_value) { +void SpinBox::_update_text() { String value = String::num(get_value(), Math::range_step_decimals(get_step())); if (is_localizing_numeral_system()) { value = TS->format_number(value); @@ -55,7 +55,6 @@ void SpinBox::_value_changed(double p_value) { } line_edit->set_text(value); - Range::_value_changed(p_value); } void SpinBox::_text_submitted(const String &p_string) { @@ -73,7 +72,7 @@ void SpinBox::_text_submitted(const String &p_string) { if (value.get_type() != Variant::NIL) { set_value(value); } - _value_changed(0); + _update_text(); } void SpinBox::_text_changed(const String &p_string) { @@ -192,7 +191,7 @@ void SpinBox::gui_input(const Ref &p_event) { void SpinBox::_line_edit_focus_enter() { int col = line_edit->get_caret_column(); - _value_changed(0); // Update the LineEdit's text. + _update_text(); line_edit->set_caret_column(col); // LineEdit text might change and it clears any selection. Have to re-select here. @@ -202,6 +201,10 @@ void SpinBox::_line_edit_focus_enter() { } void SpinBox::_line_edit_focus_exit() { + // Discontinue because the focus_exit was caused by left-clicking the arrows. + if (get_viewport()->gui_get_focus_owner() == get_line_edit()) { + return; + } // Discontinue because the focus_exit was caused by right-click context menu. if (line_edit->is_menu_visible()) { return; @@ -228,6 +231,7 @@ void SpinBox::_update_theme_item_cache() { void SpinBox::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { + _update_text(); _adjust_width_for_icon(theme_cache.updown_icon); RID ci = get_canvas_item(); @@ -242,7 +246,7 @@ void SpinBox::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { _adjust_width_for_icon(theme_cache.updown_icon); - _value_changed(0); + _update_text(); } break; case NOTIFICATION_EXIT_TREE: { @@ -250,7 +254,6 @@ void SpinBox::_notification(int p_what) { } break; case NOTIFICATION_TRANSLATION_CHANGED: { - _value_changed(0); queue_redraw(); } break; @@ -279,7 +282,7 @@ void SpinBox::set_suffix(const String &p_suffix) { } suffix = p_suffix; - _value_changed(0); + _update_text(); } String SpinBox::get_suffix() const { @@ -292,7 +295,7 @@ void SpinBox::set_prefix(const String &p_prefix) { } prefix = p_prefix; - _value_changed(0); + _update_text(); } String SpinBox::get_prefix() const { diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index 0e9d424f687..29b278c50ee 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -46,8 +46,8 @@ class SpinBox : public Range { void _range_click_timeout(); void _release_mouse(); + void _update_text(); void _text_submitted(const String &p_string); - virtual void _value_changed(double p_value) override; void _text_changed(const String &p_string); String prefix; From 39f9c8cd29b11a9f13def38d4837878ef8a4549f Mon Sep 17 00:00:00 2001 From: kobewi Date: Sat, 10 Jun 2023 21:29:24 +0200 Subject: [PATCH 14/31] Preserve selection when focusing SpinBox (cherry picked from commit 968c5f6247b51691474bd1cf0cc9d0a34735a6f0) --- scene/gui/line_edit.cpp | 16 ++++++++++++++++ scene/gui/line_edit.h | 1 + scene/gui/spin_box.cpp | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 17cc81c8d34..472a1bdc6ba 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -1527,6 +1527,22 @@ void LineEdit::set_text(String p_text) { scroll_offset = 0.0; } +void LineEdit::set_text_with_selection(const String &p_text) { + Selection selection_copy = selection; + + clear_internal(); + insert_text_at_caret(p_text); + _create_undo_state(); + + int tlen = text.length(); + selection = selection_copy; + selection.begin = MIN(selection.begin, tlen); + selection.end = MIN(selection.end, tlen); + selection.start_column = MIN(selection.start_column, tlen); + + queue_redraw(); +} + void LineEdit::set_text_direction(Control::TextDirection p_text_direction) { ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); if (text_direction != p_text_direction) { diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 81c506069ac..954e565793f 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -282,6 +282,7 @@ public: void set_text(String p_text); String get_text() const; + void set_text_with_selection(const String &p_text); // Set text, while preserving selection. void set_text_direction(TextDirection p_text_direction); TextDirection get_text_direction() const; diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index e4c7be33f0e..f813c6d6608 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -54,7 +54,7 @@ void SpinBox::_update_text() { } } - line_edit->set_text(value); + line_edit->set_text_with_selection(value); } void SpinBox::_text_submitted(const String &p_string) { From 31e582b970b46fc7bc568a05159a8a34403c09b8 Mon Sep 17 00:00:00 2001 From: RedworkDE <10944644+RedworkDE@users.noreply.github.com> Date: Tue, 30 May 2023 20:19:08 +0200 Subject: [PATCH 15/31] Fix crash when selecting lines in text edit (cherry picked from commit 6b7008b73beca0df5722cd4434dc4ac4ad7c99fe) --- scene/gui/text_edit.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 9eee5177800..3bf599e25c6 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -7153,7 +7153,9 @@ void TextEdit::_update_selection_mode_line() { if (line < carets[caret_idx].selection.selecting_line) { /* Caret is above us. */ set_caret_line(line - 1, false, true, 0, caret_idx); - carets.write[caret_idx].selection.selecting_column = text[get_selection_line(caret_idx)].length(); + carets.write[caret_idx].selection.selecting_column = has_selection(caret_idx) + ? text[get_selection_line(caret_idx)].length() + : 0; } else { /* Caret is below us. */ set_caret_line(line + 1, false, true, 0, caret_idx); From 5a82eadd2d95f4312f7d9bbcb9cf307d00f3eb96 Mon Sep 17 00:00:00 2001 From: kobewi Date: Fri, 7 Apr 2023 23:34:52 +0200 Subject: [PATCH 16/31] Make sure script cache is created after reimport (cherry picked from commit 680ed7f612b0a448aab6d574cbd68124ea0c9e55) --- editor/editor_file_system.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 0807d457cbc..75437772f9a 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -2328,6 +2328,7 @@ void EditorFileSystem::reimport_files(const Vector &p_files) { ResourceUID::get_singleton()->update_cache(); // After reimporting, update the cache. _save_filesystem_cache(); + _update_pending_script_classes(); importing = false; if (!is_scanning()) { emit_signal(SNAME("filesystem_changed")); From f9a0ac507ceb886056ddc4a137094e8066defcc3 Mon Sep 17 00:00:00 2001 From: Yuri Sizov Date: Mon, 12 Jun 2023 18:18:51 +0200 Subject: [PATCH 17/31] Avoid error spam when (un)pausing GPUParticles out of tree (cherry picked from commit 991f4d51832b71753c30a289b3b2d4d9a019f7fc) --- scene/2d/gpu_particles_2d.cpp | 10 ++++++---- scene/3d/gpu_particles_3d.cpp | 20 +++++++++++--------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index 08c7b8e131a..9fd41ff227f 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -551,10 +551,12 @@ void GPUParticles2D::_notification(int p_what) { case NOTIFICATION_PAUSED: case NOTIFICATION_UNPAUSED: { - if (can_process()) { - RS::get_singleton()->particles_set_speed_scale(particles, speed_scale); - } else { - RS::get_singleton()->particles_set_speed_scale(particles, 0); + if (is_inside_tree()) { + if (can_process()) { + RS::get_singleton()->particles_set_speed_scale(particles, speed_scale); + } else { + RS::get_singleton()->particles_set_speed_scale(particles, 0); + } } } break; diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index b4131ff4b27..c5ec3ef453c 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -417,15 +417,6 @@ NodePath GPUParticles3D::get_sub_emitter() const { void GPUParticles3D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_PAUSED: - case NOTIFICATION_UNPAUSED: { - if (can_process()) { - RS::get_singleton()->particles_set_speed_scale(particles, speed_scale); - } else { - RS::get_singleton()->particles_set_speed_scale(particles, 0); - } - } break; - // Use internal process when emitting and one_shot is on so that when // the shot ends the editor can properly update. case NOTIFICATION_INTERNAL_PROCESS: { @@ -450,6 +441,17 @@ void GPUParticles3D::_notification(int p_what) { RS::get_singleton()->particles_set_subemitter(particles, RID()); } break; + case NOTIFICATION_PAUSED: + case NOTIFICATION_UNPAUSED: { + if (is_inside_tree()) { + if (can_process()) { + RS::get_singleton()->particles_set_speed_scale(particles, speed_scale); + } else { + RS::get_singleton()->particles_set_speed_scale(particles, 0); + } + } + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { // Make sure particles are updated before rendering occurs if they were active before. if (is_visible_in_tree() && !RS::get_singleton()->particles_is_inactive(particles)) { From 22d3fa72926b60eb7f32c2b5fa4cff32c1f69281 Mon Sep 17 00:00:00 2001 From: Haoyu Qiu Date: Tue, 13 Jun 2023 11:31:53 +0800 Subject: [PATCH 18/31] Fix crash when opening a TileSet with invalid tiles (cherry picked from commit 8b5d5dc2c4da38614dd73a6a53a476ce3170ed71) --- editor/plugins/tiles/tiles_editor_plugin.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp index 1682d07e13a..9e065b689bf 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.cpp +++ b/editor/plugins/tiles/tiles_editor_plugin.cpp @@ -106,9 +106,11 @@ void TilesEditorPlugin::_thread() { Vector2i coords = tile_map->get_cell_atlas_coords(0, cell); int alternative = tile_map->get_cell_alternative_tile(0, cell); - Vector2 center = world_pos - atlas_source->get_tile_data(coords, alternative)->get_texture_origin(); - encompassing_rect.expand_to(center - atlas_source->get_tile_texture_region(coords).size / 2); - encompassing_rect.expand_to(center + atlas_source->get_tile_texture_region(coords).size / 2); + if (atlas_source->has_tile(coords) && atlas_source->has_alternative_tile(coords, alternative)) { + Vector2 center = world_pos - atlas_source->get_tile_data(coords, alternative)->get_texture_origin(); + encompassing_rect.expand_to(center - atlas_source->get_tile_texture_region(coords).size / 2); + encompassing_rect.expand_to(center + atlas_source->get_tile_texture_region(coords).size / 2); + } } } From 627ddd412e7a18aea45eef41236eec0b0a2d9d6e Mon Sep 17 00:00:00 2001 From: kobewi Date: Fri, 16 Jun 2023 13:12:56 +0200 Subject: [PATCH 19/31] Fix filesystem cache split error (cherry picked from commit 7f18db9d9d444885005df2edebf7632070fe9e55) --- editor/editor_file_system.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 75437772f9a..97c2eee0ea9 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -262,7 +262,7 @@ void EditorFileSystem::_scan_filesystem() { } else { Vector split = l.split("::"); - ERR_CONTINUE(split.size() != 9); + ERR_CONTINUE(split.size() < 9); String name = split[0]; String file; From 23c1eae8b62d01cb858d93f2791dae0732acd11d Mon Sep 17 00:00:00 2001 From: Phil Hudson <1914950+phil-hudson@users.noreply.github.com> Date: Tue, 13 Jun 2023 18:52:57 +0800 Subject: [PATCH 20/31] fix(android): set pending intent flag to stop insta-crash fix(android): add conditional check for minimum sdk version fix(android): formatting fix(android): formatting fix(android): formatting fix(android): formatting again (cherry picked from commit ce7f648694300a34747c5bab665f7d7f5e51ff61) --- .../java/lib/src/org/godotengine/godot/Godot.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 9b65a52b70d..9902efbdf29 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -685,8 +685,14 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC Intent notifierIntent = new Intent(activity, activity.getClass()); notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - PendingIntent pendingIntent = PendingIntent.getActivity(activity, 0, - notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent pendingIntent; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + pendingIntent = PendingIntent.getActivity(activity, 0, + notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + } else { + pendingIntent = PendingIntent.getActivity(activity, 0, + notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT); + } int startResult; try { From 52f583fdee1130db349521fe440ffd9075da8598 Mon Sep 17 00:00:00 2001 From: MewPurPur Date: Thu, 22 Jun 2023 16:06:20 +0200 Subject: [PATCH 21/31] Document ShaderInclude (cherry picked from commit da84efc96d81e19deddb801eafc92a6295be5eba) --- doc/classes/ShaderInclude.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/classes/ShaderInclude.xml b/doc/classes/ShaderInclude.xml index 40072a933b6..483da7dc2f5 100644 --- a/doc/classes/ShaderInclude.xml +++ b/doc/classes/ShaderInclude.xml @@ -1,13 +1,17 @@ + A snippet of shader code to be included in a [Shader] with [code]#include[/code]. + A shader include file, saved with the [code].gdshaderinc[/code] extension. This class allows you to define a custom shader snippet that can be included in a [Shader] by using the preprocessor directive [code]#include[/code], followed by the file path (e.g. [code]#include "res://shader_lib.gdshaderinc"[/code]). The snippet doesn't have to be a valid shader on its own. + $DOCS_URL/tutorials/shaders/shader_reference/shader_preprocessor.html + Returns the code of the shader include file. The returned text is what the user has written, not the full generated code used internally. From dd2907f9b7c44c8f4d6bef9e3579e1cff41611e2 Mon Sep 17 00:00:00 2001 From: jpcerrone Date: Wed, 14 Jun 2023 14:34:09 -0300 Subject: [PATCH 22/31] Fix for Win+M crashing the editor Fixes #77790 Adds missing 'break' statement to 'VulkanContext::prepare_buffers' function. It was mistakenly removed in #72859 (cherry picked from commit bd786ce0d9951deb547238b7ef75583c7b840ae4) --- drivers/vulkan/vulkan_context.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index f65056951d0..3feed2b1095 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -2276,6 +2276,7 @@ Error VulkanContext::prepare_buffers() { // presentation engine will still present the image correctly. print_verbose("Vulkan: Early suboptimal swapchain, recreating."); _update_swap_chain(w); + break; } else if (err != VK_SUCCESS) { ERR_BREAK_MSG(err != VK_SUCCESS, "Vulkan: Did not create swapchain successfully. Error code: " + String(string_VkResult(err))); } else { From 7a59ef60ee8bbc70831d523bed53ba4b0419cc35 Mon Sep 17 00:00:00 2001 From: kobewi Date: Wed, 28 Jun 2023 15:11:10 +0200 Subject: [PATCH 23/31] Fix crash with failed compatibility tiles (cherry picked from commit 1a9739f591a2cc7ee4ca8d4b90b5e20c6abaf390) --- scene/resources/tile_set.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index efe04ce4e1b..2d117a467bf 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -2590,6 +2590,7 @@ void TileSet::_compatibility_conversion() { compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_SINGLE_TILE; TileData *tile_data = atlas_source->get_tile_data(coords, alternative_tile); + ERR_CONTINUE(!tile_data); tile_data->set_flip_h(flip_h); tile_data->set_flip_v(flip_v); From e862643007fe1cd9c0a1f0b04b457e2b6b040664 Mon Sep 17 00:00:00 2001 From: Haoyu Qiu Date: Wed, 28 Jun 2023 15:27:38 +0800 Subject: [PATCH 24/31] Fix function signature in EditorImportPlugin example (cherry picked from commit cea00ebf58e6b8dfbc968e978bf130dbdc92f1eb) --- doc/classes/EditorImportPlugin.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml index 15cb18a5995..25424b26db2 100644 --- a/doc/classes/EditorImportPlugin.xml +++ b/doc/classes/EditorImportPlugin.xml @@ -30,10 +30,10 @@ func _get_preset_count(): return 1 - func _get_preset_name(i): + func _get_preset_name(preset_index): return "Default" - func _get_import_options(i): + func _get_import_options(path, preset_index): return [{"name": "my_option", "default_value": false}] func _import(source_file, save_path, options, platform_variants, gen_files): From de57ac9227c6b347ad26fadfbe15ad322ad4894e Mon Sep 17 00:00:00 2001 From: Ninni Pipping Date: Mon, 26 Jun 2023 08:48:47 +0200 Subject: [PATCH 25/31] Fix documentation for `Mutex.try_lock` Documentation was not updated when return type was changed from `Error` to `bool` (cherry picked from commit 40e2168ac67a7fcd8cbab50de51ecf45d7ca570d) --- doc/classes/Mutex.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/classes/Mutex.xml b/doc/classes/Mutex.xml index aa298e15aff..9fe677aeec2 100644 --- a/doc/classes/Mutex.xml +++ b/doc/classes/Mutex.xml @@ -22,7 +22,7 @@ Tries locking this [Mutex], but does not block. Returns [code]true[/code] on success, [code]false[/code] otherwise. - [b]Note:[/b] This function returns [constant OK] if the thread already has ownership of the mutex. + [b]Note:[/b] This function returns [code]true[/code] if the thread already has ownership of the mutex. From b3c74b48744f03291cab13b17d0f59af334d6a41 Mon Sep 17 00:00:00 2001 From: Haoyu Qiu Date: Sun, 2 Jul 2023 16:48:37 +0800 Subject: [PATCH 26/31] Translate "No match" message in FindReplaceBar (cherry picked from commit ac454ce2a75fb4a10ea23681d450e4d7605ba050) --- editor/code_editor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 77fb936043c..5f52867d5bf 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -408,7 +408,7 @@ void FindReplaceBar::_update_matches_label() { matches_label->add_theme_color_override("font_color", results_count > 0 ? get_theme_color(SNAME("font_color"), SNAME("Label")) : get_theme_color(SNAME("error_color"), SNAME("Editor"))); if (results_count == 0) { - matches_label->set_text("No match"); + matches_label->set_text(TTR("No match")); } else if (results_count_to_current == -1) { matches_label->set_text(vformat(TTRN("%d match", "%d matches", results_count), results_count)); } else { From 82ea4b47180a78e4a15b3d54010169eea75493f3 Mon Sep 17 00:00:00 2001 From: Amir-Rasteg <127231771+Amir-Rasteg@users.noreply.github.com> Date: Mon, 3 Jul 2023 12:09:19 -0400 Subject: [PATCH 27/31] Fix a typo in the `String.to_float` description (cherry picked from commit 9744657bb8410ecfaa462a2c575a68c9b40cfbad) --- doc/classes/String.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 7bc44ea26d2..00870793325 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -884,11 +884,11 @@ Converts the string representing a decimal number into a [float]. This method stops on the first non-number character, except the first decimal point ([code].[/code]) and the exponent letter ([code]e[/code]). See also [method is_valid_float]. [codeblock] - var a = "12.35".to_float() # a is 12.35 - var b = "1.2.3".to_float() # b is 1.2 - var c = "12xy3".to_float() # c is 12.0 - var d = "1e3".to_float() # d is 1000.0 - var e = "Hello!".to_int() # e is 0.0 + var a = "12.35".to_float() # a is 12.35 + var b = "1.2.3".to_float() # b is 1.2 + var c = "12xy3".to_float() # c is 12.0 + var d = "1e3".to_float() # d is 1000.0 + var e = "Hello!".to_float() # e is 0.0 [/codeblock] From 07ec20b8fabc9e366d2ebf1af8ad3d2698cf4ec3 Mon Sep 17 00:00:00 2001 From: NiskashY Date: Wed, 28 Jun 2023 02:32:27 +0300 Subject: [PATCH 28/31] Fix disabled slider highlighting (cherry picked from commit 4394936392802fb48fd6ccddc385bedd67aa9b31) --- scene/gui/slider.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index 341b2363dd3..ab650881c6c 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -192,7 +192,7 @@ void Slider::_notification(int p_what) { Ref style = theme_cache.slider_style; Ref tick = theme_cache.tick_icon; - bool highlighted = mouse_inside || has_focus(); + bool highlighted = editable && (mouse_inside || has_focus()); Ref grabber; if (editable) { if (highlighted) { From 96a480feac71d66fb0e0e74efb99fac0096c001d Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Thu, 13 Jul 2023 16:32:40 +0200 Subject: [PATCH 29/31] [Net] Explicitly handle buffer errors in send/recv (cherry picked from commit 28001b9ef642d82df2fc36989b1d7b52c7b7ccba) --- drivers/unix/net_socket_posix.cpp | 20 ++++++++++++++++++++ drivers/unix/net_socket_posix.h | 1 + 2 files changed, 21 insertions(+) diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp index b46af012f11..a8074aa3f67 100644 --- a/drivers/unix/net_socket_posix.cpp +++ b/drivers/unix/net_socket_posix.cpp @@ -204,6 +204,9 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const { if (err == WSAEACCES) { return ERR_NET_UNAUTHORIZED; } + if (err == WSAEMSGSIZE || err == WSAENOBUFS) { + return ERR_NET_BUFFER_TOO_SMALL; + } print_verbose("Socket error: " + itos(err)); return ERR_NET_OTHER; #else @@ -222,6 +225,9 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const { if (errno == EACCES) { return ERR_NET_UNAUTHORIZED; } + if (errno == ENOBUFS) { + return ERR_NET_BUFFER_TOO_SMALL; + } print_verbose("Socket error: " + itos(errno)); return ERR_NET_OTHER; #endif @@ -550,6 +556,10 @@ Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) { return ERR_BUSY; } + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } + return FAILED; } @@ -571,6 +581,10 @@ Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddr return ERR_BUSY; } + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } + return FAILED; } @@ -606,6 +620,9 @@ Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) { if (err == ERR_NET_WOULD_BLOCK) { return ERR_BUSY; } + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } return FAILED; } @@ -625,6 +642,9 @@ Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP if (err == ERR_NET_WOULD_BLOCK) { return ERR_BUSY; } + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } return FAILED; } diff --git a/drivers/unix/net_socket_posix.h b/drivers/unix/net_socket_posix.h index bd2088b4f95..2682530e151 100644 --- a/drivers/unix/net_socket_posix.h +++ b/drivers/unix/net_socket_posix.h @@ -56,6 +56,7 @@ private: ERR_NET_IN_PROGRESS, ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE, ERR_NET_UNAUTHORIZED, + ERR_NET_BUFFER_TOO_SMALL, ERR_NET_OTHER, }; From 2120fcee1caf750ceaf74f387907c28619995b22 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Wed, 19 Jul 2023 09:46:53 +0200 Subject: [PATCH 30/31] [ENet] Update to upstream master. (cherry picked from commit 35b70681e73742a280169805a6162a5777f6ce9e) --- thirdparty/README.md | 2 +- thirdparty/enet/enet/enet.h | 16 +-- thirdparty/enet/godot.cpp | 4 + thirdparty/enet/host.c | 16 ++- thirdparty/enet/packet.c | 77 ++++++------- thirdparty/enet/peer.c | 84 +++++++++------ thirdparty/enet/protocol.c | 210 +++++++++++++++++++++--------------- 7 files changed, 241 insertions(+), 168 deletions(-) diff --git a/thirdparty/README.md b/thirdparty/README.md index f8922937296..212044a7051 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -115,7 +115,7 @@ commits. ## enet - Upstream: http://enet.bespin.org -- Version: 1.3.17 (e0e7045b7e056b454b5093cb34df49dc4cee0bee, 2020) +- Version: git (ea4607a90dbfbcf4da2669ea998585253d8e70b1, 2023) - License: MIT Files extracted from upstream source: diff --git a/thirdparty/enet/enet/enet.h b/thirdparty/enet/enet/enet.h index 77f8004b808..5232f8a8698 100644 --- a/thirdparty/enet/enet/enet.h +++ b/thirdparty/enet/enet/enet.h @@ -68,7 +68,8 @@ typedef enum _ENetSocketOption ENET_SOCKOPT_RCVTIMEO = 6, ENET_SOCKOPT_SNDTIMEO = 7, ENET_SOCKOPT_ERROR = 8, - ENET_SOCKOPT_NODELAY = 9 + ENET_SOCKOPT_NODELAY = 9, + ENET_SOCKOPT_TTL = 10 } ENetSocketOption; typedef enum _ENetSocketShutdown @@ -179,7 +180,7 @@ typedef struct _ENetOutgoingCommand enet_uint16 unreliableSequenceNumber; enet_uint32 sentTime; enet_uint32 roundTripTimeout; - enet_uint32 roundTripTimeoutLimit; + enet_uint32 queueTime; enet_uint32 fragmentOffset; enet_uint16 fragmentLength; enet_uint16 sendAttempts; @@ -222,7 +223,7 @@ enum ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024, ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024, ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000, - ENET_HOST_DEFAULT_MTU = 1400, + ENET_HOST_DEFAULT_MTU = 1392, ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE = 32 * 1024 * 1024, ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA = 32 * 1024 * 1024, @@ -262,7 +263,8 @@ typedef struct _ENetChannel typedef enum _ENetPeerFlag { - ENET_PEER_FLAG_NEEDS_DISPATCH = (1 << 0) + ENET_PEER_FLAG_NEEDS_DISPATCH = (1 << 0), + ENET_PEER_FLAG_CONTINUE_SENDING = (1 << 1) } ENetPeerFlag; /** @@ -322,7 +324,7 @@ typedef struct _ENetPeer enet_uint16 outgoingReliableSequenceNumber; ENetList acknowledgements; ENetList sentReliableCommands; - ENetList sentUnreliableCommands; + ENetList outgoingSendReliableCommands; ENetList outgoingCommands; ENetList dispatchedCommands; enet_uint16 flags; @@ -385,7 +387,7 @@ typedef struct _ENetHost size_t channelLimit; /**< maximum number of channels allowed for connected peers */ enet_uint32 serviceTime; ENetList dispatchQueue; - int continueSending; + enet_uint32 totalQueued; size_t packetSize; enet_uint16 headerFlags; ENetProtocol commands [ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS]; @@ -585,6 +587,7 @@ ENET_API void enet_host_channel_limit (ENetHost *, size_t); ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32); extern void enet_host_bandwidth_throttle (ENetHost *); extern enet_uint32 enet_host_random_seed (void); +extern enet_uint32 enet_host_random (ENetHost *); ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *); ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID); @@ -598,6 +601,7 @@ ENET_API void enet_peer_disconnect_later (ENetPeer *, enet_uint32 ENET_API void enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32); extern int enet_peer_throttle (ENetPeer *, enet_uint32); extern void enet_peer_reset_queues (ENetPeer *); +extern int enet_peer_has_outgoing_commands (ENetPeer *); extern void enet_peer_setup_outgoing_command (ENetPeer *, ENetOutgoingCommand *); extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16); extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, const void *, size_t, enet_uint32, enet_uint32); diff --git a/thirdparty/enet/godot.cpp b/thirdparty/enet/godot.cpp index 2cbfe59fc6c..9ce126a475d 100644 --- a/thirdparty/enet/godot.cpp +++ b/thirdparty/enet/godot.cpp @@ -535,6 +535,10 @@ int enet_socket_receive(ENetSocket socket, ENetAddress *address, ENetBuffer *buf if (err == ERR_BUSY) { return 0; } + if (err == ERR_OUT_OF_MEMORY) { + // A packet above the ENET_PROTOCOL_MAXIMUM_MTU was received. + return -2; + } if (err != OK) { return -1; diff --git a/thirdparty/enet/host.c b/thirdparty/enet/host.c index 21ab27e2473..adb3533cf19 100644 --- a/thirdparty/enet/host.c +++ b/thirdparty/enet/host.c @@ -96,6 +96,7 @@ enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelL host -> totalSentPackets = 0; host -> totalReceivedData = 0; host -> totalReceivedPackets = 0; + host -> totalQueued = 0; host -> connectedPeers = 0; host -> bandwidthLimitedPeers = 0; @@ -123,8 +124,8 @@ enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelL enet_list_clear (& currentPeer -> acknowledgements); enet_list_clear (& currentPeer -> sentReliableCommands); - enet_list_clear (& currentPeer -> sentUnreliableCommands); enet_list_clear (& currentPeer -> outgoingCommands); + enet_list_clear (& currentPeer -> outgoingSendReliableCommands); enet_list_clear (& currentPeer -> dispatchedCommands); enet_peer_reset (currentPeer); @@ -160,6 +161,16 @@ enet_host_destroy (ENetHost * host) enet_free (host); } +enet_uint32 +enet_host_random (ENetHost * host) +{ + /* Mulberry32 by Tommy Ettinger */ + enet_uint32 n = (host -> randomSeed += 0x6D2B79F5U); + n = (n ^ (n >> 15)) * (n | 1U); + n ^= n + (n ^ (n >> 7)) * (n | 61U); + return n ^ (n >> 14); +} + /** Initiates a connection to a foreign host. @param host host seeking the connection @param address destination for the connection @@ -199,7 +210,8 @@ enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelC currentPeer -> channelCount = channelCount; currentPeer -> state = ENET_PEER_STATE_CONNECTING; currentPeer -> address = * address; - currentPeer -> connectID = ++ host -> randomSeed; + currentPeer -> connectID = enet_host_random (host); + currentPeer -> mtu = host -> mtu; if (host -> outgoingBandwidth == 0) currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; diff --git a/thirdparty/enet/packet.c b/thirdparty/enet/packet.c index 5fa78b28ae3..d51c6404049 100644 --- a/thirdparty/enet/packet.c +++ b/thirdparty/enet/packet.c @@ -98,53 +98,46 @@ enet_packet_resize (ENetPacket * packet, size_t dataLength) return 0; } -static int initializedCRC32 = 0; -static enet_uint32 crcTable [256]; - -static enet_uint32 -reflect_crc (int val, int bits) +static const enet_uint32 crcTable [256] = { - int result = 0, bit; + 0, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x5005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0xBDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; - for (bit = 0; bit < bits; bit ++) - { - if(val & 1) result |= 1 << (bits - 1 - bit); - val >>= 1; - } - - return result; -} - -static void -initialize_crc32 (void) -{ - int byte; - - for (byte = 0; byte < 256; ++ byte) - { - enet_uint32 crc = reflect_crc (byte, 8) << 24; - int offset; - - for(offset = 0; offset < 8; ++ offset) - { - if (crc & 0x80000000) - crc = (crc << 1) ^ 0x04c11db7; - else - crc <<= 1; - } - - crcTable [byte] = reflect_crc (crc, 32); - } - - initializedCRC32 = 1; -} - enet_uint32 enet_crc32 (const ENetBuffer * buffers, size_t bufferCount) { enet_uint32 crc = 0xFFFFFFFF; - - if (! initializedCRC32) initialize_crc32 (); while (bufferCount -- > 0) { @@ -153,7 +146,7 @@ enet_crc32 (const ENetBuffer * buffers, size_t bufferCount) while (data < dataEnd) { - crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++]; + crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++]; } ++ buffers; diff --git a/thirdparty/enet/peer.c b/thirdparty/enet/peer.c index 9370ef4be12..a7ac012079a 100644 --- a/thirdparty/enet/peer.c +++ b/thirdparty/enet/peer.c @@ -90,6 +90,13 @@ enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt) } /** Queues a packet to be sent. + + On success, ENet will assume ownership of the packet, and so enet_packet_destroy + should not be called on it thereafter. On failure, the caller still must destroy + the packet on its own as ENet has not queued the packet. The caller can also + check the packet's referenceCount field after sending to check if ENet queued + the packet and thus incremented the referenceCount. + @param peer destination for the packet @param channelID channel on which to send @param packet packet to send @@ -99,7 +106,7 @@ enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt) int enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet) { - ENetChannel * channel = & peer -> channels [channelID]; + ENetChannel * channel; ENetProtocol command; size_t fragmentLength; @@ -108,6 +115,7 @@ enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet) packet -> dataLength > peer -> host -> maximumPacketSize) return -1; + channel = & peer -> channels [channelID]; fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment); if (peer -> host -> checksum != NULL) fragmentLength -= sizeof(enet_uint32); @@ -320,8 +328,8 @@ enet_peer_reset_queues (ENetPeer * peer) enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements))); enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands); - enet_peer_reset_outgoing_commands (& peer -> sentUnreliableCommands); enet_peer_reset_outgoing_commands (& peer -> outgoingCommands); + enet_peer_reset_outgoing_commands (& peer -> outgoingSendReliableCommands); enet_peer_reset_incoming_commands (& peer -> dispatchedCommands); if (peer -> channels != NULL && peer -> channelCount > 0) @@ -563,6 +571,17 @@ enet_peer_disconnect (ENetPeer * peer, enet_uint32 data) } } +int +enet_peer_has_outgoing_commands (ENetPeer * peer) +{ + if (enet_list_empty (& peer -> outgoingCommands) && + enet_list_empty (& peer -> outgoingSendReliableCommands) && + enet_list_empty (& peer -> sentReliableCommands)) + return 0; + + return 1; +} + /** Request a disconnection from a peer, but only after all queued outgoing packets are sent. @param peer peer to request a disconnection @param data data describing the disconnection @@ -573,8 +592,7 @@ void enet_peer_disconnect_later (ENetPeer * peer, enet_uint32 data) { if ((peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) && - ! (enet_list_empty (& peer -> outgoingCommands) && - enet_list_empty (& peer -> sentReliableCommands))) + enet_peer_has_outgoing_commands (peer)) { peer -> state = ENET_PEER_STATE_DISCONNECT_LATER; peer -> eventData = data; @@ -618,8 +636,6 @@ enet_peer_queue_acknowledgement (ENetPeer * peer, const ENetProtocol * command, void enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoingCommand) { - ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID]; - peer -> outgoingDataTotal += enet_protocol_command_size (outgoingCommand -> command.header.command) + outgoingCommand -> fragmentLength; if (outgoingCommand -> command.header.channelID == 0xFF) @@ -630,36 +646,40 @@ enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoin outgoingCommand -> unreliableSequenceNumber = 0; } else - if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) { - ++ channel -> outgoingReliableSequenceNumber; - channel -> outgoingUnreliableSequenceNumber = 0; + ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID]; - outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; - outgoingCommand -> unreliableSequenceNumber = 0; - } - else - if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED) - { - ++ peer -> outgoingUnsequencedGroup; + if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) + { + ++ channel -> outgoingReliableSequenceNumber; + channel -> outgoingUnreliableSequenceNumber = 0; - outgoingCommand -> reliableSequenceNumber = 0; - outgoingCommand -> unreliableSequenceNumber = 0; + outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED) + { + ++ peer -> outgoingUnsequencedGroup; + + outgoingCommand -> reliableSequenceNumber = 0; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + { + if (outgoingCommand -> fragmentOffset == 0) + ++ channel -> outgoingUnreliableSequenceNumber; + + outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber; + } } - else - { - if (outgoingCommand -> fragmentOffset == 0) - ++ channel -> outgoingUnreliableSequenceNumber; - - outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; - outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber; - } - + outgoingCommand -> sendAttempts = 0; outgoingCommand -> sentTime = 0; outgoingCommand -> roundTripTimeout = 0; - outgoingCommand -> roundTripTimeoutLimit = 0; outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> reliableSequenceNumber); + outgoingCommand -> queueTime = ++ peer -> host -> totalQueued; switch (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) { @@ -670,12 +690,16 @@ enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoin case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: outgoingCommand -> command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup); break; - + default: break; } - enet_list_insert (enet_list_end (& peer -> outgoingCommands), outgoingCommand); + if ((outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) != 0 && + outgoingCommand -> packet != NULL) + enet_list_insert (enet_list_end (& peer -> outgoingSendReliableCommands), outgoingCommand); + else + enet_list_insert (enet_list_end (& peer -> outgoingCommands), outgoingCommand); } ENetOutgoingCommand * diff --git a/thirdparty/enet/protocol.c b/thirdparty/enet/protocol.c index d7fe80f1178..af307af7e54 100644 --- a/thirdparty/enet/protocol.c +++ b/thirdparty/enet/protocol.c @@ -9,7 +9,7 @@ #include "enet/time.h" #include "enet/enet.h" -static size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] = +static const size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] = { 0, sizeof (ENetProtocolAcknowledge), @@ -159,16 +159,16 @@ enet_protocol_notify_disconnect (ENetHost * host, ENetPeer * peer, ENetEvent * e } static void -enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer) +enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer, ENetList * sentUnreliableCommands) { ENetOutgoingCommand * outgoingCommand; - if (enet_list_empty (& peer -> sentUnreliableCommands)) + if (enet_list_empty (sentUnreliableCommands)) return; do { - outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentUnreliableCommands); + outgoingCommand = (ENetOutgoingCommand *) enet_list_front (sentUnreliableCommands); enet_list_remove (& outgoingCommand -> outgoingCommandList); @@ -185,14 +185,38 @@ enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer) } enet_free (outgoingCommand); - } while (! enet_list_empty (& peer -> sentUnreliableCommands)); + } while (! enet_list_empty (sentUnreliableCommands)); if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER && - enet_list_empty (& peer -> outgoingCommands) && - enet_list_empty (& peer -> sentReliableCommands)) + ! enet_peer_has_outgoing_commands (peer)) enet_peer_disconnect (peer, peer -> eventData); } +static ENetOutgoingCommand * +enet_protocol_find_sent_reliable_command (ENetList * list, enet_uint16 reliableSequenceNumber, enet_uint8 channelID) +{ + ENetListIterator currentCommand; + + for (currentCommand = enet_list_begin (list); + currentCommand != enet_list_end (list); + currentCommand = enet_list_next (currentCommand)) + { + ENetOutgoingCommand * outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + if (! (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)) + continue; + + if (outgoingCommand -> sendAttempts < 1) + break; + + if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber && + outgoingCommand -> command.header.channelID == channelID) + return outgoingCommand; + } + + return NULL; +} + static ENetProtocolCommand enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliableSequenceNumber, enet_uint8 channelID) { @@ -214,24 +238,9 @@ enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliabl if (currentCommand == enet_list_end (& peer -> sentReliableCommands)) { - for (currentCommand = enet_list_begin (& peer -> outgoingCommands); - currentCommand != enet_list_end (& peer -> outgoingCommands); - currentCommand = enet_list_next (currentCommand)) - { - outgoingCommand = (ENetOutgoingCommand *) currentCommand; - - if (! (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)) - continue; - - if (outgoingCommand -> sendAttempts < 1) return ENET_PROTOCOL_COMMAND_NONE; - - if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber && - outgoingCommand -> command.header.channelID == channelID) - break; - } - - if (currentCommand == enet_list_end (& peer -> outgoingCommands)) - return ENET_PROTOCOL_COMMAND_NONE; + outgoingCommand = enet_protocol_find_sent_reliable_command (& peer -> outgoingCommands, reliableSequenceNumber, channelID); + if (outgoingCommand == NULL) + outgoingCommand = enet_protocol_find_sent_reliable_command (& peer -> outgoingSendReliableCommands, reliableSequenceNumber, channelID); wasSent = 0; } @@ -331,6 +340,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT; peer -> connectID = command -> connect.connectID; peer -> address = host -> receivedAddress; + peer -> mtu = host -> mtu; peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID); peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth); peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth); @@ -375,7 +385,8 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) mtu = ENET_PROTOCOL_MAXIMUM_MTU; - peer -> mtu = mtu; + if (mtu < peer -> mtu) + peer -> mtu = mtu; if (host -> outgoingBandwidth == 0 && peer -> incomingBandwidth == 0) @@ -542,7 +553,8 @@ enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENet fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength); * currentData += fragmentLength; - if (fragmentLength > host -> maximumPacketSize || + if (fragmentLength <= 0 || + fragmentLength > host -> maximumPacketSize || * currentData < host -> receivedData || * currentData > & host -> receivedData [host -> receivedDataLength]) return -1; @@ -566,6 +578,7 @@ enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENet if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT || fragmentNumber >= fragmentCount || totalLength > host -> maximumPacketSize || + totalLength < fragmentCount || fragmentOffset >= totalLength || fragmentLength > totalLength - fragmentOffset) return -1; @@ -921,8 +934,7 @@ enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer * break; case ENET_PEER_STATE_DISCONNECT_LATER: - if (enet_list_empty (& peer -> outgoingCommands) && - enet_list_empty (& peer -> sentReliableCommands)) + if (! enet_peer_has_outgoing_commands (peer)) enet_peer_disconnect (peer, peer -> eventData); break; @@ -1230,6 +1242,9 @@ enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event) & buffer, 1); + if (receivedLength == -2) + continue; + if (receivedLength < 0) return -1; @@ -1293,7 +1308,7 @@ enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer) buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge)) { - host -> continueSending = 1; + peer -> flags |= ENET_PEER_FLAG_CONTINUE_SENDING; break; } @@ -1333,10 +1348,11 @@ static int enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event) { ENetOutgoingCommand * outgoingCommand; - ENetListIterator currentCommand, insertPosition; + ENetListIterator currentCommand, insertPosition, insertSendReliablePosition; currentCommand = enet_list_begin (& peer -> sentReliableCommands); insertPosition = enet_list_begin (& peer -> outgoingCommands); + insertSendReliablePosition = enet_list_begin (& peer -> outgoingSendReliableCommands); while (currentCommand != enet_list_end (& peer -> sentReliableCommands)) { @@ -1353,7 +1369,7 @@ enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * even if (peer -> earliestTimeout != 0 && (ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMaximum || - (outgoingCommand -> roundTripTimeout >= outgoingCommand -> roundTripTimeoutLimit && + ((1 << (outgoingCommand -> sendAttempts - 1)) >= peer -> timeoutLimit && ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMinimum))) { enet_protocol_notify_disconnect (host, peer, event); @@ -1361,14 +1377,18 @@ enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * even return 1; } - if (outgoingCommand -> packet != NULL) - peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength; - ++ peer -> packetsLost; outgoingCommand -> roundTripTimeout *= 2; - enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList)); + if (outgoingCommand -> packet != NULL) + { + peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength; + + enet_list_insert (insertSendReliablePosition, enet_list_remove (& outgoingCommand -> outgoingCommandList)); + } + else + enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList)); if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) && ! enet_list_empty (& peer -> sentReliableCommands)) @@ -1383,22 +1403,41 @@ enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * even } static int -enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer) +enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer, ENetList * sentUnreliableCommands) { ENetProtocol * command = & host -> commands [host -> commandCount]; ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; ENetOutgoingCommand * outgoingCommand; - ENetListIterator currentCommand; - ENetChannel *channel; - enet_uint16 reliableWindow; + ENetListIterator currentCommand, currentSendReliableCommand; + ENetChannel *channel = NULL; + enet_uint16 reliableWindow = 0; size_t commandSize; - int windowExceeded = 0, windowWrap = 0, canPing = 1; + int windowWrap = 0, canPing = 1; currentCommand = enet_list_begin (& peer -> outgoingCommands); - - while (currentCommand != enet_list_end (& peer -> outgoingCommands)) + currentSendReliableCommand = enet_list_begin (& peer -> outgoingSendReliableCommands); + + for (;;) { - outgoingCommand = (ENetOutgoingCommand *) currentCommand; + if (currentCommand != enet_list_end (& peer -> outgoingCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + if (currentSendReliableCommand != enet_list_end (& peer -> outgoingSendReliableCommands) && + ENET_TIME_LESS (((ENetOutgoingCommand *) currentSendReliableCommand) -> queueTime, outgoingCommand -> queueTime)) + goto useSendReliableCommand; + + currentCommand = enet_list_next (currentCommand); + } + else + if (currentSendReliableCommand != enet_list_end (& peer -> outgoingSendReliableCommands)) + { + useSendReliableCommand: + outgoingCommand = (ENetOutgoingCommand *) currentSendReliableCommand; + currentSendReliableCommand = enet_list_next (currentSendReliableCommand); + } + else + break; if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) { @@ -1406,33 +1445,29 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer) reliableWindow = outgoingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (channel != NULL) { - if (! windowWrap && - outgoingCommand -> sendAttempts < 1 && + if (windowWrap) + continue; + else + if (outgoingCommand -> sendAttempts < 1 && ! (outgoingCommand -> reliableSequenceNumber % ENET_PEER_RELIABLE_WINDOW_SIZE) && (channel -> reliableWindows [(reliableWindow + ENET_PEER_RELIABLE_WINDOWS - 1) % ENET_PEER_RELIABLE_WINDOWS] >= ENET_PEER_RELIABLE_WINDOW_SIZE || channel -> usedReliableWindows & ((((1 << (ENET_PEER_FREE_RELIABLE_WINDOWS + 2)) - 1) << reliableWindow) | (((1 << (ENET_PEER_FREE_RELIABLE_WINDOWS + 2)) - 1) >> (ENET_PEER_RELIABLE_WINDOWS - reliableWindow))))) - windowWrap = 1; - if (windowWrap) { - currentCommand = enet_list_next (currentCommand); - + windowWrap = 1; + currentSendReliableCommand = enet_list_end (& peer -> outgoingSendReliableCommands); + continue; } } - + if (outgoingCommand -> packet != NULL) { - if (! windowExceeded) + enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE; + + if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu)) { - enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE; - - if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu)) - windowExceeded = 1; - } - if (windowExceeded) - { - currentCommand = enet_list_next (currentCommand); + currentSendReliableCommand = enet_list_end (& peer -> outgoingSendReliableCommands); continue; } @@ -1448,13 +1483,11 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer) (outgoingCommand -> packet != NULL && (enet_uint16) (peer -> mtu - host -> packetSize) < (enet_uint16) (commandSize + outgoingCommand -> fragmentLength))) { - host -> continueSending = 1; - + peer -> flags |= ENET_PEER_FLAG_CONTINUE_SENDING; + break; } - currentCommand = enet_list_next (currentCommand); - if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) { if (channel != NULL && outgoingCommand -> sendAttempts < 1) @@ -1466,10 +1499,7 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer) ++ outgoingCommand -> sendAttempts; if (outgoingCommand -> roundTripTimeout == 0) - { - outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance; - outgoingCommand -> roundTripTimeoutLimit = peer -> timeoutLimit * outgoingCommand -> roundTripTimeout; - } + outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance; if (enet_list_empty (& peer -> sentReliableCommands)) peer -> nextTimeout = host -> serviceTime + outgoingCommand -> roundTripTimeout; @@ -1522,7 +1552,7 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer) enet_list_remove (& outgoingCommand -> outgoingCommandList); if (outgoingCommand -> packet != NULL) - enet_list_insert (enet_list_end (& peer -> sentUnreliableCommands), outgoingCommand); + enet_list_insert (enet_list_end (sentUnreliableCommands), outgoingCommand); } buffer -> data = command; @@ -1555,9 +1585,8 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer) host -> bufferCount = buffer - host -> buffers; if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER && - enet_list_empty (& peer -> outgoingCommands) && - enet_list_empty (& peer -> sentReliableCommands) && - enet_list_empty (& peer -> sentUnreliableCommands)) + ! enet_peer_has_outgoing_commands (peer) && + enet_list_empty (sentUnreliableCommands)) enet_peer_disconnect (peer, peer -> eventData); return canPing; @@ -1568,22 +1597,24 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch { enet_uint8 headerData [sizeof (ENetProtocolHeader) + sizeof (enet_uint32)]; ENetProtocolHeader * header = (ENetProtocolHeader *) headerData; - ENetPeer * currentPeer; - int sentLength; + int sentLength = 0; size_t shouldCompress = 0; - - host -> continueSending = 1; + ENetList sentUnreliableCommands; - while (host -> continueSending) - for (host -> continueSending = 0, - currentPeer = host -> peers; + enet_list_clear (& sentUnreliableCommands); + + for (int sendPass = 0, continueSending = 0; sendPass <= continueSending; ++ sendPass) + for (ENetPeer * currentPeer = host -> peers; currentPeer < & host -> peers [host -> peerCount]; ++ currentPeer) { if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED || - currentPeer -> state == ENET_PEER_STATE_ZOMBIE) + currentPeer -> state == ENET_PEER_STATE_ZOMBIE || + (sendPass > 0 && ! (currentPeer -> flags & ENET_PEER_FLAG_CONTINUE_SENDING))) continue; + currentPeer -> flags &= ~ ENET_PEER_FLAG_CONTINUE_SENDING; + host -> headerFlags = 0; host -> commandCount = 0; host -> bufferCount = 1; @@ -1600,21 +1631,22 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) return 1; else - continue; + goto nextPeer; } - if ((enet_list_empty (& currentPeer -> outgoingCommands) || - enet_protocol_check_outgoing_commands (host, currentPeer)) && + if (((enet_list_empty (& currentPeer -> outgoingCommands) && + enet_list_empty (& currentPeer -> outgoingSendReliableCommands)) || + enet_protocol_check_outgoing_commands (host, currentPeer, & sentUnreliableCommands)) && enet_list_empty (& currentPeer -> sentReliableCommands) && ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> lastReceiveTime) >= currentPeer -> pingInterval && currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing)) { enet_peer_ping (currentPeer); - enet_protocol_check_outgoing_commands (host, currentPeer); + enet_protocol_check_outgoing_commands (host, currentPeer, & sentUnreliableCommands); } if (host -> commandCount == 0) - continue; + goto nextPeer; if (currentPeer -> packetLossEpoch == 0) currentPeer -> packetLossEpoch = host -> serviceTime; @@ -1625,7 +1657,7 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent; #ifdef ENET_DEBUG - printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands) : 0); + printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingCommands) + enet_list_size (& currentPeer -> outgoingSendReliableCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands) : 0); #endif currentPeer -> packetLossVariance = (currentPeer -> packetLossVariance * 3 + ENET_DIFFERENCE (packetLoss, currentPeer -> packetLoss)) / 4; @@ -1687,13 +1719,17 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch sentLength = enet_socket_send (host -> socket, & currentPeer -> address, host -> buffers, host -> bufferCount); - enet_protocol_remove_sent_unreliable_commands (currentPeer); + enet_protocol_remove_sent_unreliable_commands (currentPeer, & sentUnreliableCommands); if (sentLength < 0) return -1; host -> totalSentData += sentLength; host -> totalSentPackets ++; + + nextPeer: + if (currentPeer -> flags & ENET_PEER_FLAG_CONTINUE_SENDING) + continueSending = sendPass + 1; } return 0; From c945ec71d374adf3fb613e7bebd649713d2d7cae Mon Sep 17 00:00:00 2001 From: Yuri Sizov Date: Thu, 20 Jul 2023 14:55:40 +0200 Subject: [PATCH 31/31] Add changelog for 4.0.4 --- CHANGELOG.md | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 232ef25ee93..d33883f62ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,207 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [4.0.4] - TBD + +See the [release announcement](https://godotengine.org/article/maintenance-release-godot-4-0-4) for details. + +### Added + +#### C#/.NET + +- Add version defines to help users deal with breaking changes ([GH-78270](https://github.com/godotengine/godot/pull/78270)). + +#### Documentation + +- Document GI techniques ignoring VisualInstance3D and Camera3D layers ([GH-74688](https://github.com/godotengine/godot/pull/74688)). +- Document how to use the global animation library in GDScript ([GH-74894](https://github.com/godotengine/godot/pull/74894)). +- Add more info on the nature of NAN ([GH-75614](https://github.com/godotengine/godot/pull/75614)). +- Add Stretch Mode description to ProjectSettings.xml ([GH-76272](https://github.com/godotengine/godot/pull/76272)). +- Document seamless caveats on small textures in NoiseTexture2D and NoiseTexture3D ([GH-77017](https://github.com/godotengine/godot/pull/77017)). +- Document the database for `Input.get_joy_name()` and `Input.get_joy_guid()` ([GH-77768](https://github.com/godotengine/godot/pull/77768)). +- Document the InitialAction enum in RenderingDevice ([GH-77882](https://github.com/godotengine/godot/pull/77882)). +- Document ShaderInclude ([GH-78562](https://github.com/godotengine/godot/pull/78562)). + +#### Editor + +- Add an editor option to copy system info to clipboard ([GH-65902](https://github.com/godotengine/godot/pull/65902)). + +#### Input + +- Add support for DPAD Center key of Android TV remote controller ([GH-77115](https://github.com/godotengine/godot/pull/77115)). + +### Changed + +#### 2D + +- Make tile atlas merge dialog use filter nearest on both sides ([GH-77385](https://github.com/godotengine/godot/pull/77385)). +- Don't create bones from empty scene ([GH-77473](https://github.com/godotengine/godot/pull/77473)). +- Don't disable `Material` and `PlaceholderMaterial` when `disable_3d=yes` ([GH-77654](https://github.com/godotengine/godot/pull/77654)). +- Draw materials in tile atlas view ([GH-77909](https://github.com/godotengine/godot/pull/77909)). + +#### Animation + +- Hide Animation Frames section when there are no animations ([GH-77221](https://github.com/godotengine/godot/pull/77221)). +- Improve `Skeleton3D::find_bone()` performance ([GH-77307](https://github.com/godotengine/godot/pull/77307)). +- Avoid mutating the same Skin multiple times ([GH-77505](https://github.com/godotengine/godot/pull/77505)). + +#### C#/.NET + +- Always decode `dotnet` output as UTF-8 ([GH-74065](https://github.com/godotengine/godot/pull/74065)). +- Link the right build property to REAL_T_IS_DOUBLE ([GH-77198](https://github.com/godotengine/godot/pull/77198)). + +#### Editor + +- Use nearest with mipmaps texture filter in SpriteFrames editor plugin ([GH-74341](https://github.com/godotengine/godot/pull/74341)). +- Make sure script cache is created after reimport ([GH-75798](https://github.com/godotengine/godot/pull/75798)). +- Make SpriteFrames editor toolbar a `FlowContainer` ([GH-77034](https://github.com/godotengine/godot/pull/77034)). +- Prevent selecting unselectable `EditorProperty` with RMB ([GH-77148](https://github.com/godotengine/godot/pull/77148)). +- Do not translate node name when assigned to an exported field ([GH-77217](https://github.com/godotengine/godot/pull/77217)). +- Allow up to INT32_MAX max size in Array/Dictionary editor ([GH-77225](https://github.com/godotengine/godot/pull/77225)). +- Avoid error spam on first opening of a not yet imported project ([GH-77276](https://github.com/godotengine/godot/pull/77276)). +- Ensure quotes are escaped when converting built-in scripts ([GH-77399](https://github.com/godotengine/godot/pull/77399)). +- Ignore the `project_settings_override` file when in editor ([GH-77459](https://github.com/godotengine/godot/pull/77459)). + +#### GDScript + +- Treat `BitField` as `int` (not `Enum`) ([GH-77579](https://github.com/godotengine/godot/pull/77579)). + +#### GUI + +- Stop dragging when `Slider` changes editability ([GH-77242](https://github.com/godotengine/godot/pull/77242)). +- Use defined key mapping for closing popups and dialogs ([GH-77297](https://github.com/godotengine/godot/pull/77297)). +- TextServer: Prevent duplicate line breaks on virtual spaces when line width is significantly smaller than character width ([GH-77514](https://github.com/godotengine/godot/pull/77514)). +- Cancel tooltip when mouse leaves viewport ([GH-77933](https://github.com/godotengine/godot/pull/77933)). +- Preserve selection when focusing SpinBox ([GH-78092](https://github.com/godotengine/godot/pull/78092)). + +#### Input + +- Improve touchpad and mouse support for the Android editor ([GH-77498](https://github.com/godotengine/godot/pull/77498)). +- Skip error messages for buttons that don't exist ([GH-77748](https://github.com/godotengine/godot/pull/77748)). + +#### Rendering + +- Disable AMD switchable graphics on Windows with Vulkan to fix driver issue ([GH-73450](https://github.com/godotengine/godot/pull/73450)). +- Take 3D resolution scaling into account for mesh LOD ([GH-77294](https://github.com/godotengine/godot/pull/77294)). + +#### Thirdparty + +- brotli updated to version ed1995b6b. +- msdfgen updated to version 1.10. +- recast updated to version 1.6.0. +- tinyexr updated to version 1.0.5. +- wslay updated to version 0e7d106ff. +- zstd updated to version 1.5.5. +- CA root certificates updated to 2023-06-02 bundle from Mozilla. + +### Fixed + +#### 2D + +- Fix crash when opening a TileSet with invalid tiles ([GH-78165](https://github.com/godotengine/godot/pull/78165)). +- Fix crash with failed compatibility tiles ([GH-78796](https://github.com/godotengine/godot/pull/78796)). + +#### 3D + +- Fix CSGPolygon3D in path mode disappearing at runtime ([GH-77118](https://github.com/godotengine/godot/pull/77118)). + +#### Animation + +- Fix type check in AnimationTrackKeyEdit for methods ([GH-74948](https://github.com/godotengine/godot/pull/74948)). +- Fix `AnimatedSprite3D` autoplay warning ([GH-77028](https://github.com/godotengine/godot/pull/77028)). +- Adjust BoneAttachment3D children/meshes during rest fixer ([GH-77123](https://github.com/godotengine/godot/pull/77123)). +- Fix `get_bone_pose_global_no_override()` returning incorrect values ([GH-77194](https://github.com/godotengine/godot/pull/77194)). +- Fix for SkeletonIK3D interpolation and bone roll ([GH-77469](https://github.com/godotengine/godot/pull/77469)). +- Fix AnimationPlayer cumulative `speed_scale` ([GH-77500](https://github.com/godotengine/godot/pull/77500)). +- Fix adding bones with the same name after calling `Skeleton3D.clear_bones()` ([GH-77874](https://github.com/godotengine/godot/pull/77874)). + +#### Audio + +- Fix trim when importing WAV ([GH-75261](https://github.com/godotengine/godot/pull/75261)). +- Fix 2D audio in multiple viewports ([GH-76713](https://github.com/godotengine/godot/pull/76713)). +- Fix crash in AudioStream preview ([GH-77664](https://github.com/godotengine/godot/pull/77664)). +- Fix issue causing the Android editor to crash when creating a new AudioStreamMicrophone ([GH-77686](https://github.com/godotengine/godot/pull/77686)). + +#### Buildsystem + +- CI: Fix running the unit tests on windows ([GH-76887](https://github.com/godotengine/godot/pull/76887)). +- Linux: Fix udev fallback logic with `use_sowrap=no` ([GH-79111](https://github.com/godotengine/godot/pull/79111)). + +#### C#/.NET + +- Fix C# glue generation for enums with negative values ([GH-77018](https://github.com/godotengine/godot/pull/77018)). +- Fix `SendToScriptDebugger` crash ([GH-77377](https://github.com/godotengine/godot/pull/77377)). + +#### Core + +- Fix `StringName` comparison ([GH-77197](https://github.com/godotengine/godot/pull/77197)). +- Fix calling `TextureStorage::texture_3d_update()` could cause a crash ([GH-77266](https://github.com/godotengine/godot/pull/77266)). + +#### Editor + +- Fix theme of editor VCS dialogs ([GH-75983](https://github.com/godotengine/godot/pull/75983)). +- Fix calculation bug with `TextEdit::get_line_height()` ([GH-76605](https://github.com/godotengine/godot/pull/76605)). +- Fix Input Map key assignments missing after project conversion ([GH-77134](https://github.com/godotengine/godot/pull/77134)). +- Fix `Window` derived nodes being unselectable for `ViewportTexture` `NodePath` ([GH-77312](https://github.com/godotengine/godot/pull/77312)). +- Fix crash when using tile atlas merge with recreated alt tile ([GH-77382](https://github.com/godotengine/godot/pull/77382)). +- Fix filesystem cache split error ([GH-78324](https://github.com/godotengine/godot/pull/78324)). +- Fix saving size in `CreateDialog` ([GH-78403](https://github.com/godotengine/godot/pull/78403)). +- Shaders: Exclude incorrect completion options for `render_mode` in shaders ([GH-77086](https://github.com/godotengine/godot/pull/77086)). + +#### GDScript + +- Add missing `script_type` `nullptr` check ([GH-75943](https://github.com/godotengine/godot/pull/75943)). +- Fix warning ignoring for member variables ([GH-76203](https://github.com/godotengine/godot/pull/76203)). +- Fix `validate_call_arg()` for unresolved datatype ([GH-77091](https://github.com/godotengine/godot/pull/77091)). +- Fix extraction of chained `tr()` calls ([GH-77538](https://github.com/godotengine/godot/pull/77538)). + +#### GUI + +- Fix `Range`-derived nodes not redrawing after `set_value_no_signal` ([GH-70834](https://github.com/godotengine/godot/pull/70834)). +- Fix adding colors to swatches not updating in previous ColorPickers ([GH-76751](https://github.com/godotengine/godot/pull/76751)). +- Fix crash when changing node type from PopupMenu to ItemList ([GH-76854](https://github.com/godotengine/godot/pull/76854)). +- Fix `ItemList` item text positions in RTL mode ([GH-77166](https://github.com/godotengine/godot/pull/77166)). +- Fix crash when selecting lines in text edit ([GH-77667](https://github.com/godotengine/godot/pull/77667)). +- Fix SVG font rendering after ThorVG update ([GH-77942](https://github.com/godotengine/godot/pull/77942)). +- Fix disabled slider highlighting ([GH-78776](https://github.com/godotengine/godot/pull/78776)). + +#### Input + +- Fix errors that appear while reordering input map entries ([GH-77009](https://github.com/godotengine/godot/pull/77009)). +- Fix spatial viewport multitouch detection support ([GH-78083](https://github.com/godotengine/godot/pull/78083)). + +#### Navigation + +- Fix agent avoidance position not updated when entering SceneTree ([GH-77110](https://github.com/godotengine/godot/pull/77110)). + +#### Networking + +- Fix HTTPClient `_request` using wrong size ([GH-75867](https://github.com/godotengine/godot/pull/75867)). +- ENet: Better handle truncated socket messages ([GH-79699](https://github.com/godotengine/godot/pull/79699)). + +#### Particles + +- Correctly reset particle size and rotation in ParticlesProcessMaterial ([GH-78021](https://github.com/godotengine/godot/pull/78021)). +- Avoid error spam when (un)pausing GPUParticles out of tree ([GH-78143](https://github.com/godotengine/godot/pull/78143)). + +#### Physics + +- Fix width and center position of `CapsuleShape2D::get_rect` ([GH-77065](https://github.com/godotengine/godot/pull/77065)). + +#### Porting + +- Android: Set pending intent flag to stop insta-crash ([GH-78175](https://github.com/godotengine/godot/pull/78175)). +- Windows: Fix for Win+M crashing the editor ([GH-78235](https://github.com/godotengine/godot/pull/78235)). + +#### Rendering + +- Fix typo in FinalAction `switch` statement in RenderingDevice ([GH-75945](https://github.com/godotengine/godot/pull/75945)). +- Fix modulation propagation for Y-sorted CanvasItems ([GH-77079](https://github.com/godotengine/godot/pull/77079)). +- Fix LightmapGI dynamic object lighting ([GH-77089](https://github.com/godotengine/godot/pull/77089)). +- Fix calculation of skinned AABB for unused bones ([GH-77265](https://github.com/godotengine/godot/pull/77265)). +- Fix uninitialized Y-sort modulate for CanvasItems ([GH-78134](https://github.com/godotengine/godot/pull/78134)). + + ## [4.0.3] - 2023-05-19 See the [release announcement](https://godotengine.org/article/maintenance-release-godot-4-0-3) for details. @@ -3106,6 +3307,7 @@ See the [release announcement](https://godotengine.org/article/godot-3-3-has-arr - Only WebAssembly is supported now, since all browsers supporting WebGL 2.0 also support WebAssembly. +[4.0.4]: https://github.com/godotengine/godot/compare/4.0.3-stable...4.0.4-stable [4.0.3]: https://github.com/godotengine/godot/compare/4.0.2-stable...4.0.3-stable [4.0.2]: https://github.com/godotengine/godot/compare/4.0.1-stable...4.0.2-stable [4.0.1]: https://github.com/godotengine/godot/compare/4.0-stable...4.0.1-stable