From e7e022b3eeb53b50e83ef792233b74ae118732d1 Mon Sep 17 00:00:00 2001 From: Michael Alexsander Date: Sat, 11 Jul 2020 12:32:27 -0300 Subject: [PATCH 01/18] Prefer the highlight version of the "GuiTabIcon" icon for buttons, and make their width/height equal (cherry picked from commit 26381265b88d02412593d5a786772544e6a1ac09) --- editor/editor_audio_buses.cpp | 4 ++-- editor/icons/icon_GUI_tab_menu.svg | 2 +- editor/icons/icon_GUI_tab_menu_hl.svg | 2 +- editor/plugins/animation_player_editor_plugin.cpp | 2 +- editor/plugins/canvas_item_editor_plugin.cpp | 4 ++-- editor/plugins/spatial_editor_plugin.cpp | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index aeb6aec44cd5..b8621b27ffbb 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -89,7 +89,7 @@ void EditorAudioBus::_notification(int p_what) { bypass->set_icon(get_icon("AudioBusBypass", "EditorIcons")); bypass->add_color_override("icon_color_pressed", bypass_color); - bus_options->set_icon(get_icon("GuiTabMenu", "EditorIcons")); + bus_options->set_icon(get_icon("GuiTabMenuHl", "EditorIcons")); update_bus(); set_process(true); @@ -181,7 +181,7 @@ void EditorAudioBus::_notification(int p_what) { mute->set_icon(get_icon("AudioBusMute", "EditorIcons")); bypass->set_icon(get_icon("AudioBusBypass", "EditorIcons")); - bus_options->set_icon(get_icon("GuiTabMenu", "EditorIcons")); + bus_options->set_icon(get_icon("GuiTabMenuHl", "EditorIcons")); } break; case NOTIFICATION_MOUSE_EXIT: case NOTIFICATION_DRAG_END: { diff --git a/editor/icons/icon_GUI_tab_menu.svg b/editor/icons/icon_GUI_tab_menu.svg index 8bf5ef2f7de2..55e98143a697 100644 --- a/editor/icons/icon_GUI_tab_menu.svg +++ b/editor/icons/icon_GUI_tab_menu.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/editor/icons/icon_GUI_tab_menu_hl.svg b/editor/icons/icon_GUI_tab_menu_hl.svg index 42d58a5abf38..e4c5b7bf1e54 100644 --- a/editor/icons/icon_GUI_tab_menu_hl.svg +++ b/editor/icons/icon_GUI_tab_menu_hl.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 994bc8bb6fd5..3c8a82647609 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -126,7 +126,7 @@ void AnimationPlayerEditor::_notification(int p_what) { stop->set_icon(get_icon("Stop", "EditorIcons")); onion_toggle->set_icon(get_icon("Onion", "EditorIcons")); - onion_skinning->set_icon(get_icon("GuiTabMenu", "EditorIcons")); + onion_skinning->set_icon(get_icon("GuiTabMenuHl", "EditorIcons")); pin->set_icon(get_icon("Pin", "EditorIcons")); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index bebf46f3127c..95453fa5f692 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -3861,7 +3861,7 @@ void CanvasItemEditor::_notification(int p_what) { rotate_button->set_icon(get_icon("ToolRotate", "EditorIcons")); smart_snap_button->set_icon(get_icon("Snap", "EditorIcons")); grid_snap_button->set_icon(get_icon("SnapGrid", "EditorIcons")); - snap_config_menu->set_icon(get_icon("GuiTabMenu", "EditorIcons")); + snap_config_menu->set_icon(get_icon("GuiTabMenuHl", "EditorIcons")); skeleton_menu->set_icon(get_icon("Bone", "EditorIcons")); override_camera_button->set_icon(get_icon("Camera2D", "EditorIcons")); pan_button->set_icon(get_icon("ToolPan", "EditorIcons")); @@ -3878,7 +3878,7 @@ void CanvasItemEditor::_notification(int p_what) { key_scale_button->set_icon(get_icon("KeyScale", "EditorIcons")); key_insert_button->set_icon(get_icon("Key", "EditorIcons")); key_auto_insert_button->set_icon(get_icon("AutoKey", "EditorIcons")); - animation_menu->set_icon(get_icon("GuiTabMenu", "EditorIcons")); + animation_menu->set_icon(get_icon("GuiTabMenuHl", "EditorIcons")); zoom_minus->set_icon(get_icon("ZoomLess", "EditorIcons")); zoom_plus->set_icon(get_icon("ZoomMore", "EditorIcons")); diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 953a8b953f4a..7f5ebb262d2d 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -2542,7 +2542,7 @@ void SpatialEditorViewport::_notification(int p_what) { if (p_what == NOTIFICATION_THEME_CHANGED) { - view_menu->set_icon(get_icon("GuiTabMenu", "EditorIcons")); + view_menu->set_icon(get_icon("GuiTabMenuHl", "EditorIcons")); preview_camera->set_icon(get_icon("Camera", "EditorIcons")); view_menu->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); From f6479786c07b5515bce179af718f96d7365c312c Mon Sep 17 00:00:00 2001 From: Jitesh Date: Sat, 11 Jul 2020 12:21:45 -0700 Subject: [PATCH 02/18] change minimum horizontal size from 200 to 240 (cherry picked from commit a8905b2a4e8e1364521948c2d3aa734ead5f715d) --- editor/plugins/canvas_item_editor_plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 95453fa5f692..20bf32abe35e 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -6262,7 +6262,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte selector->add_child(vbc); vbc->set_h_size_flags(SIZE_EXPAND_FILL); vbc->set_v_size_flags(SIZE_EXPAND_FILL); - vbc->set_custom_minimum_size(Size2(200, 260) * EDSCALE); + vbc->set_custom_minimum_size(Size2(240, 260) * EDSCALE); btn_group = memnew(VBoxContainer); vbc->add_child(btn_group); From 8218170b3cd0466028cb90de950c44155782c959 Mon Sep 17 00:00:00 2001 From: Patrick Dawson Date: Mon, 13 Jul 2020 01:39:39 +0200 Subject: [PATCH 03/18] Avoid overflow when calculating visible_cells (cherry picked from commit 9e28df22a0195340016f3ec7271a0bd738c0e928) --- scene/resources/world_2d.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 13d5c9699f39..0323be62caba 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -228,7 +228,7 @@ struct SpatialIndexer2D { List added; List removed; - int visible_cells = (end.x - begin.x) * (end.y - begin.y); + uint64_t visible_cells = (uint64_t)(end.x - begin.x) * (uint64_t)(end.y - begin.y); if (visible_cells > 10000) { From cfda32add037a5f4f28150d73f69a2b178db3066 Mon Sep 17 00:00:00 2001 From: volzhs Date: Tue, 14 Jul 2020 02:34:18 +0900 Subject: [PATCH 04/18] Fix overlappingObjects vector crash use clear_overlaps() instead of clearing overlappingObjects directly (cherry picked from commit 4e987f5ab91ee0455858cf29ecebf1df050e565f) --- modules/bullet/area_bullet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp index 4f332b7f5052..d327eca5dec6 100644 --- a/modules/bullet/area_bullet.cpp +++ b/modules/bullet/area_bullet.cpp @@ -181,7 +181,7 @@ void AreaBullet::reload_body() { void AreaBullet::set_space(SpaceBullet *p_space) { // Clear the old space if there is one if (space) { - overlappingObjects.clear(); + clear_overlaps(false); isScratched = false; // Remove this object form the physics world From a8188265c247996c6cd93274304fe085502dd985 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Sun, 12 Jul 2020 14:22:06 +0200 Subject: [PATCH 05/18] Disable file logging for the project manager Due to `user://` returning the current working directory when no project is open, this caused logs to be written to `$HOME` most of the time. This closes #40305. (cherry picked from commit b89dc6ae0051e04f710b66e94454720f321fb14e) --- main/main.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/main/main.cpp b/main/main.cpp index 7e3806a9df8e..551b5ccf5d9c 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -954,21 +954,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph #endif - GLOBAL_DEF("logging/file_logging/enable_file_logging", false); - // Only file logging by default on desktop platforms as logs can't be - // accessed easily on mobile/Web platforms (if at all). - // This also prevents logs from being created for the editor instance, as feature tags - // are disabled while in the editor (even if they should logically apply). - GLOBAL_DEF("logging/file_logging/enable_file_logging.pc", true); - GLOBAL_DEF("logging/file_logging/log_path", "user://logs/godot.log"); - GLOBAL_DEF("logging/file_logging/max_log_files", 5); - ProjectSettings::get_singleton()->set_custom_property_info("logging/file_logging/max_log_files", PropertyInfo(Variant::INT, "logging/file_logging/max_log_files", PROPERTY_HINT_RANGE, "0,20,1,or_greater")); //no negative numbers - if (FileAccess::get_create_func(FileAccess::ACCESS_USERDATA) && GLOBAL_GET("logging/file_logging/enable_file_logging")) { - String base_path = GLOBAL_GET("logging/file_logging/log_path"); - int max_files = GLOBAL_GET("logging/file_logging/max_log_files"); - OS::get_singleton()->add_logger(memnew(RotatedFileLogger(base_path, max_files))); - } - #ifdef TOOLS_ENABLED if (editor) { Engine::get_singleton()->set_editor_hint(true); @@ -985,6 +970,23 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } #endif + GLOBAL_DEF("logging/file_logging/enable_file_logging", false); + // Only file logging by default on desktop platforms as logs can't be + // accessed easily on mobile/Web platforms (if at all). + // This also prevents logs from being created for the editor instance, as feature tags + // are disabled while in the editor (even if they should logically apply). + GLOBAL_DEF("logging/file_logging/enable_file_logging.pc", true); + GLOBAL_DEF("logging/file_logging/log_path", "user://logs/godot.log"); + GLOBAL_DEF("logging/file_logging/max_log_files", 5); + ProjectSettings::get_singleton()->set_custom_property_info("logging/file_logging/max_log_files", PropertyInfo(Variant::INT, "logging/file_logging/max_log_files", PROPERTY_HINT_RANGE, "0,20,1,or_greater")); //no negative numbers + if (!project_manager && !editor && FileAccess::get_create_func(FileAccess::ACCESS_USERDATA) && GLOBAL_GET("logging/file_logging/enable_file_logging")) { + // Don't create logs for the project manager as they would be written to + // the current working directory, which is inconvenient. + String base_path = GLOBAL_GET("logging/file_logging/log_path"); + int max_files = GLOBAL_GET("logging/file_logging/max_log_files"); + OS::get_singleton()->add_logger(memnew(RotatedFileLogger(base_path, max_files))); + } + if (main_args.size() == 0 && String(GLOBAL_DEF("application/run/main_scene", "")) == "") { #ifdef TOOLS_ENABLED if (!editor && !project_manager) { From f7b994aef8e359d83d1c1e5a302d82b39d7256b6 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Tue, 14 Jul 2020 06:43:58 -0400 Subject: [PATCH 06/18] Revert "Include gdscript warning name in the warning message." This reverts commit de3ad3b30ecb8de1aa112df7d61630102f077b5b. (cherry picked from commit d92fa3b547c93a32425d207ec491361d3cd7ec12) --- modules/gdscript/gdscript.cpp | 62 +++++++++++++++++------------------ 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 267e559b7ad9..7f89f2037c4d 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2020,116 +2020,114 @@ String GDScriptWarning::get_message() const { #define CHECK_SYMBOLS(m_amount) ERR_FAIL_COND_V(symbols.size() < m_amount, String()); - String msg; - switch (code) { case UNASSIGNED_VARIABLE_OP_ASSIGN: { CHECK_SYMBOLS(1); - msg = "Using assignment with operation but the variable '" + symbols[0] + "' was not previously assigned a value."; + return "Using assignment with operation but the variable '" + symbols[0] + "' was not previously assigned a value."; } break; case UNASSIGNED_VARIABLE: { CHECK_SYMBOLS(1); - msg = "The variable '" + symbols[0] + "' was used but never assigned a value."; + return "The variable '" + symbols[0] + "' was used but never assigned a value."; } break; case UNUSED_VARIABLE: { CHECK_SYMBOLS(1); - msg = "The local variable '" + symbols[0] + "' is declared but never used in the block. If this is intended, prefix it with an underscore: '_" + symbols[0] + "'"; + return "The local variable '" + symbols[0] + "' is declared but never used in the block. If this is intended, prefix it with an underscore: '_" + symbols[0] + "'"; } break; case SHADOWED_VARIABLE: { CHECK_SYMBOLS(2); - msg = "The local variable '" + symbols[0] + "' is shadowing an already-defined variable at line " + symbols[1] + "."; + return "The local variable '" + symbols[0] + "' is shadowing an already-defined variable at line " + symbols[1] + "."; } break; case UNUSED_CLASS_VARIABLE: { CHECK_SYMBOLS(1); - msg = "The class variable '" + symbols[0] + "' is declared but never used in the script."; + return "The class variable '" + symbols[0] + "' is declared but never used in the script."; } break; case UNUSED_ARGUMENT: { CHECK_SYMBOLS(2); - msg = "The argument '" + symbols[1] + "' is never used in the function '" + symbols[0] + "'. If this is intended, prefix it with an underscore: '_" + symbols[1] + "'"; + return "The argument '" + symbols[1] + "' is never used in the function '" + symbols[0] + "'. If this is intended, prefix it with an underscore: '_" + symbols[1] + "'"; } break; case UNREACHABLE_CODE: { CHECK_SYMBOLS(1); - msg = "Unreachable code (statement after return) in function '" + symbols[0] + "()'."; + return "Unreachable code (statement after return) in function '" + symbols[0] + "()'."; } break; case STANDALONE_EXPRESSION: { - msg = "Standalone expression (the line has no effect)."; + return "Standalone expression (the line has no effect)."; } break; case VOID_ASSIGNMENT: { CHECK_SYMBOLS(1); - msg = "Assignment operation, but the function '" + symbols[0] + "()' returns void."; + return "Assignment operation, but the function '" + symbols[0] + "()' returns void."; } break; case NARROWING_CONVERSION: { - msg = "Narrowing conversion (float is converted to int and loses precision)."; + return "Narrowing conversion (float is converted to int and loses precision)."; } break; case FUNCTION_MAY_YIELD: { CHECK_SYMBOLS(1); - msg = "Assigned variable is typed but the function '" + symbols[0] + "()' may yield and return a GDScriptFunctionState instead."; + return "Assigned variable is typed but the function '" + symbols[0] + "()' may yield and return a GDScriptFunctionState instead."; } break; case VARIABLE_CONFLICTS_FUNCTION: { CHECK_SYMBOLS(1); - msg = "Variable declaration of '" + symbols[0] + "' conflicts with a function of the same name."; + return "Variable declaration of '" + symbols[0] + "' conflicts with a function of the same name."; } break; case FUNCTION_CONFLICTS_VARIABLE: { CHECK_SYMBOLS(1); - msg = "Function declaration of '" + symbols[0] + "()' conflicts with a variable of the same name."; + return "Function declaration of '" + symbols[0] + "()' conflicts with a variable of the same name."; } break; case FUNCTION_CONFLICTS_CONSTANT: { CHECK_SYMBOLS(1); - msg = "Function declaration of '" + symbols[0] + "()' conflicts with a constant of the same name."; + return "Function declaration of '" + symbols[0] + "()' conflicts with a constant of the same name."; } break; case INCOMPATIBLE_TERNARY: { - msg = "Values of the ternary conditional are not mutually compatible."; + return "Values of the ternary conditional are not mutually compatible."; } break; case UNUSED_SIGNAL: { CHECK_SYMBOLS(1); - msg = "The signal '" + symbols[0] + "' is declared but never emitted."; + return "The signal '" + symbols[0] + "' is declared but never emitted."; } break; case RETURN_VALUE_DISCARDED: { CHECK_SYMBOLS(1); - msg = "The function '" + symbols[0] + "()' returns a value, but this value is never used."; + return "The function '" + symbols[0] + "()' returns a value, but this value is never used."; } break; case PROPERTY_USED_AS_FUNCTION: { CHECK_SYMBOLS(2); - msg = "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a property with the same name. Did you mean to access it?"; + return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a property with the same name. Did you mean to access it?"; } break; case CONSTANT_USED_AS_FUNCTION: { CHECK_SYMBOLS(2); - msg = "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a constant with the same name. Did you mean to access it?"; + return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a constant with the same name. Did you mean to access it?"; } break; case FUNCTION_USED_AS_PROPERTY: { CHECK_SYMBOLS(2); - msg = "The property '" + symbols[0] + "' was not found in base '" + symbols[1] + "' but there's a method with the same name. Did you mean to call it?"; + return "The property '" + symbols[0] + "' was not found in base '" + symbols[1] + "' but there's a method with the same name. Did you mean to call it?"; } break; case INTEGER_DIVISION: { - msg = "Integer division, decimal part will be discarded."; + return "Integer division, decimal part will be discarded."; } break; case UNSAFE_PROPERTY_ACCESS: { CHECK_SYMBOLS(2); - msg = "The property '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype)."; + return "The property '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype)."; } break; case UNSAFE_METHOD_ACCESS: { CHECK_SYMBOLS(2); - msg = "The method '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype)."; + return "The method '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype)."; } break; case UNSAFE_CAST: { CHECK_SYMBOLS(1); - msg = "The value is cast to '" + symbols[0] + "' but has an unknown type."; + return "The value is cast to '" + symbols[0] + "' but has an unknown type."; } break; case UNSAFE_CALL_ARGUMENT: { CHECK_SYMBOLS(4); - msg = "The argument '" + symbols[0] + "' of the function '" + symbols[1] + "' requires a the subtype '" + symbols[2] + "' but the supertype '" + symbols[3] + "' was provided"; + return "The argument '" + symbols[0] + "' of the function '" + symbols[1] + "' requires a the subtype '" + symbols[2] + "' but the supertype '" + symbols[3] + "' was provided"; } break; case DEPRECATED_KEYWORD: { CHECK_SYMBOLS(2); - msg = "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'."; + return "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'."; } break; case STANDALONE_TERNARY: { - msg = "Standalone ternary conditional operator: the return value is being discarded."; - } break; + return "Standalone ternary conditional operator: the return value is being discarded."; + } case WARNING_MAX: - ERR_FAIL_V_MSG(String(), "Invalid GDScript warning code: " + get_name_from_code(code) + "."); + break; // Can't happen, but silences warning } - return msg + " [" + get_name() + "]"; + ERR_FAIL_V_MSG(String(), "Invalid GDScript warning code: " + get_name_from_code(code) + "."); #undef CHECK_SYMBOLS } From 4677502d7c93fd800d45f9e7e78714a1bf73dd04 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Tue, 14 Jul 2020 07:11:16 -0400 Subject: [PATCH 07/18] Include gdscript warning name in LSP message. My initial attempt changed this in the gdscript code, which resulted in a duplicate warning name in the builtin editor. We should just append the warning name in the LSP instead. This uses parens to match what is shown in the builtin editor. (cherry picked from commit 8dcc39ec91babb282bff69c80bb7f2bde657677f) --- modules/gdscript/language_server/gdscript_extend_parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 121ccbd10226..dc449b6ab231 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -63,7 +63,7 @@ void ExtendGDScriptParser::update_diagnostics() { const GDScriptWarning &warning = E->get(); lsp::Diagnostic diagnostic; diagnostic.severity = lsp::DiagnosticSeverity::Warning; - diagnostic.message = warning.get_message(); + diagnostic.message = "(" + warning.get_name() + "): " + warning.get_message(); diagnostic.source = "gdscript"; diagnostic.code = warning.code; lsp::Range range; From a06ee5e763810ec58ec31d77ac90585dda7adca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Verschelde?= Date: Tue, 14 Jul 2020 13:16:49 +0200 Subject: [PATCH 08/18] PO loader: Fix unclosed files and error messages Fixes #40324. (cherry picked from commit 47cc2029721735be841291d81bef4d0aea634055) Also removes empty `p_path` as done in 4857648a16585bbd0fb2fbc33d3d0f768b8223b5. --- core/io/translation_loader_po.cpp | 27 ++++++++++++++------------- core/io/translation_loader_po.h | 2 +- editor/editor_settings.cpp | 2 +- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 4f7eeddc437c..aebf5f204da0 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -33,10 +33,8 @@ #include "core/os/file_access.h" #include "core/translation.h" -RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const String &p_path) { - +RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { enum Status { - STATUS_NONE, STATUS_READING_ID, STATUS_READING_STRING, @@ -56,6 +54,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S bool skip_this = false; bool skip_next = false; bool is_eof = false; + const String path = f->get_path(); while (!is_eof) { @@ -67,7 +66,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S if (status == STATUS_READING_ID) { memdelete(f); - ERR_FAIL_V_MSG(RES(), p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); + ERR_FAIL_V_MSG(RES(), "Unexpected EOF while reading 'msgid' at: " + path + ":" + itos(line)); } else { break; } @@ -76,9 +75,8 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S if (l.begins_with("msgid")) { if (status == STATUS_READING_ID) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), p_path + ":" + itos(line) + " Unexpected 'msgid', was expecting 'msgstr' while parsing: "); + ERR_FAIL_V_MSG(RES(), "Unexpected 'msgid', was expecting 'msgstr' while parsing: " + path + ":" + itos(line)); } if (msg_id != "") { @@ -98,9 +96,8 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S if (l.begins_with("msgstr")) { if (status != STATUS_READING_ID) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), p_path + ":" + itos(line) + " Unexpected 'msgstr', was expecting 'msgid' while parsing: "); + ERR_FAIL_V_MSG(RES(), "Unexpected 'msgstr', was expecting 'msgid' while parsing: " + path + ":" + itos(line)); } l = l.substr(6, l.length()).strip_edges(); @@ -115,7 +112,10 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S continue; //nothing to read or comment } - ERR_FAIL_COND_V_MSG(!l.begins_with("\"") || status == STATUS_NONE, RES(), p_path + ":" + itos(line) + " Invalid line '" + l + "' while parsing: "); + if (!l.begins_with("\"") || status == STATUS_NONE) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Invalid line '" + l + "' while parsing: " + path + ":" + itos(line)); + } l = l.substr(1, l.length()); //find final quote @@ -128,7 +128,10 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S } } - ERR_FAIL_COND_V_MSG(end_pos == -1, RES(), p_path + ":" + itos(line) + " Expected '\"' at end of message while parsing file: "); + if (end_pos == -1) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Expected '\"' at end of message while parsing: " + path + ":" + itos(line)); + } l = l.substr(0, end_pos); l = l.c_unescape(); @@ -141,7 +144,6 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S line++; } - f->close(); memdelete(f); if (status == STATUS_READING_STRING) { @@ -153,7 +155,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S config = msg_str; } - ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + p_path + "."); + ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + path + "."); Vector configs = config.split("\n"); for (int i = 0; i < configs.size(); i++) { @@ -190,7 +192,6 @@ RES TranslationLoaderPO::load(const String &p_path, const String &p_original_pat void TranslationLoaderPO::get_recognized_extensions(List *p_extensions) const { p_extensions->push_back("po"); - //p_extensions->push_back("mo"); //mo in the future... } bool TranslationLoaderPO::handles_type(const String &p_type) const { diff --git a/core/io/translation_loader_po.h b/core/io/translation_loader_po.h index 47e64276cac3..13e07a6cf06d 100644 --- a/core/io/translation_loader_po.h +++ b/core/io/translation_loader_po.h @@ -37,7 +37,7 @@ class TranslationLoaderPO : public ResourceFormatLoader { public: - static RES load_translation(FileAccess *f, Error *r_error, const String &p_path = String()); + static RES load_translation(FileAccess *f, Error *r_error); virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions(List *p_extensions) const; virtual bool handles_type(const String &p_type) const; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 4649bf0d1895..0a15aabe80b0 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -997,7 +997,7 @@ void EditorSettings::setup_language() { FileAccessMemory *fa = memnew(FileAccessMemory); fa->open_custom(data.ptr(), data.size()); - Ref tr = TranslationLoaderPO::load_translation(fa, NULL, "translation_" + String(etl->lang)); + Ref tr = TranslationLoaderPO::load_translation(fa, NULL); if (tr.is_valid()) { tr->set_locale(etl->lang); From 468a1c0271820feb194e0e8899719080c6b2ce9c Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Thu, 25 Jun 2020 15:32:50 +0200 Subject: [PATCH 09/18] UDPServer handles PacketPeerUDP-client association UDPServer now uses a single socket which is shared with the PacketPeerUDP it creates and has a new `poll` function to read incoming packets on that socket and delivers them to the appropriate peer. PacketPeerUDP created this way never reads from the socket, but are allowed to write on it using sendto. This is needed because Windows (unlike Linux/BSD) does not support packet routing when multiple sockets are bound on the same address/port. (cherry picked from commit 147bbe215509b6875fa226286a4d3a8144e55d31) --- core/io/packet_peer_udp.cpp | 73 +++++++++++++++----------- core/io/packet_peer_udp.h | 7 ++- core/io/udp_server.cpp | 100 +++++++++++++++++++++++++++++++++--- core/io/udp_server.h | 29 ++++++++++- 4 files changed, 170 insertions(+), 39 deletions(-) diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp index 2d05e5bf645d..5a6c867f13da 100644 --- a/core/io/packet_peer_udp.cpp +++ b/core/io/packet_peer_udp.cpp @@ -31,6 +31,7 @@ #include "packet_peer_udp.h" #include "core/io/ip.h" +#include "core/io/udp_server.h" void PacketPeerUDP::set_blocking_mode(bool p_enable) { @@ -38,13 +39,14 @@ void PacketPeerUDP::set_blocking_mode(bool p_enable) { } void PacketPeerUDP::set_broadcast_enabled(bool p_enabled) { + ERR_FAIL_COND(udp_server); broadcast = p_enabled; if (_sock.is_valid() && _sock->is_open()) _sock->set_broadcasting_enabled(p_enabled); } Error PacketPeerUDP::join_multicast_group(IP_Address p_multi_address, String p_if_name) { - + ERR_FAIL_COND_V(udp_server, ERR_LOCKED); ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(!p_multi_address.is_valid(), ERR_INVALID_PARAMETER); @@ -59,7 +61,7 @@ Error PacketPeerUDP::join_multicast_group(IP_Address p_multi_address, String p_i } Error PacketPeerUDP::leave_multicast_group(IP_Address p_multi_address, String p_if_name) { - + ERR_FAIL_COND_V(udp_server, ERR_LOCKED); ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(!_sock->is_open(), ERR_UNCONFIGURED); return _sock->leave_multicast_group(p_multi_address, p_if_name); @@ -133,7 +135,7 @@ Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) { } do { - if (connected) { + if (connected && !udp_server) { err = _sock->send(p_buffer, p_buffer_size, sent); } else { err = _sock->sendto(p_buffer, p_buffer_size, sent, peer_addr, peer_port); @@ -188,26 +190,25 @@ Error PacketPeerUDP::listen(int p_port, const IP_Address &p_bind_address, int p_ return OK; } -Error PacketPeerUDP::connect_socket(Ref p_sock) { - Error err; - int read = 0; - uint16_t r_port; - IP_Address r_ip; - - err = p_sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, r_ip, r_port, true); - ERR_FAIL_COND_V(err != OK, err); - err = p_sock->connect_to_host(r_ip, r_port); - ERR_FAIL_COND_V(err != OK, err); +Error PacketPeerUDP::connect_shared_socket(Ref p_sock, IP_Address p_ip, uint16_t p_port, UDPServer *p_server) { + udp_server = p_server; + connected = true; _sock = p_sock; - peer_addr = r_ip; - peer_port = r_port; + peer_addr = p_ip; + peer_port = p_port; packet_ip = peer_addr; packet_port = peer_port; - connected = true; return OK; } +void PacketPeerUDP::disconnect_shared_socket() { + udp_server = nullptr; + _sock = Ref(NetSocket::create()); + close(); +} + Error PacketPeerUDP::connect_to_host(const IP_Address &p_host, int p_port) { + ERR_FAIL_COND_V(udp_server, ERR_LOCKED); ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER); @@ -245,9 +246,13 @@ bool PacketPeerUDP::is_connected_to_host() const { } void PacketPeerUDP::close() { - - if (_sock.is_valid()) + if (udp_server) { + udp_server->remove_peer(peer_addr, peer_port); + udp_server = nullptr; + _sock = Ref(NetSocket::create()); + } else if (_sock.is_valid()) { _sock->close(); + } rb.resize(16); queue_count = 0; connected = false; @@ -266,6 +271,9 @@ Error PacketPeerUDP::_poll() { if (!_sock->is_open()) { return FAILED; } + if (udp_server) { + return OK; // Handled by UDPServer. + } Error err; int read; @@ -287,23 +295,29 @@ Error PacketPeerUDP::_poll() { return FAILED; } - if (rb.space_left() < read + 24) { + err = store_packet(ip, port, recv_buffer, read); #ifdef TOOLS_ENABLED - WARN_PRINTS("Buffer full, dropping packets!"); -#endif - continue; + if (err != OK) { + WARN_PRINT("Buffer full, dropping packets!"); } - - uint32_t port32 = port; - rb.write(ip.get_ipv6(), 16); - rb.write((uint8_t *)&port32, 4); - rb.write((uint8_t *)&read, 4); - rb.write(recv_buffer, read); - ++queue_count; +#endif } return OK; } + +Error PacketPeerUDP::store_packet(IP_Address p_ip, uint32_t p_port, uint8_t *p_buf, int p_buf_size) { + if (rb.space_left() < p_buf_size + 24) { + return ERR_OUT_OF_MEMORY; + } + rb.write(p_ip.get_ipv6(), 16); + rb.write((uint8_t *)&p_port, 4); + rb.write((uint8_t *)&p_buf_size, 4); + rb.write(p_buf, p_buf_size); + ++queue_count; + return OK; +} + bool PacketPeerUDP::is_listening() const { return _sock.is_valid() && _sock->is_open(); @@ -349,6 +363,7 @@ PacketPeerUDP::PacketPeerUDP() : connected(false), blocking(true), broadcast(false), + udp_server(nullptr), _sock(Ref(NetSocket::create())) { rb.resize(16); } diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h index b5a9fc9ec3c1..249f84fca7ca 100644 --- a/core/io/packet_peer_udp.h +++ b/core/io/packet_peer_udp.h @@ -35,6 +35,8 @@ #include "core/io/net_socket.h" #include "core/io/packet_peer.h" +class UDPServer; + class PacketPeerUDP : public PacketPeer { GDCLASS(PacketPeerUDP, PacketPeer); @@ -55,6 +57,7 @@ class PacketPeerUDP : public PacketPeer { bool connected; bool blocking; bool broadcast; + UDPServer *udp_server; Ref _sock; static void _bind_methods(); @@ -72,7 +75,9 @@ class PacketPeerUDP : public PacketPeer { Error wait(); bool is_listening() const; - Error connect_socket(Ref p_sock); // Used by UDPServer + Error connect_shared_socket(Ref p_sock, IP_Address p_ip, uint16_t p_port, UDPServer *ref); // Used by UDPServer + void disconnect_shared_socket(); // Used by UDPServer + Error store_packet(IP_Address p_ip, uint32_t p_port, uint8_t *p_buf, int p_buf_size); // Used internally and by UDPServer Error connect_to_host(const IP_Address &p_host, int p_port); bool is_connected_to_host() const; diff --git a/core/io/udp_server.cpp b/core/io/udp_server.cpp index 16b7863cddb3..485bb19f1e1d 100644 --- a/core/io/udp_server.cpp +++ b/core/io/udp_server.cpp @@ -33,10 +33,58 @@ void UDPServer::_bind_methods() { ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &UDPServer::listen, DEFVAL("*")); + ClassDB::bind_method(D_METHOD("poll"), &UDPServer::poll); ClassDB::bind_method(D_METHOD("is_connection_available"), &UDPServer::is_connection_available); ClassDB::bind_method(D_METHOD("is_listening"), &UDPServer::is_listening); ClassDB::bind_method(D_METHOD("take_connection"), &UDPServer::take_connection); ClassDB::bind_method(D_METHOD("stop"), &UDPServer::stop); + ClassDB::bind_method(D_METHOD("set_max_pending_connections", "max_pending_connections"), &UDPServer::set_max_pending_connections); + ClassDB::bind_method(D_METHOD("get_max_pending_connections"), &UDPServer::get_max_pending_connections); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_pending_connections", PROPERTY_HINT_RANGE, "0,256,1"), "set_max_pending_connections", "get_max_pending_connections"); +} + +Error UDPServer::poll() { + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + if (!_sock->is_open()) { + return ERR_UNCONFIGURED; + } + Error err; + int read; + IP_Address ip; + uint16_t port; + while (true) { + err = _sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, ip, port); + if (err != OK) { + if (err == ERR_BUSY) { + break; + } + return FAILED; + } + Peer p; + p.ip = ip; + p.port = port; + List::Element *E = peers.find(p); + if (!E) { + E = pending.find(p); + } + if (E) { + E->get().peer->store_packet(ip, port, recv_buffer, read); + } else { + if (pending.size() >= max_pending_connections) { + // Drop connection. + continue; + } + // It's a new peer, add it to the pending list. + Peer peer; + peer.ip = ip; + peer.port = port; + peer.peer = memnew(PacketPeerUDP); + peer.peer->connect_shared_socket(_sock, ip, port, this); + peer.peer->store_packet(ip, port, recv_buffer, read); + pending.push_back(peer); + } + } + return OK; } Error UDPServer::listen(uint16_t p_port, const IP_Address &p_bind_address) { @@ -82,8 +130,24 @@ bool UDPServer::is_connection_available() const { if (!_sock->is_open()) return false; - Error err = _sock->poll(NetSocket::POLL_TYPE_IN, 0); - return (err == OK); + return pending.size() > 0; +} + +void UDPServer::set_max_pending_connections(int p_max) { + ERR_FAIL_COND_MSG(p_max < 0, "Max pending connections value must be a positive number (0 means refuse new connections)."); + max_pending_connections = p_max; + while (p_max > pending.size()) { + List::Element *E = pending.back(); + if (!E) { + break; + } + memdelete(E->get().peer); + pending.erase(E); + } +} + +int UDPServer::get_max_pending_connections() const { + return max_pending_connections; } Ref UDPServer::take_connection() { @@ -93,11 +157,20 @@ Ref UDPServer::take_connection() { return conn; } - conn = Ref(memnew(PacketPeerUDP)); - conn->connect_socket(_sock); - _sock = Ref(NetSocket::create()); - listen(bind_port, bind_address); - return conn; + Peer peer = pending[0]; + pending.pop_front(); + peers.push_back(peer); + return peer.peer; +} + +void UDPServer::remove_peer(IP_Address p_ip, int p_port) { + Peer peer; + peer.ip = p_ip; + peer.port = p_port; + List::Element *E = peers.find(peer); + if (E) { + peers.erase(E); + } } void UDPServer::stop() { @@ -107,6 +180,19 @@ void UDPServer::stop() { } bind_port = 0; bind_address = IP_Address(); + List::Element *E = peers.front(); + while (E) { + E->get().peer->disconnect_shared_socket(); + E = E->next(); + } + E = pending.front(); + while (E) { + E->get().peer->disconnect_shared_socket(); + memdelete(E->get().peer); + E = E->next(); + } + peers.clear(); + pending.clear(); } UDPServer::UDPServer() : diff --git a/core/io/udp_server.h b/core/io/udp_server.h index 90bb82b62b6e..3175b09b1952 100644 --- a/core/io/udp_server.h +++ b/core/io/udp_server.h @@ -38,15 +38,40 @@ class UDPServer : public Reference { GDCLASS(UDPServer, Reference); protected: - static void _bind_methods(); - int bind_port; + enum { + PACKET_BUFFER_SIZE = 65536 + }; + + struct Peer { + PacketPeerUDP *peer; + IP_Address ip; + uint16_t port = 0; + + bool operator==(const Peer &p_other) const { + return (ip == p_other.ip && port == p_other.port); + } + }; + uint8_t recv_buffer[PACKET_BUFFER_SIZE]; + + int bind_port = 0; IP_Address bind_address; + + List peers; + List pending; + int max_pending_connections = 16; + Ref _sock; + static void _bind_methods(); + public: + void remove_peer(IP_Address p_ip, int p_port); Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")); + Error poll(); bool is_listening() const; bool is_connection_available() const; + void set_max_pending_connections(int p_max); + int get_max_pending_connections() const; Ref take_connection(); void stop(); From 91b2d020a8139f1eef7dc754b7dcb601eabca959 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Mon, 13 Jul 2020 15:45:08 +0200 Subject: [PATCH 10/18] Document updated UDPServer interface. (cherry picked from commit 839c7b1ba3e57008ebaca39f6c472c2b281129ec) --- doc/classes/UDPServer.xml | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/doc/classes/UDPServer.xml b/doc/classes/UDPServer.xml index 71a9422689d2..844c984ed487 100644 --- a/doc/classes/UDPServer.xml +++ b/doc/classes/UDPServer.xml @@ -5,6 +5,7 @@ A simple server that opens a UDP socket and returns connected [PacketPeerUDP] upon receiving new packets. See also [method PacketPeerUDP.connect_to_host]. + After starting the server ([method listen]), you will need to [method poll] it at regular intervals (e.g. inside [method Node._process]) for it to process new packets, delivering them to the appropriate [PacketPeerUDP], and taking new connections. Below a small example of how it can be used: [codeblock] # server.gd @@ -17,6 +18,7 @@ server.listen(4242) func _process(delta): + server.poll() # Important! if server.is_connection_available(): var peer : PacketPeerUDP = server.take_connection() var pkt = peer.get_packet() @@ -57,7 +59,7 @@ - Returns [code]true[/code] if a packet with a new address/port combination is received on the socket. + Returns [code]true[/code] if a packet with a new address/port combination was received on the socket. @@ -78,21 +80,33 @@ Starts the server by opening a UDP socket listening on the given port. You can optionally specify a [code]bind_address[/code] to only listen for packets sent to that address. See also [method PacketPeerUDP.listen]. + + + + + Call this method at regular intervals (e.g. inside [method Node._process]) to process new packets. And packet from known address/port pair will be delivered to the appropriate [PacketPeerUDP], any packet received from an unknown address/port pair will be added as a pending connection (see [method is_connection_available], [method take_connection]). The maximum number of pending connection is defined via [member max_pending_connections]. + + - Stops the server, closing the UDP socket if open. Will not disconnect any connected [PacketPeerUDP]. + Stops the server, closing the UDP socket if open. Will close all connected [PacketPeerUDP] accepted via [method take_connection] (remote peers will not be notified). - Returns a [PacketPeerUDP] connected to the address/port combination of the first packet in queue. Will return [code]null[/code] if no packet is in queue. See also [method PacketPeerUDP.connect_to_host]. + Returns the first pending connection (connected to the appropriate address/port). Will return [code]null[/code] if no new connection is available. See also [method is_connection_available], [method PacketPeerUDP.connect_to_host]. + + + Define the maximum number of pending connections, during [method poll], any new pending connection exceeding that value will be automatically dropped. Setting this value to [code]0[/code] effectively prevents any new pending connection to be accepted (e.g. when all your players have connected). + + From 18eddfc98d2d8a5f6c850bebdb08a983f3936a9a Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Mon, 13 Jul 2020 19:14:27 +0200 Subject: [PATCH 11/18] Funnel refuse_new_connections to Godot ENet. (cherry picked from commit 7ec5c917d143783c04a2816d3da7ebc48d47816b) --- modules/enet/networked_multiplayer_enet.cpp | 3 +++ thirdparty/enet/enet/enet.h | 1 + thirdparty/enet/godot.cpp | 11 +++++++++++ 3 files changed, 15 insertions(+) diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 406eb467f036..59b4c0b99fbd 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -668,6 +668,9 @@ int NetworkedMultiplayerENet::get_unique_id() const { void NetworkedMultiplayerENet::set_refuse_new_connections(bool p_enable) { refuse_connections = p_enable; +#ifdef GODOT_ENET + enet_host_refuse_new_connections(host, p_enable); +#endif } bool NetworkedMultiplayerENet::is_refusing_new_connections() const { diff --git a/thirdparty/enet/enet/enet.h b/thirdparty/enet/enet/enet.h index 3900353c345a..d3b3b54b9d10 100644 --- a/thirdparty/enet/enet/enet.h +++ b/thirdparty/enet/enet/enet.h @@ -587,6 +587,7 @@ extern void enet_host_bandwidth_throttle (ENetHost *); extern enet_uint32 enet_host_random_seed (void); ENET_API void enet_host_dtls_server_setup (ENetHost *, void *, void *); ENET_API void enet_host_dtls_client_setup (ENetHost *, void *, uint8_t, const char *); +ENET_API void enet_host_refuse_new_connections (ENetHost *, int); ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *); ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID); diff --git a/thirdparty/enet/godot.cpp b/thirdparty/enet/godot.cpp index da3a86277b33..3975c1d86e9c 100644 --- a/thirdparty/enet/godot.cpp +++ b/thirdparty/enet/godot.cpp @@ -51,6 +51,7 @@ class ENetGodotSocket { virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port) = 0; virtual int set_option(ENetSocketOption p_option, int p_value) = 0; virtual void close() = 0; + virtual void set_refuse_new_connections(bool p_refuse) { /* Only used by dtls server */ } virtual ~ENetGodotSocket(){}; }; @@ -250,6 +251,10 @@ class ENetDTLSServer : public ENetGodotSocket { close(); } + void set_refuse_new_connections(bool p_refuse) { + udp_server->set_max_pending_connections(p_refuse ? 0 : 16); + } + Error bind(IP_Address p_ip, uint16_t p_port) { return udp_server->listen(p_port, p_ip); } @@ -269,6 +274,7 @@ class ENetDTLSServer : public ENetGodotSocket { } Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port) { + udp_server->poll(); // TODO limits? Maybe we can better enforce allowed connections! if (udp_server->is_connection_available()) { Ref udp = udp_server->take_connection(); @@ -409,6 +415,11 @@ void enet_host_dtls_client_setup(ENetHost *host, void *p_cert, uint8_t p_verify, memdelete(sock); } +void enet_host_refuse_new_connections(ENetHost *host, int p_refuse) { + ERR_FAIL_COND(!host->socket); + ((ENetGodotSocket *)host->socket)->set_refuse_new_connections(p_refuse); +} + int enet_socket_bind(ENetSocket socket, const ENetAddress *address) { IP_Address ip; From 67313c681d1e56b5babdc4a2f4f03a2f814a8974 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Mon, 13 Jul 2020 19:35:57 +0200 Subject: [PATCH 12/18] Reorganize ENet pactches. (cherry picked from commit 32fbe37ab4d0f654fc4625bda018d157e0987ab2) --- thirdparty/enet/enet/enet.h | 35 ++++----- thirdparty/enet/enet/godot.h | 8 +++ thirdparty/enet/enet/godot_ext.h | 18 +++++ thirdparty/enet/patches/dtls_support.patch | 13 ---- .../{ipv6_support.patch => godot.patch} | 71 +++++++++---------- 5 files changed, 77 insertions(+), 68 deletions(-) create mode 100644 thirdparty/enet/enet/godot_ext.h delete mode 100644 thirdparty/enet/patches/dtls_support.patch rename thirdparty/enet/patches/{ipv6_support.patch => godot.patch} (61%) diff --git a/thirdparty/enet/enet/enet.h b/thirdparty/enet/enet/enet.h index d3b3b54b9d10..24d36647d920 100644 --- a/thirdparty/enet/enet/enet.h +++ b/thirdparty/enet/enet/enet.h @@ -13,7 +13,16 @@ extern "C" #include #include +// -- Godot start -- +#if 0 +#ifdef _WIN32 +#include "enet/win32.h" +#else +#include "enet/unix.h" +#endif +#endif #include "enet/godot.h" +// -- Godot end -- #include "enet/types.h" #include "enet/protocol.h" @@ -69,6 +78,7 @@ typedef enum _ENetSocketShutdown ENET_SOCKET_SHUTDOWN_READ_WRITE = 2 } ENetSocketShutdown; +#define ENET_HOST_ANY 0 #define ENET_HOST_BROADCAST 0xFFFFFFFFU #define ENET_PORT_ANY 0 @@ -82,13 +92,15 @@ typedef enum _ENetSocketShutdown * but not for enet_host_create. Once a server responds to a broadcast, the * address is updated from ENET_HOST_BROADCAST to the server's actual IP address. */ +// -- Godot start -- +#if 0 typedef struct _ENetAddress { - uint8_t host[16]; + enet_uint32 host; enet_uint16 port; - uint8_t wildcard; } ENetAddress; -#define enet_host_equal(host_a, host_b) (memcmp(&host_a, &host_b,16) == 0) +#endif +// -- Godot end -- /** * Packet flag bit constants. @@ -535,16 +547,6 @@ ENET_API int enet_address_set_host_ip (ENetAddress * address, const char * hostN */ ENET_API int enet_address_set_host (ENetAddress * address, const char * hostName); -/** Sets the host field in the address parameter from ip struct. - @param address destination to store resolved address - @param ip the ip struct to read from - @param size the size of the ip struct. - @retval 0 on success - @retval != 0 on failure - @returns the address of the given ip in address on success. -*/ -ENET_API void enet_address_set_ip(ENetAddress * address, const uint8_t * ip, size_t size); - /** Gives the printable form of the IP address specified in the address parameter. @param address address printed @param hostName destination for name, must not be NULL @@ -585,9 +587,6 @@ ENET_API void enet_host_channel_limit (ENetHost *, size_t); ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32); extern void enet_host_bandwidth_throttle (ENetHost *); extern enet_uint32 enet_host_random_seed (void); -ENET_API void enet_host_dtls_server_setup (ENetHost *, void *, void *); -ENET_API void enet_host_dtls_client_setup (ENetHost *, void *, uint8_t, const char *); -ENET_API void enet_host_refuse_new_connections (ENetHost *, int); ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *); ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID); @@ -617,6 +616,10 @@ ENET_API size_t enet_range_coder_decompress (void *, const enet_uint8 *, size_t, extern size_t enet_protocol_command_size (enet_uint8); +// -- Godot start -- +#include "enet/godot_ext.h" +// -- Godot end -- + #ifdef __cplusplus } #endif diff --git a/thirdparty/enet/enet/godot.h b/thirdparty/enet/enet/godot.h index 4f25ea9c7b96..296b92763d5f 100644 --- a/thirdparty/enet/enet/godot.h +++ b/thirdparty/enet/enet/godot.h @@ -69,4 +69,12 @@ typedef struct typedef void ENetSocketSet; +typedef struct _ENetAddress +{ + uint8_t host[16]; + uint16_t port; + uint8_t wildcard; +} ENetAddress; +#define enet_host_equal(host_a, host_b) (memcmp(&host_a, &host_b,16) == 0) + #endif /* __ENET_GODOT_H__ */ diff --git a/thirdparty/enet/enet/godot_ext.h b/thirdparty/enet/enet/godot_ext.h new file mode 100644 index 000000000000..84a23b1c85d5 --- /dev/null +++ b/thirdparty/enet/enet/godot_ext.h @@ -0,0 +1,18 @@ +#ifndef __ENET_GODOT_EXT_H__ +#define __ENET_GODOT_EXT_H__ + +/** Sets the host field in the address parameter from ip struct. + @param address destination to store resolved address + @param ip the ip struct to read from + @param size the size of the ip struct. + @retval 0 on success + @retval != 0 on failure + @returns the address of the given ip in address on success. +*/ +ENET_API void enet_address_set_ip(ENetAddress * address, const uint8_t * ip, size_t size); + +ENET_API void enet_host_dtls_server_setup (ENetHost *, void *, void *); +ENET_API void enet_host_dtls_client_setup (ENetHost *, void *, uint8_t, const char *); +ENET_API void enet_host_refuse_new_connections (ENetHost *, int); + +#endif // __ENET_GODOT_EXT_H__ diff --git a/thirdparty/enet/patches/dtls_support.patch b/thirdparty/enet/patches/dtls_support.patch deleted file mode 100644 index ce3480a8586c..000000000000 --- a/thirdparty/enet/patches/dtls_support.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/thirdparty/enet/enet/enet.h b/thirdparty/enet/enet/enet.h -index 966e3a465d..ac7552adb2 100644 ---- a/thirdparty/enet/enet/enet.h -+++ b/thirdparty/enet/enet/enet.h -@@ -578,6 +578,8 @@ ENET_API void enet_host_channel_limit (ENetHost *, size_t); - ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32); - extern void enet_host_bandwidth_throttle (ENetHost *); - extern enet_uint32 enet_host_random_seed (void); -+ENET_API void enet_host_dtls_server_setup (ENetHost *, void *, void *); -+ENET_API void enet_host_dtls_client_setup (ENetHost *, void *, uint8_t, const char *); - - ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *); - ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID); diff --git a/thirdparty/enet/patches/ipv6_support.patch b/thirdparty/enet/patches/godot.patch similarity index 61% rename from thirdparty/enet/patches/ipv6_support.patch rename to thirdparty/enet/patches/godot.patch index 1f79863645ef..c8b4a5225dad 100644 --- a/thirdparty/enet/patches/ipv6_support.patch +++ b/thirdparty/enet/patches/godot.patch @@ -1,61 +1,54 @@ diff --git a/thirdparty/enet/enet/enet.h b/thirdparty/enet/enet/enet.h -index 650b199ee5..246cbb0a62 100644 +index 54d91b5603..24d36647d9 100644 --- a/thirdparty/enet/enet/enet.h +++ b/thirdparty/enet/enet/enet.h -@@ -10,13 +10,10 @@ extern "C" +@@ -10,13 +10,19 @@ extern "C" { #endif +#include #include --#ifdef _WIN32 --#include "enet/win32.h" --#else --#include "enet/unix.h" --#endif ++// -- Godot start -- ++#if 0 + #ifdef _WIN32 + #include "enet/win32.h" + #else + #include "enet/unix.h" + #endif ++#endif +#include "enet/godot.h" ++// -- Godot end -- #include "enet/types.h" #include "enet/protocol.h" -@@ -72,7 +69,6 @@ typedef enum _ENetSocketShutdown - ENET_SOCKET_SHUTDOWN_READ_WRITE = 2 - } ENetSocketShutdown; - --#define ENET_HOST_ANY 0 - #define ENET_HOST_BROADCAST 0xFFFFFFFFU - #define ENET_PORT_ANY 0 - -@@ -88,9 +84,11 @@ typedef enum _ENetSocketShutdown +@@ -86,11 +92,15 @@ typedef enum _ENetSocketShutdown + * but not for enet_host_create. Once a server responds to a broadcast, the + * address is updated from ENET_HOST_BROADCAST to the server's actual IP address. */ ++// -- Godot start -- ++#if 0 typedef struct _ENetAddress { -- enet_uint32 host; -+ uint8_t host[16]; + enet_uint32 host; enet_uint16 port; -+ uint8_t wildcard; } ENetAddress; -+#define enet_host_equal(host_a, host_b) (memcmp(&host_a, &host_b,16) == 0) ++#endif ++// -- Godot end -- /** * Packet flag bit constants. -@@ -519,6 +517,16 @@ ENET_API int enet_socketset_select (ENetSocket, ENetSocketSet *, ENetSock - */ - ENET_API int enet_address_set_host (ENetAddress * address, const char * hostName); +@@ -606,6 +616,10 @@ ENET_API size_t enet_range_coder_decompress (void *, const enet_uint8 *, size_t, + + extern size_t enet_protocol_command_size (enet_uint8); -+/** Sets the host field in the address parameter from ip struct. -+ @param address destination to store resolved address -+ @param ip the ip struct to read from -+ @param size the size of the ip struct. -+ @retval 0 on success -+ @retval != 0 on failure -+ @returns the address of the given ip in address on success. -+*/ -+ENET_API void enet_address_set_ip(ENetAddress * address, const uint8_t * ip, size_t size); ++// -- Godot start -- ++#include "enet/godot_ext.h" ++// -- Godot end -- + - /** Gives the printable form of the IP address specified in the address parameter. - @param address address printed - @param hostName destination for name, must not be NULL + #ifdef __cplusplus + } + #endif diff --git a/thirdparty/enet/host.c b/thirdparty/enet/host.c index 3be6c0922c..fc4da4ca67 100644 --- a/thirdparty/enet/host.c @@ -70,10 +63,10 @@ index 3be6c0922c..fc4da4ca67 100644 host -> receivedData = NULL; host -> receivedDataLength = 0; diff --git a/thirdparty/enet/protocol.c b/thirdparty/enet/protocol.c -index 29d648732d..ab26886de4 100644 +index 0a60253173..fefc0e6f0a 100644 --- a/thirdparty/enet/protocol.c +++ b/thirdparty/enet/protocol.c -@@ -298,7 +298,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet +@@ -307,7 +307,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet } else if (currentPeer -> state != ENET_PEER_STATE_CONNECTING && @@ -82,7 +75,7 @@ index 29d648732d..ab26886de4 100644 { if (currentPeer -> address.port == host -> receivedAddress.port && currentPeer -> connectID == command -> connect.connectID) -@@ -1010,9 +1010,8 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) +@@ -1027,9 +1027,8 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE || @@ -94,7 +87,7 @@ index 29d648732d..ab26886de4 100644 (peer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID && sessionID != peer -> incomingSessionID)) return 0; -@@ -1054,7 +1053,7 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) +@@ -1071,7 +1070,7 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) if (peer != NULL) { From ceeb69047699043920d2efe1fa3eaddd886a9980 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Tue, 14 Jul 2020 10:19:58 -0300 Subject: [PATCH 13/18] Properly pass safe margin on initialization. Fixes jitter. (cherry picked from commit 13e0385702c9b5d152f2781ff566e07db9aeddc3) --- scene/3d/physics_body.cpp | 5 +++-- servers/physics/body_sw.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index 1e9e66722628..46932b80e923 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -1432,13 +1432,14 @@ void KinematicBody::_bind_methods() { KinematicBody::KinematicBody() : PhysicsBody(PhysicsServer::BODY_MODE_KINEMATIC) { - - margin = 0.001; locked_axis = 0; on_floor = false; on_ceiling = false; on_wall = false; + + set_safe_margin(0.001); } + KinematicBody::~KinematicBody() { if (motion_cache.is_valid()) { diff --git a/servers/physics/body_sw.cpp b/servers/physics/body_sw.cpp index 64e07e1155ae..c8b3ddf27f09 100644 --- a/servers/physics/body_sw.cpp +++ b/servers/physics/body_sw.cpp @@ -773,7 +773,7 @@ BodySW::BodySW() : active = true; mass = 1; - kinematic_safe_margin = 0.01; + kinematic_safe_margin = 0.001; //_inv_inertia=Transform(); _inv_mass = 1; bounce = 0; From f7021e57d7e682772dc98a42375fc09e820bce87 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Tue, 14 Jul 2020 16:16:08 +0200 Subject: [PATCH 14/18] Document VehicleBody3D and VehicleWheel3D limitations These classes have dozens of open bugs and missing features which may not be fixed anytime soon. It's probably better to document it upfront at this point. (cherry picked from commit 0493e7c106811e94ad8dbebb0b97c43b19e4023a) --- doc/classes/VehicleBody.xml | 1 + doc/classes/VehicleWheel.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/classes/VehicleBody.xml b/doc/classes/VehicleBody.xml index 3b99bf9ed10d..672aa59855b0 100644 --- a/doc/classes/VehicleBody.xml +++ b/doc/classes/VehicleBody.xml @@ -6,6 +6,7 @@ This node implements all the physics logic needed to simulate a car. It is based on the raycast vehicle system commonly found in physics engines. You will need to add a [CollisionShape] for the main body of your vehicle and add [VehicleWheel] nodes for the wheels. You should also add a [MeshInstance] to this node for the 3D model of your car but this model should not include meshes for the wheels. You should control the vehicle by using the [member brake], [member engine_force], and [member steering] properties and not change the position or orientation of this node directly. [b]Note:[/b] The origin point of your VehicleBody will determine the center of gravity of your vehicle so it is better to keep this low and move the [CollisionShape] and [MeshInstance] upwards. + [b]Note:[/b] This class has known issues and isn't designed to provide realistic 3D vehicle physics. If you want advanced vehicle physics, you will probably have to write your own physics integration using another [PhysicsBody] class. diff --git a/doc/classes/VehicleWheel.xml b/doc/classes/VehicleWheel.xml index b611d00da048..055ce5990f00 100644 --- a/doc/classes/VehicleWheel.xml +++ b/doc/classes/VehicleWheel.xml @@ -5,6 +5,7 @@ This node needs to be used as a child node of [VehicleBody] and simulates the behavior of one of its wheels. This node also acts as a collider to detect if the wheel is touching a surface. + [b]Note:[/b] This class has known issues and isn't designed to provide realistic 3D vehicle physics. If you want advanced vehicle physics, you will probably have to write your own physics integration using another [PhysicsBody] class. From 8bc44c868ed61cfc0cf61e94391b02767253d469 Mon Sep 17 00:00:00 2001 From: Emmanuel Leblond Date: Tue, 14 Jul 2020 18:20:20 +0200 Subject: [PATCH 15/18] Correct is_reference attribute in api.json for Reference class (cherry picked from commit b5c80088ce70bf8ef8fd3b3a0cf98bf18a453743) --- modules/gdnative/nativescript/api_generator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp index 850871579b6c..48a1d26f4f36 100644 --- a/modules/gdnative/nativescript/api_generator.cpp +++ b/modules/gdnative/nativescript/api_generator.cpp @@ -219,7 +219,7 @@ List generate_c_api_classes() { { List inheriters; ClassDB::get_inheriters_from_class("Reference", &inheriters); - bool is_reference = !!inheriters.find(class_name); + bool is_reference = !!inheriters.find(class_name) || class_name == "Reference"; // @Unclear class_api.is_reference = !class_api.is_singleton && is_reference; } From f031e72395df458be15b84374fc5cc371d072de3 Mon Sep 17 00:00:00 2001 From: Emmanuel Leblond Date: Tue, 14 Jul 2020 23:12:44 +0200 Subject: [PATCH 16/18] Add missing has_default_value field for signals in api.json (cherry picked from commit 37de4982caef5ad9909a87e071be3c933927cccb) --- modules/gdnative/nativescript/api_generator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp index 48a1d26f4f36..256c44febcd1 100644 --- a/modules/gdnative/nativescript/api_generator.cpp +++ b/modules/gdnative/nativescript/api_generator.cpp @@ -458,6 +458,7 @@ static List generate_c_api_json(const List &p_api) { source.push_back("\t\t\t\t\t{\n"); source.push_back("\t\t\t\t\t\t\"name\": \"" + e->get().argument_names[i] + "\",\n"); source.push_back("\t\t\t\t\t\t\"type\": \"" + e->get().argument_types[i] + "\",\n"); + source.push_back(String("\t\t\t\t\t\t\"has_default_value\": ") + (e->get().default_arguments.has(i) ? "true" : "false") + ",\n"); source.push_back("\t\t\t\t\t\t\"default_value\": \"" + (e->get().default_arguments.has(i) ? (String)e->get().default_arguments[i] : "") + "\"\n"); source.push_back(String("\t\t\t\t\t}") + ((i < e->get().argument_names.size() - 1) ? "," : "") + "\n"); } From e41ab634c6885d9836182148d26b48bf5add8746 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Fri, 17 Apr 2020 15:46:23 +0200 Subject: [PATCH 17/18] Mention C# gotchas in Object's dynamic call/set/connect methods This closes #34015. (cherry picked from commit 878f03d8e3452b0d18bc1b693af6029fdbacb92b) --- doc/classes/Object.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index a6f11fea55d0..59d4f47ba579 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -97,6 +97,7 @@ [codeblock] call("set", "position", Vector2(42.0, 0.0)) [/codeblock] + [b]Note:[/b] In C#, the method name must be specified as snake_case if it is defined by a built-in Godot node. This doesn't apply to user-defined methods where you should use the same convention as in the C# source (typically PascalCase). @@ -109,6 +110,7 @@ [codeblock] call_deferred("set", "position", Vector2(42.0, 0.0)) [/codeblock] + [b]Note:[/b] In C#, the method name must be specified as snake_case if it is defined by a built-in Godot node. This doesn't apply to user-defined methods where you should use the same convention as in the C# source (typically PascalCase). @@ -205,6 +207,7 @@ Returns the [Variant] value of the given [code]property[/code]. If the [code]property[/code] doesn't exist, this will return [code]null[/code]. + [b]Note:[/b] In C#, the property name must be specified as snake_case if it is defined by a built-in Godot node. This doesn't apply to user-defined properties where you should use the same convention as in the C# source (typically PascalCase). @@ -405,6 +408,7 @@ Assigns a new value to the given property. If the [code]property[/code] does not exist, nothing will happen. + [b]Note:[/b] In C#, the property name must be specified as snake_case if it is defined by a built-in Godot node. This doesn't apply to user-defined properties where you should use the same convention as in the C# source (typically PascalCase). @@ -425,6 +429,7 @@ Assigns a new value to the given property, after the current frame's physics step. This is equivalent to calling [method set] via [method call_deferred], i.e. [code]call_deferred("set", property, value)[/code]. + [b]Note:[/b] In C#, the property name must be specified as snake_case if it is defined by a built-in Godot node. This doesn't apply to user-defined properties where you should use the same convention as in the C# source (typically PascalCase). From 2244841729a479682ef916c4065f89026f359890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Verschelde?= Date: Wed, 15 Jul 2020 12:56:16 +0200 Subject: [PATCH 18/18] doc: Sync classref with current source --- doc/classes/CollisionObject2D.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/classes/CollisionObject2D.xml b/doc/classes/CollisionObject2D.xml index 4fdead27b976..e87566e42cb4 100644 --- a/doc/classes/CollisionObject2D.xml +++ b/doc/classes/CollisionObject2D.xml @@ -216,7 +216,7 @@ - + If [code]true[/code], this object is pickable. A pickable object can detect the mouse pointer entering/leaving, and if the mouse is inside it, report input events.