summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--SConstruct4
-rw-r--r--core/config/project_settings.cpp8
-rw-r--r--core/debugger/engine_debugger.cpp2
-rw-r--r--core/debugger/local_debugger.cpp4
-rw-r--r--core/debugger/remote_debugger.cpp4
-rw-r--r--core/debugger/remote_debugger_peer.cpp2
-rw-r--r--core/input/input.cpp2
-rw-r--r--core/input/input_map.cpp4
-rw-r--r--core/io/dir_access.cpp4
-rw-r--r--core/io/file_access_pack.cpp4
-rw-r--r--core/io/http_client.cpp4
-rw-r--r--core/io/http_client_tcp.cpp4
-rw-r--r--core/io/plist.cpp8
-rw-r--r--core/io/resource_loader.cpp6
-rw-r--r--core/io/translation_loader_po.cpp6
-rw-r--r--core/string/node_path.cpp2
-rw-r--r--core/string/translation_po.cpp8
-rw-r--r--core/string/ustring.cpp40
-rw-r--r--doc/classes/Curve3D.xml3
-rw-r--r--doc/classes/TextureProgressBar.xml1
-rw-r--r--drivers/alsa/audio_driver_alsa.cpp2
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp32
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.h4
-rw-r--r--drivers/gles3/shaders/canvas.glsl11
-rw-r--r--drivers/gles3/shaders/sky.glsl6
-rw-r--r--drivers/metal/rendering_device_driver_metal.mm3
-rw-r--r--drivers/unix/ip_unix.cpp75
-rw-r--r--drivers/unix/ip_unix.h6
-rw-r--r--drivers/unix/os_unix.cpp2
-rw-r--r--drivers/windows/dir_access_windows.cpp4
-rw-r--r--drivers/windows/file_access_windows.cpp2
-rw-r--r--drivers/windows/ip_windows.cpp164
-rw-r--r--drivers/windows/ip_windows.h54
-rw-r--r--editor/animation_bezier_editor.cpp2
-rw-r--r--editor/animation_track_editor.cpp4
-rw-r--r--editor/connections_dialog.cpp2
-rw-r--r--editor/create_dialog.cpp4
-rw-r--r--editor/debugger/debug_adapter/debug_adapter_parser.cpp2
-rw-r--r--editor/debugger/editor_debugger_node.cpp8
-rw-r--r--editor/debugger/editor_debugger_tree.cpp2
-rw-r--r--editor/dependency_editor.cpp2
-rw-r--r--editor/directory_create_dialog.cpp2
-rw-r--r--editor/editor_asset_installer.cpp8
-rw-r--r--editor/editor_folding.cpp2
-rw-r--r--editor/editor_help.cpp8
-rw-r--r--editor/editor_inspector.cpp10
-rw-r--r--editor/editor_node.cpp4
-rw-r--r--editor/editor_properties.cpp8
-rw-r--r--editor/editor_properties_array_dict.cpp12
-rw-r--r--editor/editor_sectioned_inspector.cpp4
-rw-r--r--editor/engine_update_label.cpp4
-rw-r--r--editor/filesystem_dock.cpp8
-rw-r--r--editor/gui/editor_file_dialog.cpp4
-rw-r--r--editor/import/resource_importer_imagefont.cpp2
-rw-r--r--editor/import/resource_importer_texture.cpp2
-rw-r--r--editor/localization_editor.cpp6
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp2
-rw-r--r--editor/plugins/path_3d_editor_plugin.cpp119
-rw-r--r--editor/plugins/path_3d_editor_plugin.h4
-rw-r--r--editor/plugins/script_text_editor.cpp4
-rw-r--r--editor/project_converter_3_to_4.cpp6
-rw-r--r--editor/script_create_dialog.cpp2
-rw-r--r--editor/shader_create_dialog.cpp2
-rw-r--r--main/main.cpp4
-rw-r--r--modules/basis_universal/image_compress_basisu.cpp14
-rw-r--r--modules/gdscript/editor/gdscript_docgen.cpp2
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp4
-rw-r--r--modules/gdscript/gdscript_editor.cpp2
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.cpp4
-rw-r--r--modules/gltf/gltf_document.cpp2
-rw-r--r--modules/mono/csharp_script.cpp2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs21
-rw-r--r--modules/mono/editor/bindings_generator.cpp24
-rw-r--r--modules/mono/editor/code_completion.cpp2
-rw-r--r--modules/mono/utils/path_utils.cpp2
-rw-r--r--modules/multiplayer/editor/replication_editor.cpp4
-rw-r--r--modules/svg/image_loader_svg.cpp2
-rw-r--r--platform/android/export/export.cpp18
-rw-r--r--platform/android/export/export_plugin.cpp2
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.cpp6
-rw-r--r--platform/macos/export/export_plugin.cpp4
-rw-r--r--platform/web/api/web_tools_editor_plugin.cpp2
-rw-r--r--platform/windows/display_server_windows.h1
-rw-r--r--platform/windows/os_windows.cpp4
-rw-r--r--platform/windows/os_windows.h1
-rw-r--r--scene/gui/file_dialog.cpp4
-rw-r--r--scene/gui/rich_text_label.cpp2
-rw-r--r--scene/gui/texture_progress_bar.cpp9
-rw-r--r--scene/main/http_request.cpp2
-rw-r--r--scene/property_utils.cpp2
-rw-r--r--scene/resources/curve.cpp114
-rw-r--r--scene/resources/curve.h4
-rw-r--r--scene/resources/font.cpp8
-rw-r--r--scene/resources/mesh.cpp4
-rw-r--r--scene/resources/packed_scene.cpp14
-rw-r--r--scene/resources/resource_format_text.cpp2
-rw-r--r--scene/resources/syntax_highlighter.cpp4
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp4
-rw-r--r--tests/scene/test_texture_progress_bar.h92
-rw-r--r--tests/test_main.cpp1
101 files changed, 770 insertions, 361 deletions
diff --git a/.gitignore b/.gitignore
index 0dcf79c460..f72ea1ac51 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,8 +36,8 @@ compile_commands.json
platform/windows/godot_res.res
# Ninja build files
-build.ninja
-.ninja
+*.ninja
+.ninja/
run_ninja_env.bat
# Generated by Godot binary
diff --git a/SConstruct b/SConstruct
index 234a6624f1..7c7d3d25a0 100644
--- a/SConstruct
+++ b/SConstruct
@@ -1050,7 +1050,7 @@ if env["ninja"]:
SetOption("experimental", "ninja")
env["NINJA_FILE_NAME"] = env["ninja_file"]
env["NINJA_DISABLE_AUTO_RUN"] = not env["ninja_auto_run"]
- env.Tool("ninja", "build.ninja")
+ env.Tool("ninja", env["ninja_file"])
# Threads
if env["threads"]:
@@ -1112,7 +1112,7 @@ atexit.register(print_elapsed_time)
def purge_flaky_files():
- paths_to_keep = ["build.ninja"]
+ paths_to_keep = [env["ninja_file"]]
for build_failure in GetBuildFailures():
path = build_failure.node.path
if os.path.isfile(path) and path not in paths_to_keep:
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 0c546403c5..a6883064d8 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -194,7 +194,7 @@ String ProjectSettings::localize_path(const String &p_path) const {
return cwd.replace_first(res_path, "res://");
} else {
- int sep = path.rfind("/");
+ int sep = path.rfind_char('/');
if (sep == -1) {
return "res://" + path;
}
@@ -306,7 +306,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
}
{ // Feature overrides.
- int dot = p_name.operator String().find(".");
+ int dot = p_name.operator String().find_char('.');
if (dot != -1) {
Vector<String> s = p_name.operator String().split(".");
@@ -441,7 +441,7 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const {
for (const _VCSort &E : vclist) {
String prop_info_name = E.name;
- int dot = prop_info_name.find(".");
+ int dot = prop_info_name.find_char('.');
if (dot != -1 && !custom_prop_info.has(prop_info_name)) {
prop_info_name = prop_info_name.substr(0, dot);
}
@@ -1098,7 +1098,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
String category = E.name;
String name = E.name;
- int div = category.find("/");
+ int div = category.find_char('/');
if (div < 0) {
category = "";
diff --git a/core/debugger/engine_debugger.cpp b/core/debugger/engine_debugger.cpp
index 6d2868cef2..a9f87ad825 100644
--- a/core/debugger/engine_debugger.cpp
+++ b/core/debugger/engine_debugger.cpp
@@ -163,7 +163,7 @@ void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, co
for (int i = 0; i < p_breakpoints.size(); i++) {
const String &bp = p_breakpoints[i];
- int sp = bp.rfind(":");
+ int sp = bp.rfind_char(':');
ERR_CONTINUE_MSG(sp == -1, vformat("Invalid breakpoint: '%s', expected file:line format.", bp));
singleton_script_debugger->insert_breakpoint(bp.substr(sp + 1, bp.length()).to_int(), bp.substr(0, sp));
diff --git a/core/debugger/local_debugger.cpp b/core/debugger/local_debugger.cpp
index 5e79a6d395..a5d807f66b 100644
--- a/core/debugger/local_debugger.cpp
+++ b/core/debugger/local_debugger.cpp
@@ -171,7 +171,7 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
} else {
String key_value = line.get_slicec(' ', 1);
- int value_pos = key_value.find("=");
+ int value_pos = key_value.find_char('=');
if (value_pos < 0) {
print_line("Error: Invalid set format. Use: set key=value");
@@ -344,7 +344,7 @@ Pair<String, int> LocalDebugger::to_breakpoint(const String &p_line) {
String breakpoint_part = p_line.get_slicec(' ', 1);
Pair<String, int> breakpoint;
- int last_colon = breakpoint_part.rfind(":");
+ int last_colon = breakpoint_part.rfind_char(':');
if (last_colon < 0) {
print_line("Error: Invalid breakpoint format. Expected [source:line]");
return breakpoint;
diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
index a02354b377..f8e42e6d92 100644
--- a/core/debugger/remote_debugger.cpp
+++ b/core/debugger/remote_debugger.cpp
@@ -338,7 +338,7 @@ void RemoteDebugger::_send_stack_vars(List<String> &p_names, List<Variant> &p_va
}
Error RemoteDebugger::_try_capture(const String &p_msg, const Array &p_data, bool &r_captured) {
- const int idx = p_msg.find(":");
+ const int idx = p_msg.find_char(':');
r_captured = false;
if (idx < 0) { // No prefix, unknown message.
return OK;
@@ -610,7 +610,7 @@ void RemoteDebugger::poll_events(bool p_is_idle) {
ERR_CONTINUE(arr[1].get_type() != Variant::ARRAY);
const String cmd = arr[0];
- const int idx = cmd.find(":");
+ const int idx = cmd.find_char(':');
bool parsed = false;
if (idx < 0) { // Not prefix, use scripts capture.
capture_parse("core", cmd, arr[1], parsed);
diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp
index 793db7330f..d38fbc8d91 100644
--- a/core/debugger/remote_debugger_peer.cpp
+++ b/core/debugger/remote_debugger_peer.cpp
@@ -224,7 +224,7 @@ RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create(const String &p_uri) {
uint16_t debug_port = 6007;
if (debug_host.contains(":")) {
- int sep_pos = debug_host.rfind(":");
+ int sep_pos = debug_host.rfind_char(':');
debug_port = debug_host.substr(sep_pos + 1).to_int();
debug_host = debug_host.substr(0, sep_pos);
}
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 7e2227c729..a0c00d7716 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -236,7 +236,7 @@ void Input::get_argument_options(const StringName &p_function, int p_idx, List<S
continue;
}
- String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length());
+ String name = pi.name.substr(pi.name.find_char('/') + 1, pi.name.length());
r_options->push_back(name.quote());
}
}
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index 954df36f3e..54f20a0bcc 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -104,7 +104,7 @@ void InputMap::get_argument_options(const StringName &p_function, int p_idx, Lis
continue;
}
- String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length());
+ String name = pi.name.substr(pi.name.find_char('/') + 1, pi.name.length());
r_options->push_back(name.quote());
}
}
@@ -302,7 +302,7 @@ void InputMap::load_from_project_settings() {
continue;
}
- String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length());
+ String name = pi.name.substr(pi.name.find_char('/') + 1, pi.name.length());
Dictionary action = GLOBAL_GET(pi.name);
float deadzone = action.has("deadzone") ? (float)action["deadzone"] : DEFAULT_DEADZONE;
diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp
index 5d2f65ca99..14588923cb 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -155,9 +155,9 @@ Error DirAccess::make_dir_recursive(const String &p_dir) {
} else if (full_dir.begins_with("user://")) {
base = "user://";
} else if (full_dir.is_network_share_path()) {
- int pos = full_dir.find("/", 2);
+ int pos = full_dir.find_char('/', 2);
ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
- pos = full_dir.find("/", pos + 1);
+ pos = full_dir.find_char('/', pos + 1);
ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
base = full_dir.substr(0, pos + 1);
} else if (full_dir.begins_with("/")) {
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp
index b9af1bfb57..8b6b445cea 100644
--- a/core/io/file_access_pack.cpp
+++ b/core/io/file_access_pack.cpp
@@ -590,8 +590,6 @@ String DirAccessPack::get_current_dir(bool p_include_drive) const {
}
bool DirAccessPack::file_exists(String p_file) {
- p_file = fix_path(p_file);
-
PackedData::PackedDir *pd = _find_dir(p_file.get_base_dir());
if (!pd) {
return false;
@@ -600,8 +598,6 @@ bool DirAccessPack::file_exists(String p_file) {
}
bool DirAccessPack::dir_exists(String p_dir) {
- p_dir = fix_path(p_dir);
-
return _find_dir(p_dir) != nullptr;
}
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 9e426c4908..b7a324e710 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -101,7 +101,7 @@ Error HTTPClient::verify_headers(const Vector<String> &p_headers) {
for (int i = 0; i < p_headers.size(); i++) {
String sanitized = p_headers[i].strip_edges();
ERR_FAIL_COND_V_MSG(sanitized.is_empty(), ERR_INVALID_PARAMETER, vformat("Invalid HTTP header at index %d: empty.", i));
- ERR_FAIL_COND_V_MSG(sanitized.find(":") < 1, ERR_INVALID_PARAMETER,
+ ERR_FAIL_COND_V_MSG(sanitized.find_char(':') < 1, ERR_INVALID_PARAMETER,
vformat("Invalid HTTP header at index %d: String must contain header-value pair, delimited by ':', but was: '%s'.", i, p_headers[i]));
}
@@ -113,7 +113,7 @@ Dictionary HTTPClient::_get_response_headers_as_dictionary() {
get_response_headers(&rh);
Dictionary ret;
for (const String &s : rh) {
- int sp = s.find(":");
+ int sp = s.find_char(':');
if (sp == -1) {
continue;
}
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
index 237ba30a80..1382aecb38 100644
--- a/core/io/http_client_tcp.cpp
+++ b/core/io/http_client_tcp.cpp
@@ -508,11 +508,11 @@ Error HTTPClientTCP::poll() {
continue;
}
if (s.begins_with("content-length:")) {
- body_size = s.substr(s.find(":") + 1, s.length()).strip_edges().to_int();
+ body_size = s.substr(s.find_char(':') + 1, s.length()).strip_edges().to_int();
body_left = body_size;
} else if (s.begins_with("transfer-encoding:")) {
- String encoding = header.substr(header.find(":") + 1, header.length()).strip_edges();
+ String encoding = header.substr(header.find_char(':') + 1, header.length()).strip_edges();
if (encoding == "chunked") {
chunked = true;
}
diff --git a/core/io/plist.cpp b/core/io/plist.cpp
index 32e83c31f2..26b8c39495 100644
--- a/core/io/plist.cpp
+++ b/core/io/plist.cpp
@@ -661,12 +661,12 @@ bool PList::load_string(const String &p_string, String &r_err_out) {
List<Ref<PListNode>> stack;
String key;
while (pos >= 0) {
- int open_token_s = p_string.find("<", pos);
+ int open_token_s = p_string.find_char('<', pos);
if (open_token_s == -1) {
r_err_out = "Unexpected end of data. No tags found.";
return false;
}
- int open_token_e = p_string.find(">", open_token_s);
+ int open_token_e = p_string.find_char('>', open_token_s);
pos = open_token_e;
String token = p_string.substr(open_token_s + 1, open_token_e - open_token_s - 1);
@@ -676,7 +676,7 @@ bool PList::load_string(const String &p_string, String &r_err_out) {
}
String value;
if (token[0] == '?' || token[0] == '!') { // Skip <?xml ... ?> and <!DOCTYPE ... >
- int end_token_e = p_string.find(">", open_token_s);
+ int end_token_e = p_string.find_char('>', open_token_s);
pos = end_token_e;
continue;
}
@@ -769,7 +769,7 @@ bool PList::load_string(const String &p_string, String &r_err_out) {
r_err_out = vformat("Mismatched <%s> tag.", token);
return false;
}
- int end_token_e = p_string.find(">", end_token_s);
+ int end_token_e = p_string.find_char('>', end_token_s);
pos = end_token_e;
String end_token = p_string.substr(end_token_s + 2, end_token_e - end_token_s - 2);
if (end_token != token) {
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 3fea697d0b..ecd0a91aa4 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -1206,7 +1206,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem
int best_score = 0;
for (int i = 0; i < res_remaps.size(); i++) {
- int split = res_remaps[i].rfind(":");
+ int split = res_remaps[i].rfind_char(':');
if (split == -1) {
continue;
}
@@ -1498,11 +1498,11 @@ Vector<String> ResourceLoader::list_directory(const String &p_directory) {
}
} else {
if (d.ends_with(".import") || d.ends_with(".remap") || d.ends_with(".uid")) {
- d = d.substr(0, d.rfind("."));
+ d = d.substr(0, d.rfind_char('.'));
}
if (d.ends_with(".gdc")) {
- d = d.substr(0, d.rfind("."));
+ d = d.substr(0, d.rfind_char('.'));
d += ".gd";
}
diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp
index 7a11d06df6..1761d6fa23 100644
--- a/core/io/translation_loader_po.cpp
+++ b/core/io/translation_loader_po.cpp
@@ -108,7 +108,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
// Record plural rule.
int p_start = config.find("Plural-Forms");
if (p_start != -1) {
- int p_end = config.find("\n", p_start);
+ int p_end = config.find_char('\n', p_start);
translation->set_plural_rule(config.substr(p_start, p_end - p_start));
}
} else {
@@ -224,7 +224,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
// Record plural rule.
int p_start = config.find("Plural-Forms");
if (p_start != -1) {
- int p_end = config.find("\n", p_start);
+ int p_end = config.find_char('\n', p_start);
translation->set_plural_rule(config.substr(p_start, p_end - p_start));
plural_forms = translation->get_plural_forms();
}
@@ -324,7 +324,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
Vector<String> configs = config.split("\n");
for (int i = 0; i < configs.size(); i++) {
String c = configs[i].strip_edges();
- int p = c.find(":");
+ int p = c.find_char(':');
if (p == -1) {
continue;
}
diff --git a/core/string/node_path.cpp b/core/string/node_path.cpp
index 3faf3bb0c5..40c81edf4c 100644
--- a/core/string/node_path.cpp
+++ b/core/string/node_path.cpp
@@ -407,7 +407,7 @@ NodePath::NodePath(const String &p_path) {
bool absolute = (path[0] == '/');
bool last_is_slash = true;
int slices = 0;
- int subpath_pos = path.find(":");
+ int subpath_pos = path.find_char(':');
if (subpath_pos != -1) {
int from = subpath_pos + 1;
diff --git a/core/string/translation_po.cpp b/core/string/translation_po.cpp
index da79e472e7..7eb8a2afeb 100644
--- a/core/string/translation_po.cpp
+++ b/core/string/translation_po.cpp
@@ -227,11 +227,11 @@ void TranslationPO::set_plural_rule(const String &p_plural_rule) {
// Set plural_forms and plural_rule.
// p_plural_rule passed in has the form "Plural-Forms: nplurals=2; plural=(n >= 2);".
- int first_semi_col = p_plural_rule.find(";");
- plural_forms = p_plural_rule.substr(p_plural_rule.find("=") + 1, first_semi_col - (p_plural_rule.find("=") + 1)).to_int();
+ int first_semi_col = p_plural_rule.find_char(';');
+ plural_forms = p_plural_rule.substr(p_plural_rule.find_char('=') + 1, first_semi_col - (p_plural_rule.find_char('=') + 1)).to_int();
- int expression_start = p_plural_rule.find("=", first_semi_col) + 1;
- int second_semi_col = p_plural_rule.rfind(";");
+ int expression_start = p_plural_rule.find_char('=', first_semi_col) + 1;
+ int second_semi_col = p_plural_rule.rfind_char(';');
plural_rule = p_plural_rule.substr(expression_start, second_semi_col - expression_start).strip_edges();
// Setup the cache to make evaluating plural rule faster later on.
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 521dfe0b8c..9e99fc3b2f 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -246,27 +246,27 @@ Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r
base = base.substr(pos + 3, base.length() - pos - 3);
}
}
- pos = base.find("#");
+ pos = base.find_char('#');
// Fragment
if (pos != -1) {
r_fragment = base.substr(pos + 1);
base = base.substr(0, pos);
}
- pos = base.find("/");
+ pos = base.find_char('/');
// Path
if (pos != -1) {
r_path = base.substr(pos, base.length() - pos);
base = base.substr(0, pos);
}
// Host
- pos = base.find("@");
+ pos = base.find_char('@');
if (pos != -1) {
// Strip credentials
base = base.substr(pos + 1, base.length() - pos - 1);
}
if (base.begins_with("[")) {
// Literal IPv6
- pos = base.rfind("]");
+ pos = base.rfind_char(']');
if (pos == -1) {
return ERR_INVALID_PARAMETER;
}
@@ -277,7 +277,7 @@ Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r
if (base.get_slice_count(":") > 2) {
return ERR_INVALID_PARAMETER;
}
- pos = base.rfind(":");
+ pos = base.rfind_char(':');
if (pos == -1) {
r_host = base;
base = "";
@@ -2641,7 +2641,7 @@ int64_t String::to_int() const {
return 0;
}
- int to = (find(".") >= 0) ? find(".") : length();
+ int to = (find_char('.') >= 0) ? find_char('.') : length();
int64_t integer = 0;
int64_t sign = 1;
@@ -4580,7 +4580,7 @@ String String::simplify_path() const {
if (p == -1) {
p = s.find(":\\");
}
- if (p != -1 && p < s.find("/")) {
+ if (p != -1 && p < s.find_char('/')) {
drive = s.substr(0, p + 2);
s = s.substr(p + 2);
}
@@ -5025,7 +5025,7 @@ String String::xml_unescape() const {
String String::pad_decimals(int p_digits) const {
String s = *this;
- int c = s.find(".");
+ int c = s.find_char('.');
if (c == -1) {
if (p_digits <= 0) {
@@ -5049,7 +5049,7 @@ String String::pad_decimals(int p_digits) const {
String String::pad_zeros(int p_digits) const {
String s = *this;
- int end = s.find(".");
+ int end = s.find_char('.');
if (end == -1) {
end = s.length();
@@ -5316,7 +5316,7 @@ String String::validate_filename() const {
}
bool String::is_valid_ip_address() const {
- if (find(":") >= 0) {
+ if (find_char(':') >= 0) {
Vector<String> ip = split(":");
for (int i = 0; i < ip.size(); i++) {
const String &n = ip[i];
@@ -5386,13 +5386,13 @@ String String::get_base_dir() const {
// Windows UNC network share path.
if (end == 0) {
if (is_network_share_path()) {
- basepos = find("/", 2);
+ basepos = find_char('/', 2);
if (basepos == -1) {
- basepos = find("\\", 2);
+ basepos = find_char('\\', 2);
}
- int servpos = find("/", basepos + 1);
+ int servpos = find_char('/', basepos + 1);
if (servpos == -1) {
- servpos = find("\\", basepos + 1);
+ servpos = find_char('\\', basepos + 1);
}
if (servpos != -1) {
end = servpos + 1;
@@ -5416,7 +5416,7 @@ String String::get_base_dir() const {
rs = *this;
}
- int sep = MAX(rs.rfind("/"), rs.rfind("\\"));
+ int sep = MAX(rs.rfind_char('/'), rs.rfind_char('\\'));
if (sep == -1) {
return base;
}
@@ -5425,7 +5425,7 @@ String String::get_base_dir() const {
}
String String::get_file() const {
- int sep = MAX(rfind("/"), rfind("\\"));
+ int sep = MAX(rfind_char('/'), rfind_char('\\'));
if (sep == -1) {
return *this;
}
@@ -5434,8 +5434,8 @@ String String::get_file() const {
}
String String::get_extension() const {
- int pos = rfind(".");
- if (pos < 0 || pos < MAX(rfind("/"), rfind("\\"))) {
+ int pos = rfind_char('.');
+ if (pos < 0 || pos < MAX(rfind_char('/'), rfind_char('\\'))) {
return "";
}
@@ -5533,8 +5533,8 @@ String String::validate_node_name() const {
}
String String::get_basename() const {
- int pos = rfind(".");
- if (pos < 0 || pos < MAX(rfind("/"), rfind("\\"))) {
+ int pos = rfind_char('.');
+ if (pos < 0 || pos < MAX(rfind_char('/'), rfind_char('\\'))) {
return *this;
}
diff --git a/doc/classes/Curve3D.xml b/doc/classes/Curve3D.xml
index 9157649af2..f8386b73b2 100644
--- a/doc/classes/Curve3D.xml
+++ b/doc/classes/Curve3D.xml
@@ -204,6 +204,9 @@
<member name="bake_interval" type="float" setter="set_bake_interval" getter="get_bake_interval" default="0.2">
The distance in meters between two adjacent cached points. Changing it forces the cache to be recomputed the next time the [method get_baked_points] or [method get_baked_length] function is called. The smaller the distance, the more points in the cache and the more memory it will consume, so use with care.
</member>
+ <member name="closed" type="bool" setter="set_closed" getter="is_closed" default="false">
+ If [code]true[/code], and the curve has more than 2 control points, the last point and the first one will be connected in a loop.
+ </member>
<member name="point_count" type="int" setter="set_point_count" getter="get_point_count" default="0">
The number of points describing the curve.
</member>
diff --git a/doc/classes/TextureProgressBar.xml b/doc/classes/TextureProgressBar.xml
index d3e334ef55..c68b521da9 100644
--- a/doc/classes/TextureProgressBar.xml
+++ b/doc/classes/TextureProgressBar.xml
@@ -42,6 +42,7 @@
</member>
<member name="radial_initial_angle" type="float" setter="set_radial_initial_angle" getter="get_radial_initial_angle" default="0.0">
Starting angle for the fill of [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE], [constant FILL_COUNTER_CLOCKWISE], or [constant FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE]. When the node's [code]value[/code] is equal to its [code]min_value[/code], the texture doesn't show up at all. When the [code]value[/code] increases, the texture fills and tends towards [member radial_fill_degrees].
+ [b]Note:[/b] [member radial_initial_angle] is wrapped between [code]0[/code] and [code]360[/code] degrees (inclusive).
</member>
<member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" enum="Control.SizeFlags" is_bitfield="true" default="1" />
<member name="step" type="float" setter="set_step" getter="get_step" overrides="Range" default="1.0" />
diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp
index 0c9635339b..6ee22b6eb5 100644
--- a/drivers/alsa/audio_driver_alsa.cpp
+++ b/drivers/alsa/audio_driver_alsa.cpp
@@ -80,7 +80,7 @@ Error AudioDriverALSA::init_output_device() {
status = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
} else {
String device = output_device_name;
- int pos = device.find(";");
+ int pos = device.find_char(';');
if (pos != -1) {
device = device.substr(0, pos);
}
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 0138f99d50..2fd3f7d7e2 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -687,6 +687,8 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
state.current_tex = RID();
+ const uint64_t base_specialization = GLES3::Config::get_singleton()->float_texture_supported ? 0 : CanvasShaderGLES3::USE_RGBA_SHADOWS;
+
for (uint32_t i = 0; i <= state.current_batch_index; i++) {
// Skipping when there is no instances.
if (state.canvas_instance_batches[i].instance_count == 0) {
@@ -705,10 +707,9 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
}
GLES3::CanvasMaterialData *material_data = state.canvas_instance_batches[i].material_data;
- CanvasShaderGLES3::ShaderVariant variant = state.canvas_instance_batches[i].shader_variant;
- uint64_t specialization = 0;
- specialization |= uint64_t(state.canvas_instance_batches[i].lights_disabled);
- specialization |= uint64_t(!GLES3::Config::get_singleton()->float_texture_supported) << 1;
+ CanvasShaderGLES3::ShaderVariant variant = CanvasShaderGLES3::MODE_DEFAULT;
+ uint64_t specialization = state.canvas_instance_batches[i].specialization;
+ specialization |= base_specialization;
RID shader_version = data.canvas_shader_default_version;
if (material_data) {
@@ -810,6 +811,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, const Point2 &p_repeat_offset) {
RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter;
+ const uint64_t specialization_command_mask = ~(CanvasShaderGLES3::USE_NINEPATCH | CanvasShaderGLES3::USE_PRIMITIVE | CanvasShaderGLES3::USE_ATTRIBUTES | CanvasShaderGLES3::USE_INSTANCING);
if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) {
_new_batch(r_batch_broken);
@@ -868,9 +870,9 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
bool lights_disabled = light_count == 0 && !state.using_directional_lights;
- if (lights_disabled != state.canvas_instance_batches[state.current_batch_index].lights_disabled) {
+ if (lights_disabled != bool(state.canvas_instance_batches[state.current_batch_index].specialization & CanvasShaderGLES3::DISABLE_LIGHTING)) {
_new_batch(r_batch_broken);
- state.canvas_instance_batches[state.current_batch_index].lights_disabled = lights_disabled;
+ state.canvas_instance_batches[state.current_batch_index].specialization ^= CanvasShaderGLES3::DISABLE_LIGHTING;
}
const Item::Command *c = p_item->commands;
@@ -936,7 +938,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.canvas_instance_batches[state.current_batch_index].tex = rect->texture;
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_RECT;
state.canvas_instance_batches[state.current_batch_index].command = c;
- state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_QUAD;
+ state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
}
_prepare_canvas_texture(rect->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
@@ -1026,7 +1028,8 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.canvas_instance_batches[state.current_batch_index].tex = np->texture;
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_NINEPATCH;
state.canvas_instance_batches[state.current_batch_index].command = c;
- state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_NINEPATCH;
+ state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
+ state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_NINEPATCH;
}
_prepare_canvas_texture(np->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
@@ -1092,7 +1095,8 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.canvas_instance_batches[state.current_batch_index].tex = polygon->texture;
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_POLYGON;
state.canvas_instance_batches[state.current_batch_index].command = c;
- state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES;
+ state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
+ state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_ATTRIBUTES;
_prepare_canvas_texture(polygon->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
@@ -1119,7 +1123,8 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.canvas_instance_batches[state.current_batch_index].primitive_points = primitive->point_count;
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_PRIMITIVE;
state.canvas_instance_batches[state.current_batch_index].command = c;
- state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_PRIMITIVE;
+ state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
+ state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_PRIMITIVE;
}
_prepare_canvas_texture(state.canvas_instance_batches[state.current_batch_index].tex, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
@@ -1164,7 +1169,8 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
_new_batch(r_batch_broken);
Color modulate(1, 1, 1, 1);
- state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES;
+ state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
+ state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_ATTRIBUTES;
if (c->type == Item::Command::TYPE_MESH) {
const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c);
state.canvas_instance_batches[state.current_batch_index].tex = m->texture;
@@ -1174,7 +1180,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
} else if (c->type == Item::Command::TYPE_MULTIMESH) {
const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(c);
state.canvas_instance_batches[state.current_batch_index].tex = mm->texture;
- state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED;
+ state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_INSTANCING;
if (GLES3::MeshStorage::get_singleton()->multimesh_uses_colors(mm->multimesh)) {
state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_COLORS;
@@ -1189,7 +1195,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(c);
RID particles = pt->particles;
state.canvas_instance_batches[state.current_batch_index].tex = pt->texture;
- state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED;
+ state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_INSTANCING;
state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_COLORS;
state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA;
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index a82e2713e0..b9d9a44e2a 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -273,14 +273,12 @@ public:
RID material;
GLES3::CanvasMaterialData *material_data = nullptr;
- CanvasShaderGLES3::ShaderVariant shader_variant = CanvasShaderGLES3::MODE_QUAD;
uint64_t vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_COLOR | RS::ARRAY_FORMAT_TEX_UV;
+ uint64_t specialization = 0;
const Item::Command *command = nullptr;
Item::Command::Type command_type = Item::Command::TYPE_ANIMATION_SLICE; // Can default to any type that doesn't form a batch.
uint32_t primitive_points = 0;
-
- bool lights_disabled = false;
};
// DataBuffer contains our per-frame data. I.e. the resources that are updated each frame.
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index 5e7fb3b338..1ac289d5a2 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -1,17 +1,16 @@
/* clang-format off */
#[modes]
-mode_quad =
-mode_ninepatch = #define USE_NINEPATCH
-mode_primitive = #define USE_PRIMITIVE
-mode_attributes = #define USE_ATTRIBUTES
-mode_instanced = #define USE_ATTRIBUTES \n#define USE_INSTANCING
+mode_default =
#[specializations]
DISABLE_LIGHTING = true
USE_RGBA_SHADOWS = false
-SINGLE_INSTANCE = false
+USE_NINEPATCH = false
+USE_PRIMITIVE = false
+USE_ATTRIBUTES = false
+USE_INSTANCING = false
#[vertex]
diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl
index 186b630bc8..043023aee0 100644
--- a/drivers/gles3/shaders/sky.glsl
+++ b/drivers/gles3/shaders/sky.glsl
@@ -2,17 +2,15 @@
#[modes]
mode_background =
-mode_half_res = #define USE_HALF_RES_PASS
-mode_quarter_res = #define USE_QUARTER_RES_PASS
mode_cubemap = #define USE_CUBEMAP_PASS
-mode_cubemap_half_res = #define USE_CUBEMAP_PASS \n#define USE_HALF_RES_PASS
-mode_cubemap_quarter_res = #define USE_CUBEMAP_PASS \n#define USE_QUARTER_RES_PASS
#[specializations]
USE_MULTIVIEW = false
USE_INVERTED_Y = true
APPLY_TONEMAPPING = true
+USE_QUARTER_RES_PASS = false
+USE_HALF_RES_PASS = false
#[vertex]
diff --git a/drivers/metal/rendering_device_driver_metal.mm b/drivers/metal/rendering_device_driver_metal.mm
index 3d58d535eb..784c9d5ae8 100644
--- a/drivers/metal/rendering_device_driver_metal.mm
+++ b/drivers/metal/rendering_device_driver_metal.mm
@@ -2020,7 +2020,8 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
ERR_FAIL_COND_V_MSG(compiler.get_entry_points_and_stages().size() != 1, Result(), "Expected a single entry point and stage.");
- EntryPoint &entry_point_stage = compiler.get_entry_points_and_stages().front();
+ SmallVector<EntryPoint> entry_pts_stages = compiler.get_entry_points_and_stages();
+ EntryPoint &entry_point_stage = entry_pts_stages.front();
SPIREntryPoint &entry_point = compiler.get_entry_point(entry_point_stage.name, entry_point_stage.execution_model);
// Process specialization constants.
diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp
index 5987c1675d..5a1a26e874 100644
--- a/drivers/unix/ip_unix.cpp
+++ b/drivers/unix/ip_unix.cpp
@@ -28,22 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#include "ip_unix.h"
-
-#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
-
-#ifdef WINDOWS_ENABLED
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <winsock2.h>
-#include <ws2tcpip.h>
-
-#include <iphlpapi.h>
-
-#include <stdio.h>
+#if defined(UNIX_ENABLED)
-#else // UNIX
+#include "ip_unix.h"
#include <netdb.h>
@@ -67,8 +54,6 @@
#include <net/if.h> // Order is important on OpenBSD, leave as last.
-#endif // UNIX
-
#include <string.h>
static IPAddress _sockaddr2ip(struct sockaddr *p_addr) {
@@ -108,7 +93,7 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos
}
if (result == nullptr || result->ai_addr == nullptr) {
- print_verbose("Invalid response from getaddrinfo");
+ print_verbose("Invalid response from getaddrinfo.");
if (result) {
freeaddrinfo(result);
}
@@ -132,56 +117,6 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos
freeaddrinfo(result);
}
-#if defined(WINDOWS_ENABLED)
-
-void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
- ULONG buf_size = 1024;
- IP_ADAPTER_ADDRESSES *addrs;
-
- while (true) {
- addrs = (IP_ADAPTER_ADDRESSES *)memalloc(buf_size);
- int err = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
- nullptr, addrs, &buf_size);
- if (err == NO_ERROR) {
- break;
- }
- memfree(addrs);
- if (err == ERROR_BUFFER_OVERFLOW) {
- continue; // will go back and alloc the right size
- }
-
- ERR_FAIL_MSG("Call to GetAdaptersAddresses failed with error " + itos(err) + ".");
- }
-
- IP_ADAPTER_ADDRESSES *adapter = addrs;
-
- while (adapter != nullptr) {
- Interface_Info info;
- info.name = adapter->AdapterName;
- info.name_friendly = adapter->FriendlyName;
- info.index = String::num_uint64(adapter->IfIndex);
-
- IP_ADAPTER_UNICAST_ADDRESS *address = adapter->FirstUnicastAddress;
- while (address != nullptr) {
- int family = address->Address.lpSockaddr->sa_family;
- if (family != AF_INET && family != AF_INET6) {
- continue;
- }
- info.ip_addresses.push_front(_sockaddr2ip(address->Address.lpSockaddr));
- address = address->Next;
- }
- adapter = adapter->Next;
- // Only add interface if it has at least one IP
- if (info.ip_addresses.size() > 0) {
- r_interfaces->insert(info.name, info);
- }
- }
-
- memfree(addrs);
-}
-
-#else // UNIX
-
void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
struct ifaddrs *ifAddrStruct = nullptr;
struct ifaddrs *ifa = nullptr;
@@ -219,8 +154,6 @@ void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces)
}
}
-#endif // UNIX
-
void IPUnix::make_default() {
_create = _create_unix;
}
@@ -232,4 +165,4 @@ IP *IPUnix::_create_unix() {
IPUnix::IPUnix() {
}
-#endif // UNIX_ENABLED || WINDOWS_ENABLED
+#endif // UNIX_ENABLED
diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h
index 274b7c561e..7e496629ef 100644
--- a/drivers/unix/ip_unix.h
+++ b/drivers/unix/ip_unix.h
@@ -31,9 +31,9 @@
#ifndef IP_UNIX_H
#define IP_UNIX_H
-#include "core/io/ip.h"
+#if defined(UNIX_ENABLED)
-#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
+#include "core/io/ip.h"
class IPUnix : public IP {
GDCLASS(IPUnix, IP);
@@ -49,6 +49,6 @@ public:
IPUnix();
};
-#endif
+#endif // UNIX_ENABLED
#endif // IP_UNIX_H
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index 3976ae7d84..ffc270cd36 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -860,7 +860,7 @@ String OS_Unix::get_locale() const {
}
String locale = get_environment("LANG");
- int tp = locale.find(".");
+ int tp = locale.find_char('.');
if (tp != -1) {
locale = locale.substr(0, tp);
}
diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp
index 24a26b56ef..3ddbde72c4 100644
--- a/drivers/windows/dir_access_windows.cpp
+++ b/drivers/windows/dir_access_windows.cpp
@@ -230,7 +230,7 @@ String DirAccessWindows::get_current_dir(bool p_include_drive) const {
return cdir;
} else {
if (_get_root_string().is_empty()) {
- int pos = cdir.find(":");
+ int pos = cdir.find_char(':');
if (pos != -1) {
return cdir.substr(pos + 1);
}
@@ -344,7 +344,7 @@ String DirAccessWindows::get_filesystem_type() const {
return "Network Share";
}
- int unit_end = path.find(":");
+ int unit_end = path.find_char(':');
ERR_FAIL_COND_V(unit_end == -1, String());
String unit = path.substr(0, unit_end + 1) + "\\";
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index 7d2247d41a..4a0e5e5f49 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -64,7 +64,7 @@ bool FileAccessWindows::is_path_invalid(const String &p_path) {
// Check for invalid operating system file.
String fname = p_path.get_file().to_lower();
- int dot = fname.find(".");
+ int dot = fname.find_char('.');
if (dot != -1) {
fname = fname.substr(0, dot);
}
diff --git a/drivers/windows/ip_windows.cpp b/drivers/windows/ip_windows.cpp
new file mode 100644
index 0000000000..4ff368d3f4
--- /dev/null
+++ b/drivers/windows/ip_windows.cpp
@@ -0,0 +1,164 @@
+/**************************************************************************/
+/* ip_windows.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#if defined(WINDOWS_ENABLED)
+
+#include "ip_windows.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include <iphlpapi.h>
+
+#include <stdio.h>
+
+#include <string.h>
+
+static IPAddress _sockaddr2ip(struct sockaddr *p_addr) {
+ IPAddress ip;
+
+ if (p_addr->sa_family == AF_INET) {
+ struct sockaddr_in *addr = (struct sockaddr_in *)p_addr;
+ ip.set_ipv4((uint8_t *)&(addr->sin_addr));
+ } else if (p_addr->sa_family == AF_INET6) {
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;
+ ip.set_ipv6(addr6->sin6_addr.s6_addr);
+ }
+
+ return ip;
+}
+
+void IPWindows::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hostname, Type p_type) const {
+ struct addrinfo hints;
+ struct addrinfo *result = nullptr;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ if (p_type == TYPE_IPV4) {
+ hints.ai_family = AF_INET;
+ } else if (p_type == TYPE_IPV6) {
+ hints.ai_family = AF_INET6;
+ hints.ai_flags = 0;
+ } else {
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_ADDRCONFIG;
+ }
+ hints.ai_flags &= ~AI_NUMERICHOST;
+
+ int s = getaddrinfo(p_hostname.utf8().get_data(), nullptr, &hints, &result);
+ if (s != 0) {
+ print_verbose("getaddrinfo failed! Cannot resolve hostname.");
+ return;
+ }
+
+ if (result == nullptr || result->ai_addr == nullptr) {
+ print_verbose("Invalid response from getaddrinfo.");
+ if (result) {
+ freeaddrinfo(result);
+ }
+ return;
+ }
+
+ struct addrinfo *next = result;
+
+ do {
+ if (next->ai_addr == nullptr) {
+ next = next->ai_next;
+ continue;
+ }
+ IPAddress ip = _sockaddr2ip(next->ai_addr);
+ if (ip.is_valid() && !r_addresses.find(ip)) {
+ r_addresses.push_back(ip);
+ }
+ next = next->ai_next;
+ } while (next);
+
+ freeaddrinfo(result);
+}
+
+void IPWindows::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
+ ULONG buf_size = 1024;
+ IP_ADAPTER_ADDRESSES *addrs;
+
+ while (true) {
+ addrs = (IP_ADAPTER_ADDRESSES *)memalloc(buf_size);
+ int err = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
+ nullptr, addrs, &buf_size);
+ if (err == NO_ERROR) {
+ break;
+ }
+ memfree(addrs);
+ if (err == ERROR_BUFFER_OVERFLOW) {
+ continue; // Will go back and alloc the right size.
+ }
+
+ ERR_FAIL_MSG("Call to GetAdaptersAddresses failed with error " + itos(err) + ".");
+ }
+
+ IP_ADAPTER_ADDRESSES *adapter = addrs;
+
+ while (adapter != nullptr) {
+ Interface_Info info;
+ info.name = adapter->AdapterName;
+ info.name_friendly = adapter->FriendlyName;
+ info.index = String::num_uint64(adapter->IfIndex);
+
+ IP_ADAPTER_UNICAST_ADDRESS *address = adapter->FirstUnicastAddress;
+ while (address != nullptr) {
+ int family = address->Address.lpSockaddr->sa_family;
+ if (family != AF_INET && family != AF_INET6) {
+ continue;
+ }
+ info.ip_addresses.push_front(_sockaddr2ip(address->Address.lpSockaddr));
+ address = address->Next;
+ }
+ adapter = adapter->Next;
+ // Only add interface if it has at least one IP.
+ if (info.ip_addresses.size() > 0) {
+ r_interfaces->insert(info.name, info);
+ }
+ }
+
+ memfree(addrs);
+}
+
+void IPWindows::make_default() {
+ _create = _create_unix;
+}
+
+IP *IPWindows::_create_unix() {
+ return memnew(IPWindows);
+}
+
+IPWindows::IPWindows() {
+}
+
+#endif // WINDOWS_ENABLED
diff --git a/drivers/windows/ip_windows.h b/drivers/windows/ip_windows.h
new file mode 100644
index 0000000000..5788d33ee2
--- /dev/null
+++ b/drivers/windows/ip_windows.h
@@ -0,0 +1,54 @@
+/**************************************************************************/
+/* ip_windows.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef IP_WINDOWS_H
+#define IP_WINDOWS_H
+
+#if defined(WINDOWS_ENABLED)
+
+#include "core/io/ip.h"
+
+class IPWindows : public IP {
+ GDCLASS(IPWindows, IP);
+
+ virtual void _resolve_hostname(List<IPAddress> &r_addresses, const String &p_hostname, Type p_type = TYPE_ANY) const override;
+
+ static IP *_create_unix();
+
+public:
+ virtual void get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const override;
+
+ static void make_default();
+ IPWindows();
+};
+
+#endif // WINDOWS_ENABLED
+
+#endif // IP_WINDOWS_H
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp
index b923dc0732..36ca417638 100644
--- a/editor/animation_bezier_editor.cpp
+++ b/editor/animation_bezier_editor.cpp
@@ -275,7 +275,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
}
String base_path = animation->track_get_path(i);
- int end = base_path.find(":");
+ int end = base_path.find_char(':');
if (end != -1) {
base_path = base_path.substr(0, end + 1);
}
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 55ea7c0082..7a1296c411 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -4340,7 +4340,7 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p
if (track_path == np) {
actual_value = value; // All good.
} else {
- int sep = track_path.rfind(":");
+ int sep = track_path.rfind_char(':');
if (sep != -1) {
String base_path = track_path.substr(0, sep);
if (base_path == np) {
@@ -6495,7 +6495,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
path = NodePath(node->get_path().get_names(), path.get_subnames(), true); // Store full path instead for copying.
} else {
text = path;
- int sep = text.find(":");
+ int sep = text.find_char(':');
if (sep != -1) {
text = text.substr(sep + 1, text.length());
}
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index b114977c3b..d76c324be0 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -524,7 +524,7 @@ void ConnectDialog::set_dst_node(Node *p_node) {
StringName ConnectDialog::get_dst_method_name() const {
String txt = dst_method->get_text();
if (txt.contains("(")) {
- txt = txt.left(txt.find("(")).strip_edges();
+ txt = txt.left(txt.find_char('(')).strip_edges();
}
return txt;
}
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 78dc772d9e..2273014f72 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -160,13 +160,13 @@ bool CreateDialog::_should_hide_type(const StringName &p_type) const {
String script_path = ScriptServer::get_global_class_path(p_type);
if (script_path.begins_with("res://addons/")) {
- int i = script_path.find("/", 13); // 13 is length of "res://addons/".
+ int i = script_path.find_char('/', 13); // 13 is length of "res://addons/".
while (i > -1) {
const String plugin_path = script_path.substr(0, i).path_join("plugin.cfg");
if (FileAccess::exists(plugin_path)) {
return !EditorNode::get_singleton()->is_addon_plugin_enabled(plugin_path);
}
- i = script_path.find("/", i + 1);
+ i = script_path.find_char('/', i + 1);
}
}
}
diff --git a/editor/debugger/debug_adapter/debug_adapter_parser.cpp b/editor/debugger/debug_adapter/debug_adapter_parser.cpp
index 2af629676a..904085630b 100644
--- a/editor/debugger/debug_adapter/debug_adapter_parser.cpp
+++ b/editor/debugger/debug_adapter/debug_adapter_parser.cpp
@@ -147,7 +147,7 @@ Dictionary DebugAdapterParser::req_initialize(const Dictionary &p_params) const
for (List<String>::Element *E = breakpoints.front(); E; E = E->next()) {
String breakpoint = E->get();
- String path = breakpoint.left(breakpoint.find(":", 6)); // Skip initial part of path, aka "res://"
+ String path = breakpoint.left(breakpoint.find_char(':', 6)); // Skip initial part of path, aka "res://"
int line = breakpoint.substr(path.size()).to_int();
DebugAdapterProtocol::get_singleton()->on_debug_breakpoint_toggled(path, line, true);
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index 0f948b4ed5..909651da45 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -160,9 +160,9 @@ void EditorDebuggerNode::_text_editor_stack_goto(const ScriptEditorDebugger *p_d
} else {
// If the script is built-in, it can be opened only if the scene is loaded in memory.
int i = file.find("::");
- int j = file.rfind("(", i);
+ int j = file.rfind_char('(', i);
if (j > -1) { // If the script is named, the string is "name (file)", so we need to extract the path.
- file = file.substr(j + 1, file.find(")", i) - j - 1);
+ file = file.substr(j + 1, file.find_char(')', i) - j - 1);
}
Ref<PackedScene> ps = ResourceLoader::load(file.get_slice("::", 0));
stack_script = ResourceLoader::load(file);
@@ -183,9 +183,9 @@ void EditorDebuggerNode::_text_editor_stack_clear(const ScriptEditorDebugger *p_
} else {
// If the script is built-in, it can be opened only if the scene is loaded in memory.
int i = file.find("::");
- int j = file.rfind("(", i);
+ int j = file.rfind_char('(', i);
if (j > -1) { // If the script is named, the string is "name (file)", so we need to extract the path.
- file = file.substr(j + 1, file.find(")", i) - j - 1);
+ file = file.substr(j + 1, file.find_char(')', i) - j - 1);
}
Ref<PackedScene> ps = ResourceLoader::load(file.get_slice("::", 0));
stack_script = ResourceLoader::load(file);
diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp
index 4d67800e6e..a9e4adf674 100644
--- a/editor/debugger/editor_debugger_tree.cpp
+++ b/editor/debugger/editor_debugger_tree.cpp
@@ -382,7 +382,7 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) {
text = ".";
} else {
text = text.replace("/root/", "");
- int slash = text.find("/");
+ int slash = text.find_char('/');
if (slash < 0) {
text = ".";
} else {
diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp
index 8ba5811ffa..9ca12070fe 100644
--- a/editor/dependency_editor.cpp
+++ b/editor/dependency_editor.cpp
@@ -471,7 +471,7 @@ void DependencyRemoveDialog::_find_localization_remaps_of_removed_files(Vector<R
for (int j = 0; j < remap_keys.size(); j++) {
PackedStringArray remapped_files = remaps[remap_keys[j]];
for (int k = 0; k < remapped_files.size(); k++) {
- int splitter_pos = remapped_files[k].rfind(":");
+ int splitter_pos = remapped_files[k].rfind_char(':');
String res_path = remapped_files[k].substr(0, splitter_pos);
if (res_path == path) {
String locale_name = remapped_files[k].substr(splitter_pos + 1);
diff --git a/editor/directory_create_dialog.cpp b/editor/directory_create_dialog.cpp
index ee03d5a7f6..d68af88fc8 100644
--- a/editor/directory_create_dialog.cpp
+++ b/editor/directory_create_dialog.cpp
@@ -142,7 +142,7 @@ void DirectoryCreateDialog::config(const String &p_base_dir, const Callable &p_a
validation_panel->update();
if (p_mode == MODE_FILE) {
- int extension_pos = p_default_name.rfind(".");
+ int extension_pos = p_default_name.rfind_char('.');
if (extension_pos > -1) {
dir_path->select(0, extension_pos);
return;
diff --git a/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp
index bce0c87452..72755e8943 100644
--- a/editor/editor_asset_installer.cpp
+++ b/editor/editor_asset_installer.cpp
@@ -130,14 +130,14 @@ void EditorAssetInstaller::open_asset(const String &p_path, bool p_autoskip_topl
// Create intermediate directories if they aren't reported by unzip.
// We are only interested in subfolders, so skip the root slash.
- int separator = source_name.find("/", 1);
+ int separator = source_name.find_char('/', 1);
while (separator != -1) {
String dir_name = source_name.substr(0, separator + 1);
if (!dir_name.is_empty() && !asset_files.has(dir_name)) {
asset_files.insert(dir_name);
}
- separator = source_name.find("/", separator + 1);
+ separator = source_name.find_char('/', separator + 1);
}
if (!source_name.is_empty() && !asset_files.has(source_name)) {
@@ -214,7 +214,7 @@ void EditorAssetInstaller::_rebuild_source_tree() {
TreeItem *parent_item;
- int separator = path.rfind("/");
+ int separator = path.rfind_char('/');
if (separator == -1) {
parent_item = root;
} else {
@@ -313,7 +313,7 @@ void EditorAssetInstaller::_rebuild_destination_tree() {
TreeItem *parent_item;
- int separator = path.rfind("/");
+ int separator = path.rfind_char('/');
if (separator == -1) {
parent_item = root;
} else {
diff --git a/editor/editor_folding.cpp b/editor/editor_folding.cpp
index 18f5610655..5cb38fa875 100644
--- a/editor/editor_folding.cpp
+++ b/editor/editor_folding.cpp
@@ -249,7 +249,7 @@ void EditorFolding::_do_object_unfolds(Object *p_object, HashSet<Ref<Resource>>
}
}
} else { //path
- int last = E.name.rfind("/");
+ int last = E.name.rfind_char('/');
if (last != -1) {
bool can_revert = EditorPropertyRevert::can_property_revert(p_object, E.name);
if (can_revert) {
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 0ca1ed2d50..9aec9d6b15 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -148,7 +148,7 @@ static String _contextualize_class_specifier(const String &p_class_specifier, co
// Here equal length + begins_with from above implies p_class_specifier == p_edited_class :)
if (p_class_specifier.length() == p_edited_class.length()) {
- int rfind = p_class_specifier.rfind(".");
+ int rfind = p_class_specifier.rfind_char('.');
if (rfind == -1) { // Single identifier
return p_class_specifier;
}
@@ -234,7 +234,7 @@ void EditorHelp::_class_desc_select(const String &p_select) {
enum_class_name = "@GlobalScope";
enum_name = link;
} else {
- const int dot_pos = link.rfind(".");
+ const int dot_pos = link.rfind_char('.');
if (dot_pos >= 0) {
enum_class_name = link.left(dot_pos);
enum_name = link.substr(dot_pos + 1);
@@ -3252,7 +3252,7 @@ EditorHelpBit::HelpData EditorHelpBit::_get_property_help_data(const StringName
enum_class_name = "@GlobalScope";
enum_name = property.enumeration;
} else {
- const int dot_pos = property.enumeration.rfind(".");
+ const int dot_pos = property.enumeration.rfind_char('.');
if (dot_pos >= 0) {
enum_class_name = property.enumeration.left(dot_pos);
enum_name = property.enumeration.substr(dot_pos + 1);
@@ -3619,7 +3619,7 @@ void EditorHelpBit::_meta_clicked(const String &p_select) {
enum_class_name = "@GlobalScope";
enum_name = link;
} else {
- const int dot_pos = link.rfind(".");
+ const int dot_pos = link.rfind_char('.');
if (dot_pos >= 0) {
enum_class_name = link.left(dot_pos);
enum_name = link.substr(dot_pos + 1);
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index fa5a059aa0..6b3c6b462d 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -3131,7 +3131,7 @@ void EditorInspector::update_tree() {
if (!array_prefix.is_empty()) {
path = path.trim_prefix(array_prefix);
- int char_index = path.find("/");
+ int char_index = path.find_char('/');
if (char_index >= 0) {
path = path.right(-char_index - 1);
} else {
@@ -3171,10 +3171,10 @@ void EditorInspector::update_tree() {
}
// Get the property label's string.
- String name_override = (path.contains("/")) ? path.substr(path.rfind("/") + 1) : path;
+ String name_override = (path.contains("/")) ? path.substr(path.rfind_char('/') + 1) : path;
String feature_tag;
{
- const int dot = name_override.find(".");
+ const int dot = name_override.find_char('.');
if (dot != -1) {
feature_tag = name_override.substr(dot);
name_override = name_override.substr(0, dot);
@@ -3189,7 +3189,7 @@ void EditorInspector::update_tree() {
const String property_label_string = EditorPropertyNameProcessor::get_singleton()->process_name(name_override, name_style, p.name, doc_name) + feature_tag;
// Remove the property from the path.
- int idx = path.rfind("/");
+ int idx = path.rfind_char('/');
if (idx > -1) {
path = path.left(idx);
} else {
@@ -3320,7 +3320,7 @@ void EditorInspector::update_tree() {
array_element_prefix = class_name_components[0];
editor_inspector_array = memnew(EditorInspectorArray(all_read_only));
- String array_label = path.contains("/") ? path.substr(path.rfind("/") + 1) : path;
+ String array_label = path.contains("/") ? path.substr(path.rfind_char('/') + 1) : path;
array_label = EditorPropertyNameProcessor::get_singleton()->process_name(property_label_string, property_name_style, p.name, doc_name);
int page = per_array_page.has(array_element_prefix) ? per_array_page[array_element_prefix] : 0;
editor_inspector_array->setup_with_move_element_function(object, array_label, array_element_prefix, page, c, use_folding);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 87f1f1b8a0..f056a477c4 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -253,8 +253,8 @@ void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vecto
// append that to yield "folder/foo.tscn".
if (difference > 0) {
String parent = full_path.substr(0, difference);
- int slash_idx = parent.rfind("/");
- slash_idx = parent.rfind("/", slash_idx - 1);
+ int slash_idx = parent.rfind_char('/');
+ slash_idx = parent.rfind_char('/', slash_idx - 1);
parent = (slash_idx >= 0 && parent.length() > 1) ? parent.substr(slash_idx + 1) : parent;
r_filenames.write[set_idx] = parent + r_filenames[set_idx];
}
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 6b6f47d9cf..2b2b32eb22 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -463,10 +463,12 @@ void EditorPropertyPath::_set_read_only(bool p_read_only) {
void EditorPropertyPath::_path_selected(const String &p_path) {
String full_path = p_path;
- ResourceUID::ID id = ResourceLoader::get_resource_uid(full_path);
- if (id != ResourceUID::INVALID_ID) {
- full_path = ResourceUID::get_singleton()->id_to_text(id);
+ if (!global) {
+ const ResourceUID::ID id = ResourceLoader::get_resource_uid(full_path);
+ if (id != ResourceUID::INVALID_ID) {
+ full_path = ResourceUID::get_singleton()->id_to_text(id);
+ }
}
emit_changed(get_edited_property(), full_path);
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 4b4c02f49b..3cc3a0f7c2 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -740,10 +740,10 @@ void EditorPropertyArray::setup(Variant::Type p_array_type, const String &p_hint
// The format of p_hint_string is:
// subType/subTypeHint:nextSubtype ... etc.
if (!p_hint_string.is_empty()) {
- int hint_subtype_separator = p_hint_string.find(":");
+ int hint_subtype_separator = p_hint_string.find_char(':');
if (hint_subtype_separator >= 0) {
String subtype_string = p_hint_string.substr(0, hint_subtype_separator);
- int slash_pos = subtype_string.find("/");
+ int slash_pos = subtype_string.find_char('/');
if (slash_pos >= 0) {
subtype_hint = PropertyHint(subtype_string.substr(slash_pos + 1, subtype_string.size() - slash_pos - 1).to_int());
subtype_string = subtype_string.substr(0, slash_pos);
@@ -1006,10 +1006,10 @@ void EditorPropertyDictionary::setup(PropertyHint p_hint, const String &p_hint_s
PackedStringArray types = p_hint_string.split(";");
if (types.size() > 0 && !types[0].is_empty()) {
String key = types[0];
- int hint_key_subtype_separator = key.find(":");
+ int hint_key_subtype_separator = key.find_char(':');
if (hint_key_subtype_separator >= 0) {
String key_subtype_string = key.substr(0, hint_key_subtype_separator);
- int slash_pos = key_subtype_string.find("/");
+ int slash_pos = key_subtype_string.find_char('/');
if (slash_pos >= 0) {
key_subtype_hint = PropertyHint(key_subtype_string.substr(slash_pos + 1, key_subtype_string.size() - slash_pos - 1).to_int());
key_subtype_string = key_subtype_string.substr(0, slash_pos);
@@ -1025,10 +1025,10 @@ void EditorPropertyDictionary::setup(PropertyHint p_hint, const String &p_hint_s
}
if (types.size() > 1 && !types[1].is_empty()) {
String value = types[1];
- int hint_value_subtype_separator = value.find(":");
+ int hint_value_subtype_separator = value.find_char(':');
if (hint_value_subtype_separator >= 0) {
String value_subtype_string = value.substr(0, hint_value_subtype_separator);
- int slash_pos = value_subtype_string.find("/");
+ int slash_pos = value_subtype_string.find_char('/');
if (slash_pos >= 0) {
value_subtype_hint = PropertyHint(value_subtype_string.substr(slash_pos + 1, value_subtype_string.size() - slash_pos - 1).to_int());
value_subtype_string = value_subtype_string.substr(0, slash_pos);
diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp
index 27cbb9810c..d88cc4d5fa 100644
--- a/editor/editor_sectioned_inspector.cpp
+++ b/editor/editor_sectioned_inspector.cpp
@@ -96,7 +96,7 @@ class SectionedInspectorFilter : public Object {
List<PropertyInfo> pinfo;
edited->get_property_list(&pinfo);
for (PropertyInfo &pi : pinfo) {
- int sp = pi.name.find("/");
+ int sp = pi.name.find_char('/');
if (pi.name == "resource_path" || pi.name == "resource_name" || pi.name == "resource_local_to_scene" || pi.name.begins_with("script/") || pi.name.begins_with("_global_script")) { //skip resource stuff
continue;
@@ -255,7 +255,7 @@ void SectionedInspector::update_category_list() {
continue;
}
- int sp = pi.name.find("/");
+ int sp = pi.name.find_char('/');
if (sp == -1) {
pi.name = "global/" + pi.name;
}
diff --git a/editor/engine_update_label.cpp b/editor/engine_update_label.cpp
index facbfc7c6b..0d40181257 100644
--- a/editor/engine_update_label.cpp
+++ b/editor/engine_update_label.cpp
@@ -240,8 +240,8 @@ EngineUpdateLabel::VersionType EngineUpdateLabel::_get_version_type(const String
}
String EngineUpdateLabel::_extract_sub_string(const String &p_line) const {
- int j = p_line.find("\"") + 1;
- return p_line.substr(j, p_line.find("\"", j) - j);
+ int j = p_line.find_char('"') + 1;
+ return p_line.substr(j, p_line.find_char('"', j) - j);
}
void EngineUpdateLabel::_notification(int p_what) {
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 3921cde71e..c7e12d1f3b 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -137,7 +137,7 @@ bool FileSystemList::edit_selected() {
String name = get_item_text(s);
line_editor->set_text(name);
- line_editor->select(0, name.rfind("."));
+ line_editor->select(0, name.rfind_char('.'));
popup_edit_commited = false; // Start edit popup processing.
popup_editor->popup();
@@ -2432,7 +2432,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
if (to_rename.is_file) {
String name = to_rename.path.get_file();
- tree->set_editor_selection(0, name.rfind("."));
+ tree->set_editor_selection(0, name.rfind_char('.'));
} else {
String name = to_rename.path.left(-1).get_file(); // Removes the "/" suffix for folders.
tree->set_editor_selection(0, name.length());
@@ -3658,10 +3658,10 @@ void FileSystemDock::_file_list_gui_input(Ref<InputEvent> p_event) {
tree_item->select(0);
} else {
// Find parent folder.
- fpath = fpath.substr(0, fpath.rfind("/") + 1);
+ fpath = fpath.substr(0, fpath.rfind_char('/') + 1);
if (fpath.size() > String("res://").size()) {
fpath = fpath.left(fpath.size() - 2); // Remove last '/'.
- const int slash_idx = fpath.rfind("/");
+ const int slash_idx = fpath.rfind_char('/');
fpath = fpath.substr(slash_idx + 1, fpath.size() - slash_idx - 1);
}
diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp
index 12f00b7a57..0381609804 100644
--- a/editor/gui/editor_file_dialog.cpp
+++ b/editor/gui/editor_file_dialog.cpp
@@ -156,7 +156,7 @@ void EditorFileDialog::popup_file_dialog() {
}
void EditorFileDialog::_focus_file_text() {
- int lp = file->get_text().rfind(".");
+ int lp = file->get_text().rfind_char('.');
if (lp != -1) {
file->select(0, lp);
file->grab_focus();
@@ -1263,7 +1263,7 @@ void EditorFileDialog::set_current_path(const String &p_path) {
if (!p_path.size()) {
return;
}
- int pos = MAX(p_path.rfind("/"), p_path.rfind("\\"));
+ int pos = MAX(p_path.rfind_char('/'), p_path.rfind_char('\\'));
if (pos == -1) {
set_current_file(p_path);
} else {
diff --git a/editor/import/resource_importer_imagefont.cpp b/editor/import/resource_importer_imagefont.cpp
index 44ae2b5ff1..950058e88e 100644
--- a/editor/import/resource_importer_imagefont.cpp
+++ b/editor/import/resource_importer_imagefont.cpp
@@ -199,7 +199,7 @@ Error ResourceImporterImageFont::import(ResourceUID::ID p_source_id, const Strin
case STEP_OFF_Y_BEGIN: {
// Read advance and offset.
if (range[c] == ' ') {
- int next = range.find(" ", c + 1);
+ int next = range.find_char(' ', c + 1);
if (next < c) {
next = range.length();
}
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index 8d6f4e0a70..d72c15bc2a 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -590,7 +590,7 @@ Error ResourceImporterTexture::import(ResourceUID::ID p_source_id, const String
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
const Color color = target_image->get_pixel(i, j);
- target_image->set_pixel(i, j, Color(color.r, 1 - color.g, color.b));
+ target_image->set_pixel(i, j, Color(color.r, 1 - color.g, color.b, color.a));
}
}
}
diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp
index 921467ccbc..7b2e6e81ee 100644
--- a/editor/localization_editor.cpp
+++ b/editor/localization_editor.cpp
@@ -441,7 +441,7 @@ void LocalizationEditor::_filesystem_files_moved(const String &p_old_file, const
bool remapped_files_updated = false;
for (int j = 0; j < remapped_files.size(); j++) {
- int splitter_pos = remapped_files[j].rfind(":");
+ int splitter_pos = remapped_files[j].rfind_char(':');
String res_path = remapped_files[j].substr(0, splitter_pos);
if (res_path == p_old_file) {
@@ -482,7 +482,7 @@ void LocalizationEditor::_filesystem_file_removed(const String &p_file) {
for (int i = 0; i < remap_keys.size() && !remaps_changed; i++) {
PackedStringArray remapped_files = remaps[remap_keys[i]];
for (int j = 0; j < remapped_files.size() && !remaps_changed; j++) {
- int splitter_pos = remapped_files[j].rfind(":");
+ int splitter_pos = remapped_files[j].rfind_char(':');
String res_path = remapped_files[j].substr(0, splitter_pos);
remaps_changed = p_file == res_path;
if (remaps_changed) {
@@ -567,7 +567,7 @@ void LocalizationEditor::update_translations() {
PackedStringArray selected = remaps[keys[i]];
for (int j = 0; j < selected.size(); j++) {
const String &s2 = selected[j];
- int qp = s2.rfind(":");
+ int qp = s2.rfind_char(':');
String path = s2.substr(0, qp);
String locale = s2.substr(qp + 1, s2.length());
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index 8db106da07..7c9c003ea1 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -907,7 +907,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons
for (int i = 0; i < headers.size(); i++) {
if (headers[i].findn("ETag:") == 0) { // Save etag
String cache_filename_base = EditorPaths::get_singleton()->get_cache_dir().path_join("assetimage_" + image_queue[p_queue_id].image_url.md5_text());
- String new_etag = headers[i].substr(headers[i].find(":") + 1, headers[i].length()).strip_edges();
+ String new_etag = headers[i].substr(headers[i].find_char(':') + 1, headers[i].length()).strip_edges();
Ref<FileAccess> file = FileAccess::open(cache_filename_base + ".etag", FileAccess::WRITE);
if (file.is_valid()) {
file->store_line(new_etag);
diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp
index 3a4b3a0ea2..de4ab828bc 100644
--- a/editor/plugins/path_3d_editor_plugin.cpp
+++ b/editor/plugins/path_3d_editor_plugin.cpp
@@ -277,8 +277,15 @@ void Path3DGizmo::redraw() {
Ref<StandardMaterial3D> path_tilt_material = gizmo_plugin->get_material("path_tilt_material", this);
Ref<StandardMaterial3D> path_tilt_muted_material = gizmo_plugin->get_material("path_tilt_muted_material", this);
Ref<StandardMaterial3D> handles_material = gizmo_plugin->get_material("handles");
+ Ref<StandardMaterial3D> first_pt_handle_material = gizmo_plugin->get_material("first_pt_handle");
+ Ref<StandardMaterial3D> last_pt_handle_material = gizmo_plugin->get_material("last_pt_handle");
+ Ref<StandardMaterial3D> closed_pt_handle_material = gizmo_plugin->get_material("closed_pt_handle");
Ref<StandardMaterial3D> sec_handles_material = gizmo_plugin->get_material("sec_handles");
+ first_pt_handle_material->set_albedo(Color(0.2, 1.0, 0.0));
+ last_pt_handle_material->set_albedo(Color(1.0, 0.2, 0.0));
+ closed_pt_handle_material->set_albedo(Color(1.0, 0.8, 0.0));
+
Ref<Curve3D> c = path->get_curve();
if (c.is_null()) {
return;
@@ -369,7 +376,7 @@ void Path3DGizmo::redraw() {
info.point_idx = idx;
// Collect in-handles except for the first point.
- if (idx > 0 && Path3DEditorPlugin::singleton->curve_edit_curve->is_pressed()) {
+ if (idx > (c->is_closed() ? -1 : 0) && Path3DEditorPlugin::singleton->curve_edit_curve->is_pressed()) {
const Vector3 in = c->get_point_in(idx);
info.type = HandleType::HANDLE_TYPE_IN;
@@ -383,7 +390,7 @@ void Path3DGizmo::redraw() {
}
// Collect out-handles except for the last point.
- if (idx < c->get_point_count() - 1 && Path3DEditorPlugin::singleton->curve_edit_curve->is_pressed()) {
+ if (idx < (c->is_closed() ? c->get_point_count() : c->get_point_count() - 1) && Path3DEditorPlugin::singleton->curve_edit_curve->is_pressed()) {
const Vector3 out = c->get_point_out(idx);
info.type = HandleType::HANDLE_TYPE_OUT;
@@ -441,7 +448,42 @@ void Path3DGizmo::redraw() {
}
if (!Path3DEditorPlugin::singleton->curve_edit->is_pressed() && primary_handle_points.size()) {
- add_handles(primary_handle_points, handles_material);
+ // Need to define indices separately.
+ // Point count.
+ const int pc = primary_handle_points.size();
+ Vector<int> idx;
+ idx.resize(pc);
+ int *idx_ptr = idx.ptrw();
+ for (int j = 0; j < pc; j++) {
+ idx_ptr[j] = j;
+ }
+
+ // Initialize arrays for first point.
+ PackedVector3Array first_pt_handle_point;
+ Vector<int> first_pt_id;
+ first_pt_handle_point.append(primary_handle_points[0]);
+ first_pt_id.append(idx[0]);
+
+ // Initialize arrays and add handle for last point if needed.
+ if (pc > 1) {
+ PackedVector3Array last_pt_handle_point;
+ Vector<int> last_pt_id;
+ last_pt_handle_point.append(primary_handle_points[pc - 1]);
+ last_pt_id.append(idx[pc - 1]);
+ primary_handle_points.remove_at(pc - 1);
+ idx.remove_at(pc - 1);
+ add_handles(last_pt_handle_point, c->is_closed() ? handles_material : last_pt_handle_material, last_pt_id);
+ }
+
+ // Add handle for first point.
+ primary_handle_points.remove_at(0);
+ idx.remove_at(0);
+ add_handles(first_pt_handle_point, c->is_closed() ? closed_pt_handle_material : first_pt_handle_material, first_pt_id);
+
+ // Add handles for remaining intermediate points.
+ if (!primary_handle_points.is_empty()) {
+ add_handles(primary_handle_points, handles_material, idx);
+ }
}
if (secondary_handle_points.size()) {
add_handles(secondary_handle_points, sec_handles_material, collected_secondary_handle_ids, false, true);
@@ -469,7 +511,7 @@ Path3DGizmo::Path3DGizmo(Path3D *p_path, float p_disk_size) {
Path3DEditorPlugin::singleton->curve_edit_curve->connect(SceneStringName(pressed), callable_mp(this, &Path3DGizmo::redraw));
Path3DEditorPlugin::singleton->curve_create->connect(SceneStringName(pressed), callable_mp(this, &Path3DGizmo::redraw));
Path3DEditorPlugin::singleton->curve_del->connect(SceneStringName(pressed), callable_mp(this, &Path3DGizmo::redraw));
- Path3DEditorPlugin::singleton->curve_close->connect(SceneStringName(pressed), callable_mp(this, &Path3DGizmo::redraw));
+ Path3DEditorPlugin::singleton->curve_closed->connect(SceneStringName(pressed), callable_mp(this, &Path3DGizmo::redraw));
}
EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
@@ -696,7 +738,7 @@ void Path3DEditorPlugin::_mode_changed(int p_mode) {
Node3DEditor::get_singleton()->clear_subgizmo_selection();
}
-void Path3DEditorPlugin::_close_curve() {
+void Path3DEditorPlugin::_toggle_closed_curve() {
Ref<Curve3D> c = path->get_curve();
if (c.is_null()) {
return;
@@ -704,13 +746,10 @@ void Path3DEditorPlugin::_close_curve() {
if (c->get_point_count() < 2) {
return;
}
- if (c->get_point_position(0) == c->get_point_position(c->get_point_count() - 1)) {
- return;
- }
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
- ur->create_action(TTR("Close Curve"));
- ur->add_do_method(c.ptr(), "add_point", c->get_point_position(0), c->get_point_in(0), c->get_point_out(0), -1);
- ur->add_undo_method(c.ptr(), "remove_point", c->get_point_count());
+ ur->create_action(TTR("Toggle Open/Closed Curve"));
+ ur->add_do_method(c.ptr(), "set_closed", !c.ptr()->is_closed());
+ ur->add_undo_method(c.ptr(), "set_closed", c.ptr()->is_closed());
ur->commit_action();
}
@@ -771,6 +810,7 @@ void Path3DEditorPlugin::_clear_curve_points() {
return;
}
Ref<Curve3D> curve = path->get_curve();
+ curve->set_closed(false);
curve->clear_points();
}
@@ -795,7 +835,7 @@ void Path3DEditorPlugin::_update_theme() {
curve_edit_tilt->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveTilt")));
curve_create->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveCreate")));
curve_del->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveDelete")));
- curve_close->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveClose")));
+ curve_closed->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveClose")));
curve_clear_points->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("Clear")));
create_curve_button->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("Curve3D")));
}
@@ -872,12 +912,12 @@ Path3DEditorPlugin::Path3DEditorPlugin() {
toolbar->add_child(curve_del);
curve_del->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_DELETE));
- curve_close = memnew(Button);
- curve_close->set_theme_type_variation("FlatButton");
- curve_close->set_focus_mode(Control::FOCUS_NONE);
- curve_close->set_tooltip_text(TTR("Close Curve"));
- toolbar->add_child(curve_close);
- curve_close->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_close_curve));
+ curve_closed = memnew(Button);
+ curve_closed->set_theme_type_variation("FlatButton");
+ curve_closed->set_focus_mode(Control::FOCUS_NONE);
+ curve_closed->set_tooltip_text(TTR("Close Curve"));
+ toolbar->add_child(curve_closed);
+ curve_closed->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_toggle_closed_curve));
curve_clear_points = memnew(Button);
curve_clear_points->set_theme_type_variation("FlatButton");
@@ -943,6 +983,14 @@ void Path3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Ref<Curve3D> curve = path->get_curve();
Ref<StandardMaterial3D> handle_material = get_material("handles", p_gizmo);
+ Ref<StandardMaterial3D> first_pt_handle_material = get_material("first_pt_handle", p_gizmo);
+ Ref<StandardMaterial3D> last_pt_handle_material = get_material("last_pt_handle", p_gizmo);
+ Ref<StandardMaterial3D> closed_pt_handle_material = get_material("closed_pt_handle", p_gizmo);
+
+ first_pt_handle_material->set_albedo(Color(0.2, 1.0, 0.0));
+ last_pt_handle_material->set_albedo(Color(1.0, 0.2, 0.0));
+ closed_pt_handle_material->set_albedo(Color(1.0, 0.8, 0.0));
+
PackedVector3Array handles;
if (Path3DEditorPlugin::singleton->curve_edit->is_pressed()) {
@@ -955,7 +1003,37 @@ void Path3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
}
if (handles.size()) {
- p_gizmo->add_vertices(handles, handle_material, Mesh::PRIMITIVE_POINTS);
+ // Point count.
+ const int pc = handles.size();
+
+ // Initialize arrays for first point.
+ PackedVector3Array first_pt;
+ first_pt.append(handles[0]);
+
+ // Initialize arrays and add handle for last point if needed.
+ if (pc > 1) {
+ PackedVector3Array last_pt;
+ last_pt.append(handles[handles.size() - 1]);
+ handles.remove_at(handles.size() - 1);
+ if (curve->is_closed()) {
+ p_gizmo->add_vertices(last_pt, handle_material, Mesh::PRIMITIVE_POINTS);
+ } else {
+ p_gizmo->add_vertices(last_pt, last_pt_handle_material, Mesh::PRIMITIVE_POINTS);
+ }
+ }
+
+ // Add handle for first point.
+ handles.remove_at(0);
+ if (curve->is_closed()) {
+ p_gizmo->add_vertices(first_pt, closed_pt_handle_material, Mesh::PRIMITIVE_POINTS);
+ } else {
+ p_gizmo->add_vertices(first_pt, first_pt_handle_material, Mesh::PRIMITIVE_POINTS);
+ }
+
+ // Add handles for remaining intermediate points.
+ if (!handles.is_empty()) {
+ p_gizmo->add_vertices(handles, handle_material, Mesh::PRIMITIVE_POINTS);
+ }
}
}
@@ -1072,5 +1150,8 @@ Path3DGizmoPlugin::Path3DGizmoPlugin(float p_disk_size) {
create_material("path_tilt_material", path_tilt_color);
create_material("path_tilt_muted_material", path_tilt_color * 0.7);
create_handle_material("handles", false, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorPathSmoothHandle"), EditorStringName(EditorIcons)));
+ create_handle_material("first_pt_handle", false, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorPathSmoothHandle"), EditorStringName(EditorIcons)));
+ create_handle_material("last_pt_handle", false, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorPathSmoothHandle"), EditorStringName(EditorIcons)));
+ create_handle_material("closed_pt_handle", false, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorPathSmoothHandle"), EditorStringName(EditorIcons)));
create_handle_material("sec_handles", false, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorCurveHandle"), EditorStringName(EditorIcons)));
}
diff --git a/editor/plugins/path_3d_editor_plugin.h b/editor/plugins/path_3d_editor_plugin.h
index 60cb7f940f..3e45c2718f 100644
--- a/editor/plugins/path_3d_editor_plugin.h
+++ b/editor/plugins/path_3d_editor_plugin.h
@@ -120,7 +120,7 @@ class Path3DEditorPlugin : public EditorPlugin {
Button *curve_edit_curve = nullptr;
Button *curve_edit_tilt = nullptr;
Button *curve_del = nullptr;
- Button *curve_close = nullptr;
+ Button *curve_closed = nullptr;
Button *curve_clear_points = nullptr;
MenuButton *handle_menu = nullptr;
@@ -144,7 +144,7 @@ class Path3DEditorPlugin : public EditorPlugin {
void _update_toolbar();
void _mode_changed(int p_mode);
- void _close_curve();
+ void _toggle_closed_curve();
void _handle_option_pressed(int p_option);
bool handle_clicked = false;
bool mirror_handle_angle = true;
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index fc8b8aa49f..cf586c792e 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -282,7 +282,7 @@ void ScriptTextEditor::_warning_clicked(const Variant &p_line) {
CodeEdit *text_editor = code_editor->get_text_editor();
String prev_line = line > 0 ? text_editor->get_line(line - 1) : "";
if (prev_line.contains("@warning_ignore")) {
- const int closing_bracket_idx = prev_line.find(")");
+ const int closing_bracket_idx = prev_line.find_char(')');
const String text_to_insert = ", " + code.quote(quote_style);
text_editor->insert_text(text_to_insert, line - 1, closing_bracket_idx);
} else {
@@ -1205,7 +1205,7 @@ void ScriptTextEditor::_update_connected_methods() {
// Account for inner classes by stripping the class names from the method,
// starting from the right since our inner class might be inside of another inner class.
- int pos = raw_name.rfind(".");
+ int pos = raw_name.rfind_char('.');
if (pos != -1) {
name = raw_name.substr(pos + 1);
}
diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp
index d08610c93f..edf3ff7296 100644
--- a/editor/project_converter_3_to_4.cpp
+++ b/editor/project_converter_3_to_4.cpp
@@ -1970,7 +1970,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
// -- func c(var a, var b) -> func c(a, b)
if (line.contains("func ") && line.contains("var ")) {
int start = line.find("func ");
- start = line.substr(start).find("(") + start;
+ start = line.substr(start).find_char('(') + start;
int end = get_end_parenthesis(line.substr(start)) + 1;
if (end > -1) {
Vector<String> parts = parse_arguments(line.substr(start, end));
@@ -2120,12 +2120,12 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
}
}
// -- func _init(p_x:int).(p_x): -> func _init(p_x:int):\n\tsuper(p_x) Object # https://github.com/godotengine/godot/issues/70542
- if (line.contains(" _init(") && line.rfind(":") > 0) {
+ if (line.contains(" _init(") && line.rfind_char(':') > 0) {
// func _init(p_arg1).(super4, super5, super6)->void:
// ^--^indent ^super_start super_end^
int indent = line.count("\t", 0, line.find("func"));
int super_start = line.find(".(");
- int super_end = line.rfind(")");
+ int super_end = line.rfind_char(')');
if (super_start > 0 && super_end > super_start) {
line = line.substr(0, super_start) + line.substr(super_end + 1) + "\n" + String("\t").repeat(indent + 1) + "super" + line.substr(super_start + 1, super_end - super_start);
}
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index d38ff7af76..8615836ddd 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -144,7 +144,7 @@ void ScriptCreateDialog::_notification(int p_what) {
void ScriptCreateDialog::_path_hbox_sorted() {
if (is_visible()) {
- int filename_start_pos = file_path->get_text().rfind("/") + 1;
+ int filename_start_pos = file_path->get_text().rfind_char('/') + 1;
int filename_end_pos = file_path->get_text().get_basename().length();
if (!is_built_in) {
diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp
index e1c797633a..33da3dd10c 100644
--- a/editor/shader_create_dialog.cpp
+++ b/editor/shader_create_dialog.cpp
@@ -103,7 +103,7 @@ void ShaderCreateDialog::_update_language_info() {
void ShaderCreateDialog::_path_hbox_sorted() {
if (is_visible()) {
- int filename_start_pos = initial_base_path.rfind("/") + 1;
+ int filename_start_pos = initial_base_path.rfind_char('/') + 1;
int filename_end_pos = initial_base_path.length();
if (!is_built_in) {
diff --git a/main/main.cpp b/main/main.cpp
index 2ac019ca53..f002e22766 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -1598,7 +1598,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
} else if (arg.ends_with("project.godot")) {
String path;
String file = arg;
- int sep = MAX(file.rfind("/"), file.rfind("\\"));
+ int sep = MAX(file.rfind_char('/'), file.rfind_char('\\'));
if (sep == -1) {
path = ".";
} else {
@@ -4145,7 +4145,7 @@ int Main::start() {
local_game_path = "res://" + local_game_path;
} else {
- int sep = local_game_path.rfind("/");
+ int sep = local_game_path.rfind_char('/');
if (sep == -1) {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
diff --git a/modules/basis_universal/image_compress_basisu.cpp b/modules/basis_universal/image_compress_basisu.cpp
index 8ca5dba225..be28d89508 100644
--- a/modules/basis_universal/image_compress_basisu.cpp
+++ b/modules/basis_universal/image_compress_basisu.cpp
@@ -38,13 +38,12 @@
#include <transcoder/basisu_transcoder.h>
#ifdef TOOLS_ENABLED
#include <encoder/basisu_comp.h>
-#endif
-void basis_universal_init() {
-#ifdef TOOLS_ENABLED
- basisu::basisu_encoder_init();
+static Mutex init_mutex;
+static bool initialized = false;
#endif
+void basis_universal_init() {
basist::basisu_transcoder_init();
}
@@ -80,6 +79,13 @@ inline void _basisu_pad_mipmap(const uint8_t *p_image_mip_data, Vector<uint8_t>
}
Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedChannels p_channels) {
+ init_mutex.lock();
+ if (!initialized) {
+ basisu::basisu_encoder_init();
+ initialized = true;
+ }
+ init_mutex.unlock();
+
uint64_t start_time = OS::get_singleton()->get_ticks_msec();
Ref<Image> image = p_image->duplicate();
diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp
index 758887a723..3a5a88d356 100644
--- a/modules/gdscript/editor/gdscript_docgen.cpp
+++ b/modules/gdscript/editor/gdscript_docgen.cpp
@@ -140,7 +140,7 @@ void GDScriptDocGen::_doctype_from_gdtype(const GDType &p_gdtype, String &r_type
r_enum = String(p_gdtype.native_type).replace("::", ".");
if (r_enum.begins_with("res://")) {
r_enum = r_enum.trim_prefix("res://");
- int dot_pos = r_enum.rfind(".");
+ int dot_pos = r_enum.rfind_char('.');
if (dot_pos >= 0) {
r_enum = r_enum.left(dot_pos).quote() + r_enum.substr(dot_pos);
}
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 0b12f2ff76..629581bd6c 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -163,7 +163,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
if (from + end_key_length > line_length) {
// If it's key length and there is a '\', dont skip to highlight esc chars.
- if (str.find("\\", from) >= 0) {
+ if (str.find_char('\\', from) >= 0) {
break;
}
}
@@ -236,7 +236,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
for (; from < line_length; from++) {
if (line_length - from < end_key_length) {
// Don't break if '\' to highlight esc chars.
- if (str.find("\\", from) < 0) {
+ if (str.find_char('\\', from) < 0) {
break;
}
}
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index a29bd1fdb5..d58cd2c3f7 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -3472,7 +3472,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
String arg = arg_itr->name;
if (arg.contains(":")) {
- arg = arg.substr(0, arg.find(":"));
+ arg = arg.substr(0, arg.find_char(':'));
}
method_hint += arg;
if (use_type_hint) {
diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp
index 2db46adef4..5ba9ee3fa6 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.cpp
+++ b/modules/gltf/editor/editor_scene_importer_blend.cpp
@@ -80,7 +80,7 @@ static bool _get_blender_version(const String &p_path, int &r_major, int &r_mino
}
pipe = pipe.substr(bl);
pipe = pipe.replace_first("Blender ", "");
- int pp = pipe.find(".");
+ int pp = pipe.find_char('.');
if (pp == -1) {
if (r_err) {
*r_err = vformat(TTR("Couldn't extract version information from Blender executable at: %s."), p_path);
@@ -96,7 +96,7 @@ static bool _get_blender_version(const String &p_path, int &r_major, int &r_mino
return false;
}
- int pp2 = pipe.find(".", pp + 1);
+ int pp2 = pipe.find_char('.', pp + 1);
r_minor = pp2 > pp ? pipe.substr(pp + 1, pp2 - pp - 1).to_int() : 0;
return true;
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 7cac61304f..2f36c29ec4 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -689,7 +689,7 @@ void GLTFDocument::_compute_node_heights(Ref<GLTFState> p_state) {
}
static Vector<uint8_t> _parse_base64_uri(const String &p_uri) {
- int start = p_uri.find(",");
+ int start = p_uri.find_char(',');
ERR_FAIL_COND_V(start == -1, Vector<uint8_t>());
CharString substr = p_uri.substr(start + 1).ascii();
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 380b401683..ec9c123f8d 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -2817,7 +2817,7 @@ Ref<Resource> ResourceFormatLoaderCSharpScript::load(const String &p_path, const
if (p_path.begins_with("csharp://")) {
// This is a virtual path used by generic types, extract the real path.
real_path = "res://" + p_path.trim_prefix("csharp://");
- real_path = real_path.substr(0, real_path.rfind(":"));
+ real_path = real_path.substr(0, real_path.rfind_char(':'));
}
Ref<CSharpScript> scr;
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
index fc67e4f592..f192bf0fb7 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
@@ -423,25 +423,10 @@ namespace Godot.SourceGenerators
if (exportAttr != null && propertySymbol != null)
{
- if (propertySymbol.GetMethod == null)
+ if (propertySymbol.GetMethod == null || propertySymbol.SetMethod == null || propertySymbol.SetMethod.IsInitOnly)
{
- // This should never happen, as we filtered WriteOnly properties, but just in case.
- context.ReportDiagnostic(Diagnostic.Create(
- Common.ExportedPropertyIsWriteOnlyRule,
- propertySymbol.Locations.FirstLocationWithSourceTreeOrDefault(),
- propertySymbol.ToDisplayString()
- ));
- return null;
- }
-
- if (propertySymbol.SetMethod == null || propertySymbol.SetMethod.IsInitOnly)
- {
- // This should never happen, as we filtered ReadOnly properties, but just in case.
- context.ReportDiagnostic(Diagnostic.Create(
- Common.ExportedMemberIsReadOnlyRule,
- propertySymbol.Locations.FirstLocationWithSourceTreeOrDefault(),
- propertySymbol.ToDisplayString()
- ));
+ // Exports can be neither read-only nor write-only but the diagnostic errors for properties are already
+ // reported by ScriptPropertyDefValGenerator.cs so just quit early here.
return null;
}
}
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index db90ac5a6e..c54d58d6a0 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -190,7 +190,7 @@ String BindingsGenerator::bbcode_to_text(const String &p_bbcode, const TypeInter
int pos = 0;
while (pos < bbcode.length()) {
- int brk_pos = bbcode.find("[", pos);
+ int brk_pos = bbcode.find_char('[', pos);
if (brk_pos < 0) {
brk_pos = bbcode.length();
@@ -210,7 +210,7 @@ String BindingsGenerator::bbcode_to_text(const String &p_bbcode, const TypeInter
break;
}
- int brk_end = bbcode.find("]", brk_pos + 1);
+ int brk_end = bbcode.find_char(']', brk_pos + 1);
if (brk_end == -1) {
String text = bbcode.substr(brk_pos, bbcode.length() - brk_pos);
@@ -239,7 +239,7 @@ String BindingsGenerator::bbcode_to_text(const String &p_bbcode, const TypeInter
output.append("[");
pos = brk_pos + 1;
} else if (tag.begins_with("method ") || tag.begins_with("constructor ") || tag.begins_with("operator ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("theme_item ") || tag.begins_with("param ")) {
- const int tag_end = tag.find(" ");
+ const int tag_end = tag.find_char(' ');
const String link_tag = tag.substr(0, tag_end);
const String link_target = tag.substr(tag_end + 1, tag.length()).lstrip(" ");
@@ -385,7 +385,7 @@ String BindingsGenerator::bbcode_to_text(const String &p_bbcode, const TypeInter
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "url") {
- int end = bbcode.find("[", brk_end);
+ int end = bbcode.find_char('[', brk_end);
if (end == -1) {
end = bbcode.length();
}
@@ -403,7 +403,7 @@ String BindingsGenerator::bbcode_to_text(const String &p_bbcode, const TypeInter
pos = brk_end + 1;
tag_stack.push_front("url");
} else if (tag == "img") {
- int end = bbcode.find("[", brk_end);
+ int end = bbcode.find_char('[', brk_end);
if (end == -1) {
end = bbcode.length();
}
@@ -455,7 +455,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
int pos = 0;
while (pos < bbcode.length()) {
- int brk_pos = bbcode.find("[", pos);
+ int brk_pos = bbcode.find_char('[', pos);
if (brk_pos < 0) {
brk_pos = bbcode.length();
@@ -488,7 +488,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
break;
}
- int brk_end = bbcode.find("]", brk_pos + 1);
+ int brk_end = bbcode.find_char(']', brk_pos + 1);
if (brk_end == -1) {
if (!line_del) {
@@ -551,7 +551,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
xml_output.append("[");
pos = brk_pos + 1;
} else if (tag.begins_with("method ") || tag.begins_with("constructor ") || tag.begins_with("operator ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("theme_item ") || tag.begins_with("param ")) {
- const int tag_end = tag.find(" ");
+ const int tag_end = tag.find_char(' ');
const String link_tag = tag.substr(0, tag_end);
const String link_target = tag.substr(tag_end + 1, tag.length()).lstrip(" ");
@@ -696,7 +696,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "code" || tag.begins_with("code ")) {
- int end = bbcode.find("[", brk_end);
+ int end = bbcode.find_char('[', brk_end);
if (end == -1) {
end = bbcode.length();
}
@@ -751,7 +751,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "url") {
- int end = bbcode.find("[", brk_end);
+ int end = bbcode.find_char('[', brk_end);
if (end == -1) {
end = bbcode.length();
}
@@ -772,7 +772,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
pos = brk_end + 1;
tag_stack.push_front("url");
} else if (tag == "img") {
- int end = bbcode.find("[", brk_end);
+ int end = bbcode.find_char('[', brk_end);
if (end == -1) {
end = bbcode.length();
}
@@ -1619,7 +1619,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
bool enum_in_static_class = false;
- if (enum_proxy_name.find(".") > 0) {
+ if (enum_proxy_name.find_char('.') > 0) {
enum_in_static_class = true;
String enum_class_name = enum_proxy_name.get_slicec('.', 0);
enum_proxy_name = enum_proxy_name.get_slicec('.', 1);
diff --git a/modules/mono/editor/code_completion.cpp b/modules/mono/editor/code_completion.cpp
index ae914e71ef..94222f3d67 100644
--- a/modules/mono/editor/code_completion.cpp
+++ b/modules/mono/editor/code_completion.cpp
@@ -116,7 +116,7 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
continue;
}
- String name = prop.name.substr(prop.name.find("/") + 1, prop.name.length());
+ String name = prop.name.substr(prop.name.find_char('/') + 1, prop.name.length());
suggestions.push_back(quoted(name));
}
} break;
diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp
index ee17a668d7..068ac8b4e1 100644
--- a/modules/mono/utils/path_utils.cpp
+++ b/modules/mono/utils/path_utils.cpp
@@ -212,7 +212,7 @@ String relative_to_impl(const String &p_path, const String &p_relative_to) {
#ifdef WINDOWS_ENABLED
String get_drive_letter(const String &p_norm_path) {
int idx = p_norm_path.find(":/");
- if (idx != -1 && idx < p_norm_path.find("/")) {
+ if (idx != -1 && idx < p_norm_path.find_char('/')) {
return p_norm_path.substr(0, idx + 1);
}
return String();
diff --git a/modules/multiplayer/editor/replication_editor.cpp b/modules/multiplayer/editor/replication_editor.cpp
index 8de82ef409..4d5480eebf 100644
--- a/modules/multiplayer/editor/replication_editor.cpp
+++ b/modules/multiplayer/editor/replication_editor.cpp
@@ -375,7 +375,7 @@ void ReplicationEditor::_add_pressed() {
return;
}
- int idx = np_text.find(":");
+ int idx = np_text.find_char(':');
if (idx == -1) {
np_text = ".:" + np_text;
} else if (idx == 0) {
@@ -554,7 +554,7 @@ void ReplicationEditor::_add_property(const NodePath &p_property, bool p_spawn,
Node *root_node = current && !current->get_root_path().is_empty() ? current->get_node(current->get_root_path()) : nullptr;
Ref<Texture2D> icon = _get_class_icon(root_node);
if (root_node) {
- String path = prop.substr(0, prop.find(":"));
+ String path = prop.substr(0, prop.find_char(':'));
String subpath = prop.substr(path.size());
Node *node = root_node->get_node_or_null(path);
if (!node) {
diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp
index b9d493b844..1f7d5504b6 100644
--- a/modules/svg/image_loader_svg.cpp
+++ b/modules/svg/image_loader_svg.cpp
@@ -53,7 +53,7 @@ void ImageLoaderSVG::_replace_color_property(const HashMap<Color, Color> &p_colo
int pos = r_string.find(p_prefix);
while (pos != -1) {
pos += prefix_len; // Skip prefix.
- int end_pos = r_string.find("\"", pos);
+ int end_pos = r_string.find_char('"', pos);
ERR_FAIL_COND_MSG(end_pos == -1, vformat("Malformed SVG string after property \"%s\".", p_prefix));
const String color_code = r_string.substr(pos, end_pos - pos);
if (color_code != "none" && !color_code.begins_with("url(")) {
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index d2b64c74a4..dc7a287a91 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -37,6 +37,8 @@
#include "editor/editor_settings.h"
#include "editor/export/editor_export.h"
+String get_default_android_sdk_path();
+
void register_android_exporter_types() {
GDREGISTER_VIRTUAL_CLASS(EditorExportPlatformAndroid);
}
@@ -54,8 +56,10 @@ void register_android_exporter() {
#else
EDITOR_DEF_BASIC("export/android/java_sdk_path", OS::get_singleton()->get_environment("JAVA_HOME"));
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/java_sdk_path", PROPERTY_HINT_GLOBAL_DIR));
- EDITOR_DEF_BASIC("export/android/android_sdk_path", OS::get_singleton()->get_environment("ANDROID_HOME"));
+
+ EDITOR_DEF_BASIC("export/android/android_sdk_path", OS::get_singleton()->has_environment("ANDROID_HOME") ? OS::get_singleton()->get_environment("ANDROID_HOME") : get_default_android_sdk_path());
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/android_sdk_path", PROPERTY_HINT_GLOBAL_DIR));
+
EDITOR_DEF("export/android/force_system_user", false);
EDITOR_DEF("export/android/shutdown_adb_on_exit", true);
@@ -69,3 +73,15 @@ void register_android_exporter() {
Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>(memnew(EditorExportPlatformAndroid));
EditorExport::get_singleton()->add_export_platform(exporter);
}
+
+inline String get_default_android_sdk_path() {
+#ifdef WINDOWS_ENABLED
+ return OS::get_singleton()->get_environment("LOCALAPPDATA").path_join("Android/Sdk");
+#elif LINUXBSD_ENABLED
+ return OS::get_singleton()->get_environment("HOME").path_join("Android/Sdk");
+#elif MACOS_ENABLED
+ return OS::get_singleton()->get_environment("HOME").path_join("Library/Android/sdk");
+#else
+ return String();
+#endif
+}
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index df3142ecbb..0b506e60d6 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -1564,7 +1564,7 @@ void EditorExportPlatformAndroid::_fix_resources(const Ref<EditorExportPreset> &
str = get_project_name(package_name);
} else {
- String lang = str.substr(str.rfind("-") + 1, str.length()).replace("-", "_");
+ String lang = str.substr(str.rfind_char('-') + 1, str.length()).replace("-", "_");
if (appnames.has(lang)) {
str = appnames[lang];
} else {
diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp
index e066e78e5b..d8d58ba54b 100644
--- a/platform/linuxbsd/wayland/wayland_thread.cpp
+++ b/platform/linuxbsd/wayland/wayland_thread.cpp
@@ -3988,10 +3988,10 @@ void WaylandThread::selection_set_text(const String &p_text) {
wl_data_source_add_listener(ss->wl_data_source_selection, &wl_data_source_listener, ss);
wl_data_source_offer(ss->wl_data_source_selection, "text/plain;charset=utf-8");
wl_data_source_offer(ss->wl_data_source_selection, "text/plain");
- }
- // TODO: Implement a good way of getting the latest serial from the user.
- wl_data_device_set_selection(ss->wl_data_device, ss->wl_data_source_selection, MAX(ss->pointer_data.button_serial, ss->last_key_pressed_serial));
+ // TODO: Implement a good way of getting the latest serial from the user.
+ wl_data_device_set_selection(ss->wl_data_device, ss->wl_data_source_selection, MAX(ss->pointer_data.button_serial, ss->last_key_pressed_serial));
+ }
// Wait for the message to get to the server before continuing, otherwise the
// clipboard update might come with a delay.
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index bf5645d9a6..b9f9d8d613 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -970,7 +970,7 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres
return Error::FAILED;
} else {
print_verbose("rcodesign (" + p_path + "):\n" + str);
- int next_nl = str.find("\n", rq_offset);
+ int next_nl = str.find_char('\n', rq_offset);
String request_uuid = (next_nl == -1) ? str.substr(rq_offset + 23, -1) : str.substr(rq_offset + 23, next_nl - rq_offset - 23);
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), vformat(TTR("Notarization request UUID: \"%s\""), request_uuid));
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), TTR("The notarization process generally takes less than an hour."));
@@ -1054,7 +1054,7 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres
return Error::FAILED;
} else {
print_verbose("notarytool (" + p_path + "):\n" + str);
- int next_nl = str.find("\n", rq_offset);
+ int next_nl = str.find_char('\n', rq_offset);
String request_uuid = (next_nl == -1) ? str.substr(rq_offset + 4, -1) : str.substr(rq_offset + 4, next_nl - rq_offset - 4);
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), vformat(TTR("Notarization request UUID: \"%s\""), request_uuid));
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), TTR("The notarization process generally takes less than an hour."));
diff --git a/platform/web/api/web_tools_editor_plugin.cpp b/platform/web/api/web_tools_editor_plugin.cpp
index d39773bde2..61801372ba 100644
--- a/platform/web/api/web_tools_editor_plugin.cpp
+++ b/platform/web/api/web_tools_editor_plugin.cpp
@@ -80,7 +80,7 @@ void WebToolsEditorPlugin::_download_zip() {
const String output_path = String("/tmp").path_join(output_name);
zipFile zip = zipOpen2(output_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io);
- const String base_path = resource_path.substr(0, resource_path.rfind("/")) + "/";
+ const String base_path = resource_path.substr(0, resource_path.rfind_char('/')) + "/";
_zip_recursive(resource_path, base_path, zip);
zipClose(zip, nullptr);
{
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 0462d3f8fa..32ed8823d3 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -40,7 +40,6 @@
#include "core/input/input.h"
#include "core/io/image.h"
#include "core/os/os.h"
-#include "drivers/unix/ip_unix.h"
#include "drivers/wasapi/audio_driver_wasapi.h"
#include "drivers/winmidi/midi_driver_winmidi.h"
#include "servers/audio_server.h"
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index fcc4655cbe..416016b112 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -43,6 +43,7 @@
#include "drivers/windows/dir_access_windows.h"
#include "drivers/windows/file_access_windows.h"
#include "drivers/windows/file_access_windows_pipe.h"
+#include "drivers/windows/ip_windows.h"
#include "drivers/windows/net_socket_winsock.h"
#include "main/main.h"
#include "servers/audio_server.h"
@@ -69,6 +70,7 @@
extern "C" {
__declspec(dllexport) DWORD NvOptimusEnablement = 1;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
+__declspec(dllexport) void NoHotPatch() {} // Disable Nahimic code injection.
}
// Workaround mingw-w64 < 4.0 bug
@@ -274,7 +276,7 @@ void OS_Windows::initialize() {
current_pi.pi.hProcess = GetCurrentProcess();
process_map->insert(GetCurrentProcessId(), current_pi);
- IPUnix::make_default();
+ IPWindows::make_default();
main_loop = nullptr;
HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown **>(&dwrite_factory));
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index a32e535f0b..fd98f9b14b 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -37,7 +37,6 @@
#include "core/config/project_settings.h"
#include "core/input/input.h"
#include "core/os/os.h"
-#include "drivers/unix/ip_unix.h"
#include "drivers/wasapi/audio_driver_wasapi.h"
#include "drivers/winmidi/midi_driver_winmidi.h"
#include "servers/audio_server.h"
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 158155a51c..e601f16843 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -50,7 +50,7 @@ void FileDialog::popup_file_dialog() {
}
void FileDialog::_focus_file_text() {
- int lp = file->get_text().rfind(".");
+ int lp = file->get_text().rfind_char('.');
if (lp != -1) {
file->select(0, lp);
if (file->is_inside_tree() && !is_part_of_edited_scene()) {
@@ -1001,7 +1001,7 @@ void FileDialog::set_current_path(const String &p_path) {
if (!p_path.size()) {
return;
}
- int pos = MAX(p_path.rfind("/"), p_path.rfind("\\"));
+ int pos = MAX(p_path.rfind_char('/'), p_path.rfind_char('\\'));
if (pos == -1) {
set_current_file(p_path);
} else {
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 26141663c1..a349d50236 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -4845,7 +4845,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
String tooltip;
bool size_in_percent = false;
if (!bbcode_value.is_empty()) {
- int sep = bbcode_value.find("x");
+ int sep = bbcode_value.find_char('x');
if (sep == -1) {
width = bbcode_value.to_int();
} else {
diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp
index 4ad56d21d3..24c68e0188 100644
--- a/scene/gui/texture_progress_bar.cpp
+++ b/scene/gui/texture_progress_bar.cpp
@@ -608,11 +608,10 @@ int TextureProgressBar::get_fill_mode() {
}
void TextureProgressBar::set_radial_initial_angle(float p_angle) {
- while (p_angle > 360) {
- p_angle -= 360;
- }
- while (p_angle < 0) {
- p_angle += 360;
+ ERR_FAIL_COND_MSG(!Math::is_finite(p_angle), "Angle is non-finite.");
+
+ if (p_angle < 0.0 || p_angle > 360.0) {
+ p_angle = Math::fposmodp(p_angle, 360.0f);
}
if (rad_init_angle == p_angle) {
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 8526611093..986bd87af2 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -87,7 +87,7 @@ String HTTPRequest::get_header_value(const PackedStringArray &p_headers, const S
String lowwer_case_header_name = p_header_name.to_lower();
for (int i = 0; i < p_headers.size(); i++) {
- if (p_headers[i].find(":") > 0) {
+ if (p_headers[i].find_char(':') > 0) {
Vector<String> parts = p_headers[i].split(":", false, 1);
if (parts.size() > 1 && parts[0].strip_edges().to_lower() == lowwer_case_header_name) {
value = parts[1].strip_edges();
diff --git a/scene/property_utils.cpp b/scene/property_utils.cpp
index f068e34beb..9cae7d2a3a 100644
--- a/scene/property_utils.cpp
+++ b/scene/property_utils.cpp
@@ -182,7 +182,7 @@ Variant PropertyUtils::get_property_default_value(const Object *p_object, const
// Heuristically check if this is a synthetic property (whatever/0, whatever/1, etc.)
// because they are not in the class DB yet must have a default (null).
String prop_str = String(p_property);
- int p = prop_str.rfind("/");
+ int p = prop_str.rfind_char('/');
if (p != -1 && p < prop_str.length() - 1) {
bool all_digits = true;
for (int i = p + 1; i < prop_str.length(); i++) {
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 765373ce99..91d3757590 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -1463,6 +1463,9 @@ void Curve3D::_remove_point(int p_index) {
void Curve3D::remove_point(int p_index) {
_remove_point(p_index);
+ if (closed && points.size() < 2) {
+ set_closed(false);
+ }
notify_property_list_changed();
}
@@ -1479,15 +1482,25 @@ Vector3 Curve3D::sample(int p_index, real_t p_offset) const {
ERR_FAIL_COND_V(pc == 0, Vector3());
if (p_index >= pc - 1) {
- return points[pc - 1].position;
+ if (!closed) {
+ return points[pc - 1].position;
+ } else {
+ p_index = pc - 1;
+ }
} else if (p_index < 0) {
return points[0].position;
}
Vector3 p0 = points[p_index].position;
Vector3 p1 = p0 + points[p_index].out;
- Vector3 p3 = points[p_index + 1].position;
- Vector3 p2 = p3 + points[p_index + 1].in;
+ Vector3 p3, p2;
+ if (!closed || p_index < pc - 1) {
+ p3 = points[p_index + 1].position;
+ p2 = p3 + points[p_index + 1].in;
+ } else {
+ p3 = points[0].position;
+ p2 = p3 + points[0].in;
+ }
return p0.bezier_interpolate(p1, p2, p3, p_offset);
}
@@ -1605,13 +1618,16 @@ void Curve3D::_bake() const {
{
Vector<RBMap<real_t, Vector3>> midpoints = _tessellate_even_length(10, bake_interval);
+ const int num_intervals = closed ? points.size() : points.size() - 1;
+
#ifdef TOOLS_ENABLED
- points_in_cache.resize(points.size());
+ points_in_cache.resize(closed ? (points.size() + 1) : points.size());
points_in_cache.set(0, 0);
#endif
+ // Point Count: Begins at 1 to account for the last point.
int pc = 1;
- for (int i = 0; i < points.size() - 1; i++) {
+ for (int i = 0; i < num_intervals; i++) {
pc++;
pc += midpoints[i].size();
#ifdef TOOLS_ENABLED
@@ -1634,18 +1650,29 @@ void Curve3D::_bake() const {
btw[0] = points[0].tilt;
int pidx = 0;
- for (int i = 0; i < points.size() - 1; i++) {
+ for (int i = 0; i < num_intervals; i++) {
for (const KeyValue<real_t, Vector3> &E : midpoints[i]) {
pidx++;
bpw[pidx] = E.value;
- bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, E.key);
- btw[pidx] = Math::lerp(points[i].tilt, points[i + 1].tilt, E.key);
+ if (!closed || i < num_intervals - 1) {
+ bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, E.key);
+ btw[pidx] = Math::lerp(points[i].tilt, points[i + 1].tilt, E.key);
+ } else {
+ bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[0].position + points[0].in, points[0].position, E.key);
+ btw[pidx] = Math::lerp(points[i].tilt, points[0].tilt, E.key);
+ }
}
pidx++;
- bpw[pidx] = points[i + 1].position;
- bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, 1.0);
- btw[pidx] = points[i + 1].tilt;
+ if (!closed || i < num_intervals - 1) {
+ bpw[pidx] = points[i + 1].position;
+ bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, 1.0);
+ btw[pidx] = points[i + 1].tilt;
+ } else {
+ bpw[pidx] = points[0].position;
+ bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[0].position + points[0].in, points[0].position, 1.0);
+ btw[pidx] = points[0].tilt;
+ }
}
// Recalculate the baked distances.
@@ -2096,6 +2123,20 @@ real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const {
return nearest;
}
+void Curve3D::set_closed(bool p_closed) {
+ if (closed == p_closed) {
+ return;
+ }
+
+ closed = p_closed;
+ mark_dirty();
+ notify_property_list_changed();
+}
+
+bool Curve3D::is_closed() const {
+ return closed;
+}
+
void Curve3D::set_bake_interval(real_t p_tolerance) {
bake_interval = p_tolerance;
mark_dirty();
@@ -2174,11 +2215,17 @@ PackedVector3Array Curve3D::tessellate(int p_max_stages, real_t p_tolerance) con
}
Vector<RBMap<real_t, Vector3>> midpoints;
- midpoints.resize(points.size() - 1);
+ const int num_intervals = closed ? points.size() : points.size() - 1;
+ midpoints.resize(num_intervals);
+ // Point Count: Begins at 1 to account for the last point.
int pc = 1;
- for (int i = 0; i < points.size() - 1; i++) {
- _bake_segment3d(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[i + 1].position, points[i + 1].in, 0, p_max_stages, p_tolerance);
+ for (int i = 0; i < num_intervals; i++) {
+ if (!closed || i < num_intervals - 1) {
+ _bake_segment3d(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[i + 1].position, points[i + 1].in, 0, p_max_stages, p_tolerance);
+ } else {
+ _bake_segment3d(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[0].position, points[0].in, 0, p_max_stages, p_tolerance);
+ }
pc++;
pc += midpoints[i].size();
}
@@ -2188,14 +2235,18 @@ PackedVector3Array Curve3D::tessellate(int p_max_stages, real_t p_tolerance) con
bpw[0] = points[0].position;
int pidx = 0;
- for (int i = 0; i < points.size() - 1; i++) {
+ for (int i = 0; i < num_intervals; i++) {
for (const KeyValue<real_t, Vector3> &E : midpoints[i]) {
pidx++;
bpw[pidx] = E.value;
}
pidx++;
- bpw[pidx] = points[i + 1].position;
+ if (!closed || i < num_intervals - 1) {
+ bpw[pidx] = points[i + 1].position;
+ } else {
+ bpw[pidx] = points[0].position;
+ }
}
return tess;
@@ -2205,10 +2256,15 @@ Vector<RBMap<real_t, Vector3>> Curve3D::_tessellate_even_length(int p_max_stages
Vector<RBMap<real_t, Vector3>> midpoints;
ERR_FAIL_COND_V_MSG(points.size() < 2, midpoints, "Curve must have at least 2 control point");
- midpoints.resize(points.size() - 1);
+ const int num_intervals = closed ? points.size() : points.size() - 1;
+ midpoints.resize(num_intervals);
- for (int i = 0; i < points.size() - 1; i++) {
- _bake_segment3d_even_length(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[i + 1].position, points[i + 1].in, 0, p_max_stages, p_length);
+ for (int i = 0; i < num_intervals; i++) {
+ if (!closed || i < num_intervals - 1) {
+ _bake_segment3d_even_length(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[i + 1].position, points[i + 1].in, 0, p_max_stages, p_length);
+ } else {
+ _bake_segment3d_even_length(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[0].position, points[0].in, 0, p_max_stages, p_length);
+ }
}
return midpoints;
}
@@ -2221,8 +2277,10 @@ PackedVector3Array Curve3D::tessellate_even_length(int p_max_stages, real_t p_le
return tess;
}
+ const int num_intervals = closed ? points.size() : points.size() - 1;
+ // Point Count: Begins at 1 to account for the last point.
int pc = 1;
- for (int i = 0; i < points.size() - 1; i++) {
+ for (int i = 0; i < num_intervals; i++) {
pc++;
pc += midpoints[i].size();
}
@@ -2232,14 +2290,18 @@ PackedVector3Array Curve3D::tessellate_even_length(int p_max_stages, real_t p_le
bpw[0] = points[0].position;
int pidx = 0;
- for (int i = 0; i < points.size() - 1; i++) {
+ for (int i = 0; i < num_intervals; i++) {
for (const KeyValue<real_t, Vector3> &E : midpoints[i]) {
pidx++;
bpw[pidx] = E.value;
}
pidx++;
- bpw[pidx] = points[i + 1].position;
+ if (!closed || i < num_intervals - 1) {
+ bpw[pidx] = points[i + 1].position;
+ } else {
+ bpw[pidx] = points[0].position;
+ }
}
return tess;
@@ -2295,13 +2357,13 @@ void Curve3D::_get_property_list(List<PropertyInfo> *p_list) const {
pi.usage &= ~PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
- if (i != 0) {
+ if (closed || i != 0) {
pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/in", i));
pi.usage &= ~PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
}
- if (i != points.size() - 1) {
+ if (closed || i != points.size() - 1) {
pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/out", i));
pi.usage &= ~PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
@@ -2329,6 +2391,8 @@ void Curve3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear_points"), &Curve3D::clear_points);
ClassDB::bind_method(D_METHOD("sample", "idx", "t"), &Curve3D::sample);
ClassDB::bind_method(D_METHOD("samplef", "fofs"), &Curve3D::samplef);
+ ClassDB::bind_method(D_METHOD("set_closed", "closed"), &Curve3D::set_closed);
+ ClassDB::bind_method(D_METHOD("is_closed"), &Curve3D::is_closed);
//ClassDB::bind_method(D_METHOD("bake","subdivs"),&Curve3D::bake,DEFVAL(10));
ClassDB::bind_method(D_METHOD("set_bake_interval", "distance"), &Curve3D::set_bake_interval);
ClassDB::bind_method(D_METHOD("get_bake_interval"), &Curve3D::get_bake_interval);
@@ -2350,6 +2414,8 @@ void Curve3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_get_data"), &Curve3D::_get_data);
ClassDB::bind_method(D_METHOD("_set_data", "data"), &Curve3D::_set_data);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "closed"), "set_closed", "is_closed");
+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval");
ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
ADD_ARRAY_COUNT("Points", "point_count", "set_point_count", "get_point_count", "point_");
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index 6da337a93f..154d91e23b 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -264,6 +264,8 @@ class Curve3D : public Resource {
mutable Vector<size_t> points_in_cache;
#endif
+ bool closed = false;
+
mutable bool baked_cache_dirty = false;
mutable PackedVector3Array baked_point_cache;
mutable Vector<real_t> baked_tilt_cache;
@@ -330,6 +332,8 @@ public:
Vector3 sample(int p_index, real_t p_offset) const;
Vector3 samplef(real_t p_findex) const;
+ void set_closed(bool p_closed);
+ bool is_closed() const;
void set_bake_interval(real_t p_tolerance);
real_t get_bake_interval() const;
void set_up_vector_enabled(bool p_enable);
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index a4677d917d..ae70443e6a 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -1734,7 +1734,7 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi
while (true) {
String line = f->get_line();
- int delimiter = line.find(" ");
+ int delimiter = line.find_char(' ');
String type = line.substr(0, delimiter);
int pos = delimiter + 1;
HashMap<String, String> keys;
@@ -1744,7 +1744,7 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi
}
while (pos < line.size()) {
- int eq = line.find("=", pos);
+ int eq = line.find_char('=', pos);
if (eq == -1) {
break;
}
@@ -1752,14 +1752,14 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi
int end = -1;
String value;
if (line[eq + 1] == '"') {
- end = line.find("\"", eq + 2);
+ end = line.find_char('"', eq + 2);
if (end == -1) {
break;
}
value = line.substr(eq + 2, end - 1 - eq - 1);
pos = end + 1;
} else {
- end = line.find(" ", eq + 1);
+ end = line.find_char(' ', eq + 1);
if (end == -1) {
end = line.size();
}
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index b0353b4f2c..8c0e087902 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -1315,7 +1315,7 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) {
String sname = p_name;
if (sname.begins_with("surface_")) {
- int sl = sname.find("/");
+ int sl = sname.find_char('/');
if (sl == -1) {
return false;
}
@@ -1708,7 +1708,7 @@ bool ArrayMesh::_get(const StringName &p_name, Variant &r_ret) const {
String sname = p_name;
if (sname.begins_with("surface_")) {
- int sl = sname.find("/");
+ int sl = sname.find_char('/');
if (sl == -1) {
return false;
}
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 809a77a487..d7036fd6d5 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -822,10 +822,10 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has
value = missing_resource_properties[E.name];
}
} else if (E.type == Variant::ARRAY && E.hint == PROPERTY_HINT_TYPE_STRING) {
- int hint_subtype_separator = E.hint_string.find(":");
+ int hint_subtype_separator = E.hint_string.find_char(':');
if (hint_subtype_separator >= 0) {
String subtype_string = E.hint_string.substr(0, hint_subtype_separator);
- int slash_pos = subtype_string.find("/");
+ int slash_pos = subtype_string.find_char('/');
PropertyHint subtype_hint = PropertyHint::PROPERTY_HINT_NONE;
if (slash_pos >= 0) {
subtype_hint = PropertyHint(subtype_string.get_slice("/", 1).to_int());
@@ -851,11 +851,11 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has
}
}
} else if (E.type == Variant::DICTIONARY && E.hint == PROPERTY_HINT_TYPE_STRING) {
- int key_value_separator = E.hint_string.find(";");
+ int key_value_separator = E.hint_string.find_char(';');
if (key_value_separator >= 0) {
- int key_subtype_separator = E.hint_string.find(":");
+ int key_subtype_separator = E.hint_string.find_char(':');
String key_subtype_string = E.hint_string.substr(0, key_subtype_separator);
- int key_slash_pos = key_subtype_string.find("/");
+ int key_slash_pos = key_subtype_string.find_char('/');
PropertyHint key_subtype_hint = PropertyHint::PROPERTY_HINT_NONE;
if (key_slash_pos >= 0) {
key_subtype_hint = PropertyHint(key_subtype_string.get_slice("/", 1).to_int());
@@ -864,9 +864,9 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has
Variant::Type key_subtype = Variant::Type(key_subtype_string.to_int());
bool convert_key = key_subtype == Variant::OBJECT && key_subtype_hint == PROPERTY_HINT_NODE_TYPE;
- int value_subtype_separator = E.hint_string.find(":", key_value_separator) - (key_value_separator + 1);
+ int value_subtype_separator = E.hint_string.find_char(':', key_value_separator) - (key_value_separator + 1);
String value_subtype_string = E.hint_string.substr(key_value_separator + 1, value_subtype_separator);
- int value_slash_pos = value_subtype_string.find("/");
+ int value_slash_pos = value_subtype_string.find_char('/');
PropertyHint value_subtype_hint = PropertyHint::PROPERTY_HINT_NONE;
if (value_slash_pos >= 0) {
value_subtype_hint = PropertyHint(value_subtype_string.get_slice("/", 1).to_int());
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 6e03fe25c9..03f0e107e4 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -1782,7 +1782,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso
for (KeyValue<Ref<Resource>, String> &E : external_resources) {
String cached_id = E.key->get_id_for_path(local_path);
if (cached_id.is_empty() || cached_ids_found.has(cached_id)) {
- int sep_pos = E.value.find("_");
+ int sep_pos = E.value.find_char('_');
if (sep_pos != -1) {
E.value = E.value.substr(0, sep_pos + 1); // Keep the order found, for improved thread loading performance.
} else {
diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp
index da90ba1ef2..4bc03a049a 100644
--- a/scene/resources/syntax_highlighter.cpp
+++ b/scene/resources/syntax_highlighter.cpp
@@ -205,7 +205,7 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
if (end_key_length == 0 || color_regions[c].line_only || from + end_key_length > line_length) {
if (from + end_key_length > line_length && (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'")) {
// If it's key length and there is a '\', dont skip to highlight esc chars.
- if (str.find("\\", from) >= 0) {
+ if (str.find_char('\\', from) >= 0) {
break;
}
}
@@ -242,7 +242,7 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
for (; from < line_length; from++) {
if (line_length - from < end_key_length) {
// Don't break if '\' to highlight esc chars.
- if (!is_string || str.find("\\", from) < 0) {
+ if (!is_string || str.find_char('\\', from) < 0) {
break;
}
}
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index 5ad92bd211..c9d5e51753 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -2064,6 +2064,10 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const
surf = surf->next;
}
}
+
+ if (p_render_list == RENDER_LIST_OPAQUE && lightmap_captures_used) {
+ RD::get_singleton()->buffer_update(scene_state.lightmap_capture_buffer, 0, sizeof(LightmapCaptureData) * lightmap_captures_used, scene_state.lightmap_captures);
+ }
}
void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, const Color &p_default_bg_color, bool p_opaque_render_buffers, bool p_pancake_shadows, int p_index) {
diff --git a/tests/scene/test_texture_progress_bar.h b/tests/scene/test_texture_progress_bar.h
new file mode 100644
index 0000000000..5eb7495a29
--- /dev/null
+++ b/tests/scene/test_texture_progress_bar.h
@@ -0,0 +1,92 @@
+/**************************************************************************/
+/* test_texture_progress_bar.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_TEXTURE_PROGRESS_BAR_H
+#define TEST_TEXTURE_PROGRESS_BAR_H
+
+#include "scene/gui/texture_progress_bar.h"
+
+#include "tests/test_macros.h"
+
+namespace TestTextureProgressBar {
+
+TEST_CASE("[SceneTree][TextureProgressBar]") {
+ TextureProgressBar *texture_progress_bar = memnew(TextureProgressBar);
+
+ SUBCASE("[TextureProgressBar] set_radial_initial_angle() should wrap angle between 0 and 360 degrees (inclusive).") {
+ texture_progress_bar->set_radial_initial_angle(0.0);
+ CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)0.0));
+
+ texture_progress_bar->set_radial_initial_angle(360.0);
+ CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)360.0));
+
+ texture_progress_bar->set_radial_initial_angle(30.5);
+ CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)30.5));
+
+ texture_progress_bar->set_radial_initial_angle(-30.5);
+ CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)(360 - 30.5)));
+
+ texture_progress_bar->set_radial_initial_angle(36000 + 30.5);
+ CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)30.5));
+
+ texture_progress_bar->set_radial_initial_angle(-(36000 + 30.5));
+ CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)(360 - 30.5)));
+ }
+
+ SUBCASE("[TextureProgressBar] set_radial_initial_angle() should not set non-finite values.") {
+ texture_progress_bar->set_radial_initial_angle(30.5);
+
+ ERR_PRINT_OFF;
+ texture_progress_bar->set_radial_initial_angle(INFINITY);
+ ERR_PRINT_ON;
+ CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)30.5));
+
+ ERR_PRINT_OFF;
+ texture_progress_bar->set_radial_initial_angle(-INFINITY);
+ ERR_PRINT_ON;
+ CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)30.5));
+
+ ERR_PRINT_OFF;
+ texture_progress_bar->set_radial_initial_angle(NAN);
+ ERR_PRINT_ON;
+ CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)30.5));
+
+ ERR_PRINT_OFF;
+ texture_progress_bar->set_radial_initial_angle(-NAN);
+ ERR_PRINT_ON;
+ CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)30.5));
+ }
+
+ memdelete(texture_progress_bar);
+}
+
+} // namespace TestTextureProgressBar
+
+#endif // TEST_TEXTURE_PROGRESS_BAR_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 65d45ae92f..d860f7b843 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -130,6 +130,7 @@
#include "tests/scene/test_physics_material.h"
#include "tests/scene/test_sprite_frames.h"
#include "tests/scene/test_style_box_texture.h"
+#include "tests/scene/test_texture_progress_bar.h"
#include "tests/scene/test_theme.h"
#include "tests/scene/test_timer.h"
#include "tests/scene/test_viewport.h"