From b90bec546df383a433c66313364470d3e7664f1e Mon Sep 17 00:00:00 2001 From: Haoyu Qiu Date: Thu, 16 Dec 2021 18:22:44 +0800 Subject: [PATCH 01/13] Make `--doctool` locale aware * Adds `indent(prefix)` to `String` * Moves the loading of tool/doc translation into `editor/editor_translation.{h,cpp}` * Makes use of doc translation when generating XML class references, and setup the translation locale based on `-l LOCALE` CLI parameter. The XML class reference won't be translated if `-l LOCALE` parameter is not given, or when it's `-l en`. (cherry picked from commit c11b1850c451b301be24559ba489c52ca63fb8bf) --- core/ustring.cpp | 21 ++++++++ core/ustring.h | 1 + core/variant_call.cpp | 2 + doc/classes/String.xml | 11 +++- editor/doc/doc_data.cpp | 44 +++++++++++++--- editor/editor_settings.cpp | 61 +++------------------ editor/editor_translation.cpp | 99 +++++++++++++++++++++++++++++++++++ editor/editor_translation.h | 41 +++++++++++++++ main/main.cpp | 7 ++- 9 files changed, 223 insertions(+), 64 deletions(-) create mode 100644 editor/editor_translation.cpp create mode 100644 editor/editor_translation.h diff --git a/core/ustring.cpp b/core/ustring.cpp index c43e3def3b0..eec5dc3649b 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -3113,6 +3113,27 @@ CharType String::ord_at(int p_idx) const { return operator[](p_idx); } +String String::indent(const String &p_prefix) const { + String new_string; + int line_start = 0; + + for (int i = 0; i < length(); i++) { + const char32_t c = operator[](i); + if (c == '\n') { + if (i == line_start) { + new_string += c; // Leave empty lines empty. + } else { + new_string += p_prefix + substr(line_start, i - line_start + 1); + } + line_start = i + 1; + } + } + if (line_start != length()) { + new_string += p_prefix + substr(line_start); + } + return new_string; +} + String String::dedent() const { String new_string; String indent; diff --git a/core/ustring.h b/core/ustring.h index bdc75c7a849..f575f620bde 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -282,6 +282,7 @@ public: String left(int p_pos) const; String right(int p_pos) const; + String indent(const String &p_prefix) const; String dedent() const; String strip_edges(bool left = true, bool right = true) const; String strip_escapes() const; diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 12f36bab4e1..2258b519e42 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -271,6 +271,7 @@ struct _VariantCall { VCALL_LOCALMEM0R(String, to_lower); VCALL_LOCALMEM1R(String, left); VCALL_LOCALMEM1R(String, right); + VCALL_LOCALMEM1R(String, indent); VCALL_LOCALMEM0R(String, dedent); VCALL_LOCALMEM2R(String, strip_edges); VCALL_LOCALMEM0R(String, strip_escapes); @@ -1675,6 +1676,7 @@ void register_variant_methods() { ADDFUNC0R(STRING, STRING, String, get_basename, varray()); ADDFUNC1R(STRING, STRING, String, plus_file, STRING, "file", varray()); ADDFUNC1R(STRING, INT, String, ord_at, INT, "at", varray()); + ADDFUNC1R(STRING, STRING, String, indent, STRING, "prefix", varray()); ADDFUNC0R(STRING, STRING, String, dedent, varray()); ADDFUNC2(STRING, NIL, String, erase, INT, "position", INT, "chars", varray()); ADDFUNC0R(STRING, INT, String, hash, varray()); diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 50bc9e66e71..96768c1e84a 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -241,7 +241,7 @@ - Returns a copy of the string with indentation (leading tabs and spaces) removed. + Returns a copy of the string with indentation (leading tabs and spaces) removed. See also [method indent] to add indentation. @@ -380,6 +380,15 @@ [/codeblock] + + + + + Returns a copy of the string with lines indented with [code]prefix[/code]. + For example, the string can be indented with two tabs using [code]"\t\t"[/code], or four spaces using [code]" "[/code]. The prefix can be any string so it can also be used to comment out strings with e.g. [code]"# "[/code]. See also [method dedent] to remove indentation. + [b]Note:[/b] Empty lines are kept empty. + + diff --git a/editor/doc/doc_data.cpp b/editor/doc/doc_data.cpp index 20eae0a8c58..bdcc92d4cd2 100644 --- a/editor/doc/doc_data.cpp +++ b/editor/doc/doc_data.cpp @@ -37,9 +37,39 @@ #include "core/os/dir_access.h" #include "core/project_settings.h" #include "core/script_language.h" +#include "core/translation.h" #include "core/version.h" #include "scene/resources/theme.h" +static String _get_indent(const String &p_text) { + String indent; + bool has_text = false; + int line_start = 0; + + for (int i = 0; i < p_text.length(); i++) { + const char32_t c = p_text[i]; + if (c == '\n') { + line_start = i + 1; + } else if (c > 32) { + has_text = true; + indent = p_text.substr(line_start, i - line_start); + break; // Indentation of the first line that has text. + } + } + if (!has_text) { + return p_text; + } + return indent; +} + +static String _translate_doc_string(const String &p_text) { + const String indent = _get_indent(p_text); + const String message = p_text.dedent().strip_edges(); + const String translated = TranslationServer::get_singleton()->doc_translate(message); + // No need to restore stripped edges because they'll be stripped again later. + return translated.indent(indent); +} + void DocData::merge_from(const DocData &p_data) { for (Map::Element *E = class_list.front(); E; E = E->next()) { ClassDoc &c = E->get(); @@ -1051,11 +1081,11 @@ Error DocData::save_classes(const String &p_default_path, const Map"); - _write_string(f, 2, c.brief_description.strip_edges().xml_escape()); + _write_string(f, 2, _translate_doc_string(c.brief_description).strip_edges().xml_escape()); _write_string(f, 1, ""); _write_string(f, 1, ""); - _write_string(f, 2, c.description.strip_edges().xml_escape()); + _write_string(f, 2, _translate_doc_string(c.description).strip_edges().xml_escape()); _write_string(f, 1, ""); _write_string(f, 1, ""); @@ -1104,7 +1134,7 @@ Error DocData::save_classes(const String &p_default_path, const Map"); - _write_string(f, 4, m.description.strip_edges().xml_escape()); + _write_string(f, 4, _translate_doc_string(m.description).strip_edges().xml_escape()); _write_string(f, 3, ""); _write_string(f, 2, ""); @@ -1132,7 +1162,7 @@ Error DocData::save_classes(const String &p_default_path, const Map"); } else { _write_string(f, 2, ""); - _write_string(f, 3, p.description.strip_edges().xml_escape()); + _write_string(f, 3, _translate_doc_string(p.description).strip_edges().xml_escape()); _write_string(f, 2, ""); } } @@ -1152,7 +1182,7 @@ Error DocData::save_classes(const String &p_default_path, const Map"); - _write_string(f, 4, m.description.strip_edges().xml_escape()); + _write_string(f, 4, _translate_doc_string(m.description).strip_edges().xml_escape()); _write_string(f, 3, ""); _write_string(f, 2, ""); @@ -1178,7 +1208,7 @@ Error DocData::save_classes(const String &p_default_path, const Map"); } } - _write_string(f, 3, k.description.strip_edges().xml_escape()); + _write_string(f, 3, _translate_doc_string(k.description).strip_edges().xml_escape()); _write_string(f, 2, ""); } @@ -1197,7 +1227,7 @@ Error DocData::save_classes(const String &p_default_path, const Map"); } - _write_string(f, 3, ti.description.strip_edges().xml_escape()); + _write_string(f, 3, _translate_doc_string(ti.description).strip_edges().xml_escape()); _write_string(f, 2, ""); } diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index a672aff6fb2..67145002b86 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -31,22 +31,18 @@ #include "editor_settings.h" #include "core/io/certs_compressed.gen.h" -#include "core/io/compression.h" #include "core/io/config_file.h" -#include "core/io/file_access_memory.h" #include "core/io/ip.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" -#include "core/io/translation_loader_po.h" #include "core/os/dir_access.h" #include "core/os/file_access.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/project_settings.h" #include "core/version.h" -#include "editor/doc_translations.gen.h" #include "editor/editor_node.h" -#include "editor/editor_translations.gen.h" +#include "editor/editor_translation.h" #include "scene/main/node.h" #include "scene/main/scene_tree.h" #include "scene/main/viewport.h" @@ -268,17 +264,14 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { const Vector locales_to_skip = String("ar,bn,fa,he,hi,ml,si,ta,te,ur").split(","); String best; - - EditorTranslationList *etl = _editor_translations; - - while (etl->data) { - const String &locale = etl->lang; + const Vector &locales = get_editor_locales(); + for (int i = 0; i < locales.size(); i++) { + const String &locale = locales[i]; // Skip locales which we can't render properly (see above comment). // Test against language code without regional variants (e.g. ur_PK). String lang_code = locale.get_slice("_", 0); if (locales_to_skip.find(lang_code) != -1) { - etl++; continue; } @@ -292,8 +285,6 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { if (best == String() && host_lang.begins_with(locale)) { best = locale; } - - etl++; } if (best == String()) { @@ -1009,50 +1000,10 @@ void EditorSettings::setup_language() { } // Load editor translation for configured/detected locale. - EditorTranslationList *etl = _editor_translations; - while (etl->data) { - if (etl->lang == lang) { - Vector data; - data.resize(etl->uncomp_size); - Compression::decompress(data.ptrw(), etl->uncomp_size, etl->data, etl->comp_size, Compression::MODE_DEFLATE); - - FileAccessMemory *fa = memnew(FileAccessMemory); - fa->open_custom(data.ptr(), data.size()); - - Ref tr = TranslationLoaderPO::load_translation(fa); - - if (tr.is_valid()) { - tr->set_locale(etl->lang); - TranslationServer::get_singleton()->set_tool_translation(tr); - break; - } - } - - etl++; - } + load_editor_translations(lang); // Load class reference translation. - DocTranslationList *dtl = _doc_translations; - while (dtl->data) { - if (dtl->lang == lang) { - Vector data; - data.resize(dtl->uncomp_size); - Compression::decompress(data.ptrw(), dtl->uncomp_size, dtl->data, dtl->comp_size, Compression::MODE_DEFLATE); - - FileAccessMemory *fa = memnew(FileAccessMemory); - fa->open_custom(data.ptr(), data.size()); - - Ref tr = TranslationLoaderPO::load_translation(fa); - - if (tr.is_valid()) { - tr->set_locale(dtl->lang); - TranslationServer::get_singleton()->set_doc_translation(tr); - break; - } - } - - dtl++; - } + load_doc_translations(lang); } void EditorSettings::setup_network() { diff --git a/editor/editor_translation.cpp b/editor/editor_translation.cpp new file mode 100644 index 00000000000..23145c27c81 --- /dev/null +++ b/editor/editor_translation.cpp @@ -0,0 +1,99 @@ +/*************************************************************************/ +/* editor_translation.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "editor/editor_translation.h" + +#include "core/io/compression.h" +#include "core/io/file_access_memory.h" +#include "core/io/translation_loader_po.h" +#include "editor/doc_translations.gen.h" +#include "editor/editor_translations.gen.h" + +Vector get_editor_locales() { + Vector locales; + + EditorTranslationList *etl = _editor_translations; + while (etl->data) { + const String &locale = etl->lang; + locales.push_back(locale); + + etl++; + } + + return locales; +} + +void load_editor_translations(const String &p_locale) { + EditorTranslationList *etl = _editor_translations; + while (etl->data) { + if (etl->lang == p_locale) { + Vector data; + data.resize(etl->uncomp_size); + Compression::decompress(data.ptrw(), etl->uncomp_size, etl->data, etl->comp_size, Compression::MODE_DEFLATE); + + FileAccessMemory *fa = memnew(FileAccessMemory); + fa->open_custom(data.ptr(), data.size()); + + Ref tr = TranslationLoaderPO::load_translation(fa); + + if (tr.is_valid()) { + tr->set_locale(etl->lang); + TranslationServer::get_singleton()->set_tool_translation(tr); + break; + } + } + + etl++; + } +} + +void load_doc_translations(const String &p_locale) { + DocTranslationList *dtl = _doc_translations; + while (dtl->data) { + if (dtl->lang == p_locale) { + Vector data; + data.resize(dtl->uncomp_size); + Compression::decompress(data.ptrw(), dtl->uncomp_size, dtl->data, dtl->comp_size, Compression::MODE_DEFLATE); + + FileAccessMemory *fa = memnew(FileAccessMemory); + fa->open_custom(data.ptr(), data.size()); + + Ref tr = TranslationLoaderPO::load_translation(fa); + + if (tr.is_valid()) { + tr->set_locale(dtl->lang); + TranslationServer::get_singleton()->set_doc_translation(tr); + break; + } + } + + dtl++; + } +} diff --git a/editor/editor_translation.h b/editor/editor_translation.h new file mode 100644 index 00000000000..43e7fea8f1e --- /dev/null +++ b/editor/editor_translation.h @@ -0,0 +1,41 @@ +/*************************************************************************/ +/* editor_translation.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 EDITOR_TRANSLATION_H +#define EDITOR_TRANSLATION_H + +#include "core/ustring.h" +#include "core/vector.h" + +Vector get_editor_locales(); +void load_editor_translations(const String &p_locale); +void load_doc_translations(const String &p_locale); + +#endif // EDITOR_TRANSLATION_H diff --git a/main/main.cpp b/main/main.cpp index cd5a4b50aad..818d7c9fcfa 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -75,6 +75,7 @@ #include "editor/doc/doc_data_class_path.gen.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" +#include "editor/editor_translation.h" #include "editor/progress_dialog.h" #include "editor/project_manager.h" #ifndef NO_EDITOR_SPLASH @@ -1498,7 +1499,6 @@ Error Main::setup2(Thread::ID p_main_tid_override) { VisualServer::get_singleton()->callbacks_register(visual_server_callbacks); _start_success = true; - locale = String(); ClassDB::set_current_api(ClassDB::API_NONE); //no more api is registered at this point @@ -1606,6 +1606,11 @@ bool Main::start() { if (doc_tool_path != "") { Engine::get_singleton()->set_editor_hint(true); // Needed to instance editor-only classes for their default values + // Translate the class reference only when `-l LOCALE` parameter is given. + if (!locale.empty() && locale != "en") { + load_doc_translations(locale); + } + { DirAccessRef da = DirAccess::open(doc_tool_path); ERR_FAIL_COND_V_MSG(!da, false, "Argument supplied to --doctool must be a valid directory path."); From adc6ec0abba0a42dd3f1d18f0a9ff9d7e8330987 Mon Sep 17 00:00:00 2001 From: Max Hilbrunner Date: Fri, 29 Oct 2021 15:43:17 +0200 Subject: [PATCH 02/13] Websockets: Fix buffer size checks in put_packet() (cherry picked from commit 6a92a2e92d1a968192917ad7908df2fc83768e79) --- modules/websocket/emws_peer.cpp | 7 +++++-- modules/websocket/wsl_peer.cpp | 7 +++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp index c7eb5726176..121481ad0fc 100644 --- a/modules/websocket/emws_peer.cpp +++ b/modules/websocket/emws_peer.cpp @@ -54,11 +54,14 @@ Error EMWSPeer::read_msg(const uint8_t *p_data, uint32_t p_size, bool p_is_strin } Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - ERR_FAIL_COND_V(_out_buf_size && ((uint64_t)godot_js_websocket_buffered_amount(peer_sock) >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY); + ERR_FAIL_COND_V(_out_buf_size && ((uint64_t)godot_js_websocket_buffered_amount(peer_sock) + p_buffer_size >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY); int is_bin = write_mode == WebSocketPeer::WRITE_MODE_BINARY ? 1 : 0; - godot_js_websocket_send(peer_sock, p_buffer, p_buffer_size, is_bin); + if (godot_js_websocket_send(peer_sock, p_buffer, p_buffer_size, is_bin) != 0) { + return FAILED; + } + return OK; }; diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp index ad093539975..459ca3e48cb 100644 --- a/modules/websocket/wsl_peer.cpp +++ b/modules/websocket/wsl_peer.cpp @@ -242,15 +242,14 @@ void WSLPeer::poll() { Error WSLPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); ERR_FAIL_COND_V(_out_pkt_size && (wslay_event_get_queued_msg_count(_data->ctx) >= (1ULL << _out_pkt_size)), ERR_OUT_OF_MEMORY); - ERR_FAIL_COND_V(_out_buf_size && (wslay_event_get_queued_msg_length(_data->ctx) >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY); + ERR_FAIL_COND_V(_out_buf_size && (wslay_event_get_queued_msg_length(_data->ctx) + p_buffer_size >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY); - struct wslay_event_msg msg; // Should I use fragmented? + struct wslay_event_msg msg; msg.opcode = write_mode == WRITE_MODE_TEXT ? WSLAY_TEXT_FRAME : WSLAY_BINARY_FRAME; msg.msg = p_buffer; msg.msg_length = p_buffer_size; - wslay_event_queue_msg(_data->ctx, &msg); - if (wslay_event_send(_data->ctx) < 0) { + if (wslay_event_queue_msg(_data->ctx, &msg) != 0 || wslay_event_send(_data->ctx) != 0) { close_now(); return FAILED; } From e058748d0661b142530453d5e6ed9c7f35f37273 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Mon, 6 Dec 2021 16:40:30 +0100 Subject: [PATCH 03/13] Document physics simulation being limited to 8 ticks per rendered frame (cherry picked from commit bfe77b8fab08e65a64450ec329096316a1923cd7) --- doc/classes/Engine.xml | 3 ++- doc/classes/ProjectSettings.xml | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml index 2758e1c3f0b..50b24403b70 100644 --- a/doc/classes/Engine.xml +++ b/doc/classes/Engine.xml @@ -150,7 +150,8 @@ [b]Note:[/b] To detect whether the script is run from an editor [i]build[/i] (e.g. when pressing [code]F5[/code]), use [method OS.has_feature] with the [code]"editor"[/code] argument instead. [code]OS.has_feature("editor")[/code] will evaluate to [code]true[/code] both when the code is running in the editor and when running the project from the editor, but it will evaluate to [code]false[/code] when the code is run from an exported project. - The number of fixed iterations per second. This controls how often physics simulation and [method Node._physics_process] methods are run. This value should generally always be set to [code]60[/code] or above, as Godot doesn't interpolate the physics step. As a result, values lower than [code]60[/code] will look stuttery. This value can be increased to make input more reactive or work around tunneling issues, but keep in mind doing so will increase CPU usage. + The number of fixed iterations per second. This controls how often physics simulation and [method Node._physics_process] methods are run. This value should generally always be set to [code]60[/code] or above, as Godot doesn't interpolate the physics step. As a result, values lower than [code]60[/code] will look stuttery. This value can be increased to make input more reactive or work around collision tunneling issues, but keep in mind doing so will increase CPU usage. See also [member target_fps] and [member ProjectSettings.physics/common/physics_fps]. + [b]Note:[/b] Only 8 physics ticks may be simulated per rendered frame at most. If more than 8 physics ticks have to be simulated per rendered frame to keep up with rendering, the game will appear to slow down (even if [code]delta[/code] is used consistently in physics calculations). Therefore, it is recommended not to increase [member Engine.iterations_per_second] above 240. Otherwise, the game will slow down when the rendering framerate goes below 30 FPS. Controls how much physics ticks are synchronized with real time. For 0 or less, the ticks are synchronized. Such values are recommended for network games, where clock synchronization matters. Higher values cause higher deviation of the in-game clock and real clock but smooth out framerate jitters. The default value of 0.5 should be fine for most; values above 2 could cause the game to react to dropped frames with a noticeable delay and are not recommended. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index ed055c6ebb8..cfe30ddc8f8 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -398,9 +398,10 @@ Message to be displayed before the backtrace when the engine crashes. - Maximum number of frames per second allowed. The actual number of frames per second may still be below this value if the game is lagging. + Maximum number of frames per second allowed. The actual number of frames per second may still be below this value if the game is lagging. See also [member physics/common/physics_fps]. If [member display/window/vsync/use_vsync] is enabled, it takes precedence and the forced FPS number cannot exceed the monitor's refresh rate. This setting is therefore mostly relevant for lowering the maximum FPS below VSync, e.g. to perform non-real-time rendering of static frames, or test the project under lag conditions. + [b]Note:[/b] This property is only read when the project starts. To change the rendering FPS cap at runtime, set [member Engine.target_fps] instead. Maximum call stack allowed for debugging GDScript. @@ -1122,8 +1123,9 @@ If disabled, the legacy behavior is used, which consists in queuing the picking input events during pause (so nodes won't get them) and flushing that queue on resume, against the state of the 2D/3D world at that point. - The number of fixed iterations per second. This controls how often physics simulation and [method Node._physics_process] methods are run. + The number of fixed iterations per second. This controls how often physics simulation and [method Node._physics_process] methods are run. See also [member debug/settings/fps/force_fps]. [b]Note:[/b] This property is only read when the project starts. To change the physics FPS at runtime, set [member Engine.iterations_per_second] instead. + [b]Note:[/b] Only 8 physics ticks may be simulated per rendered frame at most. If more than 8 physics ticks have to be simulated per rendered frame to keep up with rendering, the game will appear to slow down (even if [code]delta[/code] is used consistently in physics calculations). Therefore, it is recommended not to increase [member physics/common/physics_fps] above 240. Otherwise, the game will slow down when the rendering framerate goes below 30 FPS. Controls how much physics ticks are synchronized with real time. For 0 or less, the ticks are synchronized. Such values are recommended for network games, where clock synchronization matters. Higher values cause higher deviation of in-game clock and real clock, but allows smoothing out framerate jitters. The default value of 0.5 should be fine for most; values above 2 could cause the game to react to dropped frames with a noticeable delay and are not recommended. From ecdd46ff229519ca4c9bfc4ebd59a6765ef3a090 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Thu, 9 Dec 2021 23:47:52 +0100 Subject: [PATCH 04/13] Document sampling a TextureArray with sRGB -> linear conversion This is required when sampling an albedo map from a texture array in 3D. Otherwise, colors will look washed out. (cherry picked from commit 491acf346dcba74cb10352570e840e4297b495bc) --- doc/classes/Texture3D.xml | 3 ++- doc/classes/TextureArray.xml | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/doc/classes/Texture3D.xml b/doc/classes/Texture3D.xml index 4f53a189c5a..04b3eb77652 100644 --- a/doc/classes/Texture3D.xml +++ b/doc/classes/Texture3D.xml @@ -4,7 +4,8 @@ Texture with 3 dimensions. - Texture3D is a 3-dimensional texture that has a width, height, and depth. + Texture3D is a 3-dimensional [Texture] that has a width, height, and depth. See also [TextureArray]. + [b]Note:[/b] [Texture3D]s can only be sampled in shaders in the GLES3 backend. In GLES2, their data can be accessed via scripting, but there is no way to render them in a hardware-accelerated manner. diff --git a/doc/classes/TextureArray.xml b/doc/classes/TextureArray.xml index b282d0bf40b..09391bd87fb 100644 --- a/doc/classes/TextureArray.xml +++ b/doc/classes/TextureArray.xml @@ -4,8 +4,8 @@ Array of textures stored in a single primitive. - [TextureArray]s store an array of [Image]s in a single [Texture] primitive. Each layer of the texture array has its own mipmap chain. This makes it is a good alternative to texture atlases. - [TextureArray]s must be displayed using shaders. After importing your file as a [TextureArray] and setting the appropriate Horizontal and Vertical Slices, display it by setting it as a uniform to a shader, for example: + [TextureArray]s store an array of [Image]s in a single [Texture] primitive. Each layer of the texture array has its own mipmap chain. This makes it is a good alternative to texture atlases. See also [Texture3D]. + [TextureArray]s must be displayed using shaders. After importing your file as a [TextureArray] and setting the appropriate Horizontal and Vertical Slices, display it by setting it as a uniform to a shader, for example (2D): [codeblock] shader_type canvas_item; @@ -17,6 +17,18 @@ } [/codeblock] Set the integer uniform "index" to show a particular part of the texture as defined by the Horizontal and Vertical Slices in the importer. + [b]Note:[/b] When sampling an albedo texture from a texture array in 3D, the sRGB -> linear conversion hint ([code]hint_albedo[/code]) should be used to prevent colors from looking washed out: + [codeblock] + shader_type spatial; + + uniform sampler2DArray tex : hint_albedo; + uniform int index; + + void fragment() { + ALBEDO = texture(tex, vec3(UV.x, UV.y, float(index))); + } + [/codeblock] + [b]Note:[/b] [TextureArray]s can only be sampled in shaders in the GLES3 backend. In GLES2, their data can be accessed via scripting, but there is no way to render them in a hardware-accelerated manner. From b8c1453f49c686fd7a683c306218cf2a66208836 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Sat, 11 Dec 2021 18:29:51 +0100 Subject: [PATCH 05/13] Improve the ReflectionProbe class documentation (cherry picked from commit 2fbf88091240c3bb8e67acb5368effbe0afb8ae3) --- doc/classes/ReflectionProbe.xml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/doc/classes/ReflectionProbe.xml b/doc/classes/ReflectionProbe.xml index b5d7a5316e7..470b38ee7a1 100644 --- a/doc/classes/ReflectionProbe.xml +++ b/doc/classes/ReflectionProbe.xml @@ -1,12 +1,13 @@ - Captures its surroundings to create reflections. + Captures its surroundings to create fast, accurate reflections from a given point. Capture its surroundings as a dual paraboloid image, and stores versions of it with increasing levels of blur to simulate different material roughnesses. - The [ReflectionProbe] is used to create high-quality reflections at the cost of performance. It can be combined with [GIProbe]s and Screen Space Reflections to achieve high quality reflections. [ReflectionProbe]s render all objects within their [member cull_mask], so updating them can be quite expensive. It is best to update them once with the important static objects and then leave them. - [b]Note:[/b] By default Godot will only render 16 reflection probes. If you need more, increase the number of atlas subdivisions. This setting can be found in [member ProjectSettings.rendering/quality/reflections/atlas_subdiv]. + The [ReflectionProbe] is used to create high-quality reflections at a low performance cost (when [member update_mode] is [constant UPDATE_ONCE]). [ReflectionProbe]s can be blended together and with the rest of the scene smoothly. [ReflectionProbe]s can also be combined with [GIProbe] and screen-space reflections ([member Environment.ss_reflections_enabled]) to get more accurate reflections in specific areas. [ReflectionProbe]s render all objects within their [member cull_mask], so updating them can be quite expensive. It is best to update them once with the important static objects and then leave them as-is. + [b]Note:[/b] Unlike [GIProbe], [ReflectionProbe]s only source their environment from a [WorldEnvironment] node. If you specify an [Environment] resource within a [Camera] node, it will be ignored by the [ReflectionProbe]. This can lead to incorrect lighting within the [ReflectionProbe]. + [b]Note:[/b] By default, Godot will only render 16 reflection probes. If you need more, increase the number of atlas subdivisions. This setting can be found in [member ProjectSettings.rendering/quality/reflections/atlas_subdiv]. [b]Note:[/b] The GLES2 backend will only display two reflection probes at the same time for a single mesh. If possible, split up large meshes that span over multiple reflection probes into smaller ones. @@ -17,15 +18,17 @@ If [code]true[/code], enables box projection. This makes reflections look more correct in rectangle-shaped rooms by offsetting the reflection center depending on the camera's location. + [b]Note:[/b] To better fit rectangle-shaped rooms that are not aligned to the grid, you can rotate the [ReflectionProbe] node. - Sets the cull mask which determines what objects are drawn by this probe. Every [VisualInstance] with a layer included in this cull mask will be rendered by the probe. It is best to only include large objects which are likely to take up a lot of space in the reflection in order to save on rendering cost. + Sets the cull mask which determines what objects are drawn by this probe. Every [VisualInstance] with a layer included in this cull mask will be rendered by the probe. To improve performance, it is best to only include large objects which are likely to take up a lot of space in the reflection. If [code]true[/code], computes shadows in the reflection probe. This makes the reflection probe slower to render; you may want to disable this if using the [constant UPDATE_ALWAYS] [member update_mode]. The size of the reflection probe. The larger the extents the more space covered by the probe which will lower the perceived resolution. It is best to keep the extents only as large as you need them. + [b]Note:[/b] To better fit areas that are not aligned to the grid, you can rotate the [ReflectionProbe] node. Defines the reflection intensity. Intensity modulates the strength of the reflection. @@ -43,21 +46,21 @@ If [code]true[/code], reflections will ignore sky contribution. Ambient lighting is then controlled by the [code]interior_ambient_*[/code] properties. - Sets the max distance away from the probe an object can be before it is culled. + The maximum distance away from the [ReflectionProbe] an object can be before it is culled. Decrease this to improve performance, especially when using the [constant UPDATE_ALWAYS] [member update_mode]. - Sets the origin offset to be used when this reflection probe is in box project mode. + Sets the origin offset to be used when this [ReflectionProbe] is in [member box_projection] mode. This can be set to a non-zero value to ensure a reflection fits a rectangle-shaped room, while reducing the amount of objects that "get in the way" of the reflection. - Sets how frequently the probe is updated. Can be [constant UPDATE_ONCE] or [constant UPDATE_ALWAYS]. + Sets how frequently the [ReflectionProbe] is updated. Can be [constant UPDATE_ONCE] or [constant UPDATE_ALWAYS]. - Update the probe once on the next frame. + Update the probe once on the next frame (recommended for most objects). The corresponding radiance map will be generated over the following six frames. This takes more time to update than [constant UPDATE_ALWAYS], but it has a lower performance cost and can result in higher-quality reflections. The ReflectionProbe is updated when its transform changes, but not when nearby geometry changes. You can force a [ReflectionProbe] update by moving the [ReflectionProbe] slightly in any direction. - Update the probe every frame. This is needed when you want to capture dynamic objects. However, it results in an increased render time. Use [constant UPDATE_ONCE] whenever possible. + Update the probe every frame. This provides better results for fast-moving dynamic objects (such as cars). However, it has a significant performance cost. Due to the cost, it's recommended to only use one ReflectionProbe with [constant UPDATE_ALWAYS] at most per scene. For all other use cases, use [constant UPDATE_ONCE]. From 1d9993936c6495d71715f2b3aaec29d5c47143fb Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Wed, 15 Dec 2021 11:20:17 +0100 Subject: [PATCH 06/13] [Net] Fix WebRTC returning packets from peers too early. Due to the async nature of WebRTC implementations, the multiplayer peer could end up having queued packets from a given connection before it is able to emit the "peer_added" signal. This commit ensures that packets from peers which are not notified yet are skipped by `get_packet` and `get_available_packet_count`. (cherry picked from commit 9b8db7c63a88eb3d82a9155a10fe9d9faee0fd14) --- modules/webrtc/webrtc_multiplayer.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/webrtc/webrtc_multiplayer.cpp b/modules/webrtc/webrtc_multiplayer.cpp index 64cd7c96c64..667fbfae464 100644 --- a/modules/webrtc/webrtc_multiplayer.cpp +++ b/modules/webrtc/webrtc_multiplayer.cpp @@ -154,6 +154,10 @@ void WebRTCMultiplayer::_find_next_peer() { } // After last. while (E) { + if (!E->get()->connected) { + E = E->next(); + continue; + } for (List>::Element *F = E->get()->channels.front(); F; F = F->next()) { if (F->get()->get_available_packet_count()) { next_packet_peer = E->key(); @@ -165,6 +169,10 @@ void WebRTCMultiplayer::_find_next_peer() { E = peer_map.front(); // Before last while (E) { + if (!E->get()->connected) { + E = E->next(); + continue; + } for (List>::Element *F = E->get()->channels.front(); F; F = F->next()) { if (F->get()->get_available_packet_count()) { next_packet_peer = E->key(); @@ -357,6 +365,9 @@ int WebRTCMultiplayer::get_available_packet_count() const { } int size = 0; for (Map>::Element *E = peer_map.front(); E; E = E->next()) { + if (!E->get()->connected) { + continue; + } for (List>::Element *F = E->get()->channels.front(); F; F = F->next()) { size += F->get()->get_available_packet_count(); } From e28860d00350b2e115c879ed90cb2a0f10bf59ea Mon Sep 17 00:00:00 2001 From: Haoyu Qiu Date: Wed, 15 Dec 2021 22:38:19 +0800 Subject: [PATCH 07/13] Fix indentations in class reference XMLs (cherry picked from commit b225fec1deb74fc1046e5a59cb8e63d9317ca2cd) --- doc/classes/AnimationNode.xml | 2 +- doc/classes/HMACContext.xml | 80 +++++++++++++++++------------------ doc/classes/PhysicsServer.xml | 1 - 3 files changed, 41 insertions(+), 42 deletions(-) diff --git a/doc/classes/AnimationNode.xml b/doc/classes/AnimationNode.xml index d8f0a344f9d..9e192f0a515 100644 --- a/doc/classes/AnimationNode.xml +++ b/doc/classes/AnimationNode.xml @@ -5,7 +5,7 @@ Base resource for [AnimationTree] nodes. In general, it's not used directly, but you can create custom ones with custom blending formulas. - Inherit this when creating nodes mainly for use in [AnimationNodeBlendTree], otherwise [AnimationRootNode] should be used instead. + Inherit this when creating nodes mainly for use in [AnimationNodeBlendTree], otherwise [AnimationRootNode] should be used instead. https://docs.godotengine.org/en/3.4/tutorials/animation/animation_tree.html diff --git a/doc/classes/HMACContext.xml b/doc/classes/HMACContext.xml index 6da83ba341a..b56c5d5cba1 100644 --- a/doc/classes/HMACContext.xml +++ b/doc/classes/HMACContext.xml @@ -5,49 +5,49 @@ The HMACContext class is useful for advanced HMAC use cases, such as streaming the message as it supports creating the message over time rather than providing it all at once. - [codeblock] - extends Node - var ctx = HMACContext.new() + [codeblock] + extends Node + var ctx = HMACContext.new() - func _ready(): - var key = "supersecret".to_utf8() - var err = ctx.start(HashingContext.HASH_SHA256, key) - assert(err == OK) - var msg1 = "this is ".to_utf8() - var msg2 = "vewy vewy secret".to_utf8() - err = ctx.update(msg1) - assert(err == OK) - err = ctx.update(msg2) - assert(err == OK) - var hmac = ctx.finish() - print(hmac.hex_encode()) - [/codeblock] + func _ready(): + var key = "supersecret".to_utf8() + var err = ctx.start(HashingContext.HASH_SHA256, key) + assert(err == OK) + var msg1 = "this is ".to_utf8() + var msg2 = "vewy vewy secret".to_utf8() + err = ctx.update(msg1) + assert(err == OK) + err = ctx.update(msg2) + assert(err == OK) + var hmac = ctx.finish() + print(hmac.hex_encode()) + [/codeblock] And in C# we can use the following. - [codeblock] - using Godot; - using System; - using System.Diagnostics; + [codeblock] + using Godot; + using System; + using System.Diagnostics; - public class CryptoNode : Node - { - private HMACContext ctx = new HMACContext(); - public override void _Ready() - { - PoolByteArray key = String("supersecret").to_utf8(); - Error err = ctx.Start(HashingContext.HASH_SHA256, key); - GD.Assert(err == OK); - PoolByteArray msg1 = String("this is ").to_utf8(); - PoolByteArray msg2 = String("vewy vew secret").to_utf8(); - err = ctx.Update(msg1); - GD.Assert(err == OK); - err = ctx.Update(msg2); - GD.Assert(err == OK); - PoolByteArray hmac = ctx.Finish(); - GD.Print(hmac.HexEncode()); - } - } - [/codeblock] - [b]Note:[/b] Not available in HTML5 exports. + public class CryptoNode : Node + { + private HMACContext ctx = new HMACContext(); + public override void _Ready() + { + PoolByteArray key = String("supersecret").to_utf8(); + Error err = ctx.Start(HashingContext.HASH_SHA256, key); + GD.Assert(err == OK); + PoolByteArray msg1 = String("this is ").to_utf8(); + PoolByteArray msg2 = String("vewy vew secret").to_utf8(); + err = ctx.Update(msg1); + GD.Assert(err == OK); + err = ctx.Update(msg2); + GD.Assert(err == OK); + PoolByteArray hmac = ctx.Finish(); + GD.Print(hmac.HexEncode()); + } + } + [/codeblock] + [b]Note:[/b] Not available in HTML5 exports. diff --git a/doc/classes/PhysicsServer.xml b/doc/classes/PhysicsServer.xml index 1c692bab65b..0f67b506efa 100644 --- a/doc/classes/PhysicsServer.xml +++ b/doc/classes/PhysicsServer.xml @@ -325,7 +325,6 @@ Returns the physics layer or layers a body can collide with. -- From 9cdaf19735f411f68d019e81d3c6a4b5dd8474cd Mon Sep 17 00:00:00 2001 From: Haoyu Qiu Date: Thu, 16 Dec 2021 00:33:23 +0800 Subject: [PATCH 08/13] Fix Color.v integer assignment (cherry picked from commit a16b06e7a444c90015e5311e7165f2f69c6298f0) --- core/variant_op.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/variant_op.cpp b/core/variant_op.cpp index 162a26bbcbf..cf4ad2613e3 100644 --- a/core/variant_op.cpp +++ b/core/variant_op.cpp @@ -1486,7 +1486,7 @@ void Variant::set_named(const StringName &p_index, const Variant &p_value, bool v->set_hsv(v->get_h(), p_value._data._int, v->get_v(), v->a); valid = true; } else if (p_index == CoreStringNames::singleton->v) { - v->set_hsv(v->get_h(), v->get_v(), p_value._data._int, v->a); + v->set_hsv(v->get_h(), v->get_s(), p_value._data._int, v->a); valid = true; } } else if (p_value.type == Variant::REAL) { From 8d13ed44fc657aebca8bc80839c75274809a259a Mon Sep 17 00:00:00 2001 From: Yuri Roubinsky Date: Thu, 16 Dec 2021 10:22:01 +0300 Subject: [PATCH 09/13] [3.x] Prevent writing incorrect shader hints (cherry picked from commit 25e24f2d05533645b58c6dce64e6533750a693c4) --- servers/visual/shader_language.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index bee16577195..4b64abc7862 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -5996,6 +5996,7 @@ Error ShaderLanguage::_parse_shader(const Map &p_funct } else { _set_error("Expected valid type hint after ':'."); + return ERR_PARSE_ERROR; } if (uniform2.hint != ShaderNode::Uniform::HINT_RANGE && uniform2.hint != ShaderNode::Uniform::HINT_NONE && uniform2.hint != ShaderNode::Uniform::HINT_COLOR && type <= TYPE_MAT4) { From f8387aafd4c331074c593d9d8b19778509dc99d8 Mon Sep 17 00:00:00 2001 From: Haoyu Qiu Date: Thu, 16 Dec 2021 16:02:29 +0800 Subject: [PATCH 10/13] Fix incompatible addition in auto exposure shader (cherry picked from commit e614a0e9fcaff50c6adc24c5668b96732bc5d89f) --- drivers/gles3/shaders/exposure.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gles3/shaders/exposure.glsl b/drivers/gles3/shaders/exposure.glsl index c20812bfa3b..dc5a64e722b 100644 --- a/drivers/gles3/shaders/exposure.glsl +++ b/drivers/gles3/shaders/exposure.glsl @@ -39,7 +39,7 @@ void main() { #if 1 //more precise and expensive, but less jittery - ivec2 next_pos = ivec2(gl_FragCoord.xy + ivec2(1)) * source_render_size / target_size; + ivec2 next_pos = (ivec2(gl_FragCoord.xy) + ivec2(1)) * source_render_size / target_size; next_pos = max(next_pos, src_pos + ivec2(1)); //so it at least reads one pixel highp vec3 source_color = vec3(0.0); for (int i = src_pos.x; i < next_pos.x; i++) { From 563de4c6f4f17dcd83ad1229e443d5b2ce66b9f3 Mon Sep 17 00:00:00 2001 From: kobewi Date: Thu, 16 Dec 2021 13:37:54 +0100 Subject: [PATCH 11/13] Mention what happens if find_node() fails (cherry picked from commit e7722a9a7b036c08ea2817cc827070c27439483e) --- doc/classes/Node.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index c68d4ad4f25..47afcbe0353 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -162,7 +162,7 @@ - Finds a descendant of this node whose name matches [code]mask[/code] as in [method String.match] (i.e. case-sensitive, but [code]"*"[/code] matches zero or more characters and [code]"?"[/code] matches any single character except [code]"."[/code]). + Finds a descendant of this node whose name matches [code]mask[/code] as in [method String.match] (i.e. case-sensitive, but [code]"*"[/code] matches zero or more characters and [code]"?"[/code] matches any single character except [code]"."[/code]). Returns [code]null[/code] if no matching [Node] is found. [b]Note:[/b] It does not match against the full path, just against individual node names. If [code]owned[/code] is [code]true[/code], this method only finds nodes whose owner is this node. This is especially important for scenes instantiated through a script, because those scenes don't have an owner. [b]Note:[/b] As this method walks through all the descendants of the node, it is the slowest way to get a reference to another node. Whenever possible, consider using [method get_node] instead. To avoid using [method find_node] too often, consider caching the node reference into a variable. From 3c752893c5ccbab8a6e87a0d47180ae1dc63c1f7 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 16 Dec 2021 09:09:06 -0800 Subject: [PATCH 12/13] Fixed event spam when using the Nintendo Switch controller There is no filtering on the Nintendo Switch Pro controller thumbstick, so there will frequently be events with very slight change. These are turned into "not pressed" events, which cancel "pressed" events from keys and buttons. This change filters out up to 5% jitter, but it might be worth revisiting whether "not pressed" events should cancel "pressed" events. (cherry picked from commit e5b0f86260063d64d6a5e446426e60aa32339485) --- main/input_default.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/main/input_default.cpp b/main/input_default.cpp index 7ca580f8ede..d543eff52dc 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -811,7 +811,10 @@ void InputDefault::joy_axis(int p_device, int p_axis, const JoyAxis &p_value) { Joypad &joy = joy_names[p_device]; - if (joy.last_axis[p_axis] == p_value.value) { + // Make sure that we don't generate events for up to 5% jitter + // This is needed for Nintendo Switch Pro controllers, which jitter at rest + const float MIN_AXIS_CHANGE = 0.05f; + if (fabs(joy.last_axis[p_axis] - p_value.value) < MIN_AXIS_CHANGE) { return; } From 5f7b91136f96823cc696dd356d6137e50cce26ba Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Wed, 15 Dec 2021 20:00:35 +0200 Subject: [PATCH 13/13] [Windows] Detect new Windows Terminal and disable unsupported set_console_visible code. (cherry picked from commit 9aef3a93dd26dd3f7e7d03283cbcf85d52f6dada) --- platform/windows/os_windows.cpp | 21 ++++++++++++++++++--- platform/windows/os_windows.h | 2 ++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 1a6c77173be..aa29387c1ee 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -2229,11 +2229,25 @@ bool OS_Windows::is_window_focused() const { return window_focused; } +bool OS_Windows::_is_win11_terminal() const { + HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD dwMode = 0; + if (GetConsoleMode(hStdOut, &dwMode)) { + return ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == ENABLE_VIRTUAL_TERMINAL_PROCESSING); + } else { + return false; + } +} + void OS_Windows::set_console_visible(bool p_enabled) { if (console_visible == p_enabled) return; - ShowWindow(GetConsoleWindow(), p_enabled ? SW_SHOW : SW_HIDE); - console_visible = p_enabled; + + if (!_is_win11_terminal()) { + // GetConsoleWindow is not supported by the Windows Terminal. + ShowWindow(GetConsoleWindow(), p_enabled ? SW_SHOW : SW_HIDE); + console_visible = p_enabled; + } } bool OS_Windows::is_console_visible() const { @@ -2840,7 +2854,8 @@ Error OS_Windows::execute(const String &p_path, const List &p_arguments, } DWORD creation_flags = NORMAL_PRIORITY_CLASS & CREATE_NO_WINDOW; - if (p_path == get_executable_path() && GetConsoleWindow() != NULL) { + if (p_path == get_executable_path() && GetConsoleWindow() != NULL && _is_win11_terminal()) { + // Open a new terminal as a workaround for Windows Terminal bug. creation_flags |= CREATE_NEW_CONSOLE; } diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index c623523d72a..106aaf3c2f6 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -371,6 +371,8 @@ class OS_Windows : public OS { CrashHandler crash_handler; + bool _is_win11_terminal() const; + void _drag_event(float p_x, float p_y, int idx); void _touch_event(bool p_pressed, float p_x, float p_y, int idx);