summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml38
-rw-r--r--AUTHORS.md3
-rw-r--r--COPYRIGHT.txt2
-rw-r--r--DONORS.md44
-rw-r--r--core/array.cpp8
-rw-r--r--core/array.h2
-rw-r--r--core/bind/core_bind.cpp9
-rw-r--r--core/bind/core_bind.h1
-rw-r--r--core/class_db.cpp4
-rw-r--r--core/class_db.h4
-rw-r--r--core/dictionary.cpp4
-rw-r--r--core/dictionary.h2
-rw-r--r--core/image.cpp108
-rw-r--r--core/image.h1
-rw-r--r--core/input_map.cpp10
-rw-r--r--core/input_map.h5
-rw-r--r--core/io/http_client.cpp22
-rw-r--r--core/io/multiplayer_api.cpp722
-rw-r--r--core/io/multiplayer_api.h87
-rw-r--r--core/io/resource_format_binary.cpp8
-rw-r--r--core/io/stream_peer_ssl.cpp31
-rw-r--r--core/io/stream_peer_ssl.h1
-rw-r--r--core/math/geometry.h13
-rw-r--r--core/math/quat.cpp2
-rw-r--r--core/os/input.cpp3
-rw-r--r--core/os/input.h2
-rw-r--r--core/os/keyboard.cpp121
-rw-r--r--core/os/keyboard.h1
-rw-r--r--core/os/os.h12
-rw-r--r--core/register_core_types.cpp2
-rw-r--r--core/resource.cpp2
-rw-r--r--core/script_language.h4
-rw-r--r--core/ustring.cpp74
-rw-r--r--core/ustring.h7
-rw-r--r--core/variant.h1
-rw-r--r--core/variant_call.cpp8
-rw-r--r--core/variant_op.cpp13
-rw-r--r--doc/classes/ARVRServer.xml36
-rw-r--r--doc/classes/AnimationPlayer.xml7
-rw-r--r--doc/classes/Array.xml5
-rw-r--r--doc/classes/AudioServer.xml20
-rw-r--r--doc/classes/Basis.xml1
-rw-r--r--doc/classes/Camera.xml24
-rw-r--r--doc/classes/CanvasLayer.xml6
-rw-r--r--doc/classes/Control.xml6
-rw-r--r--doc/classes/Dictionary.xml2
-rw-r--r--doc/classes/EditorPlugin.xml24
-rw-r--r--doc/classes/File.xml4
-rw-r--r--doc/classes/HTTPClient.xml6
-rw-r--r--doc/classes/Image.xml78
-rw-r--r--doc/classes/Input.xml2
-rw-r--r--doc/classes/InterpolatedCamera.xml8
-rw-r--r--doc/classes/ItemList.xml45
-rw-r--r--doc/classes/MultiplayerAPI.xml131
-rw-r--r--doc/classes/Navigation.xml22
-rw-r--r--doc/classes/Navigation2D.xml9
-rw-r--r--doc/classes/Node.xml29
-rw-r--r--doc/classes/OS.xml99
-rw-r--r--doc/classes/OptionButton.xml7
-rw-r--r--doc/classes/PopupMenu.xml61
-rw-r--r--doc/classes/Quat.xml1
-rw-r--r--doc/classes/SceneTree.xml52
-rw-r--r--doc/classes/ScrollContainer.xml12
-rw-r--r--doc/classes/SurfaceTool.xml3
-rw-r--r--doc/classes/TabContainer.xml16
-rw-r--r--doc/classes/Tabs.xml16
-rw-r--r--doc/classes/TextEdit.xml16
-rw-r--r--doc/classes/TextureProgress.xml6
-rw-r--r--doc/classes/TileSet.xml24
-rw-r--r--doc/classes/Transform.xml1
-rw-r--r--doc/classes/Vector2.xml4
-rw-r--r--doc/classes/VideoPlayer.xml6
-rw-r--r--doc/classes/WebSocketClient.xml4
-rw-r--r--drivers/alsa/audio_driver_alsa.cpp143
-rw-r--r--drivers/alsa/audio_driver_alsa.h13
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.cpp212
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.h22
-rw-r--r--drivers/gles2/shader_compiler_gles2.cpp6
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp4
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp20
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.cpp421
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.h28
-rw-r--r--drivers/wasapi/audio_driver_wasapi.cpp142
-rw-r--r--drivers/wasapi/audio_driver_wasapi.h6
-rw-r--r--editor/code_editor.cpp50
-rw-r--r--editor/create_dialog.cpp21
-rw-r--r--editor/editor_data.cpp25
-rw-r--r--editor/editor_data.h1
-rw-r--r--editor/editor_help.cpp27
-rw-r--r--editor/editor_help.h1
-rw-r--r--editor/editor_node.cpp94
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/editor_themes.cpp4
-rw-r--r--editor/filesystem_dock.cpp91
-rw-r--r--editor/filesystem_dock.h5
-rw-r--r--editor/find_in_files.cpp829
-rw-r--r--editor/find_in_files.h184
-rw-r--r--editor/import/editor_import_collada.cpp2
-rw-r--r--editor/import/editor_scene_importer_gltf.cpp15
-rw-r--r--editor/import/resource_importer_wav.cpp28
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp6
-rw-r--r--editor/plugins/animation_tree_editor_plugin.cpp2
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp2
-rw-r--r--editor/plugins/item_list_editor_plugin.cpp28
-rw-r--r--editor/plugins/item_list_editor_plugin.h4
-rw-r--r--editor/plugins/script_editor_plugin.cpp194
-rw-r--r--editor/plugins/script_editor_plugin.h25
-rw-r--r--editor/plugins/script_text_editor.cpp69
-rw-r--r--editor/plugins/script_text_editor.h9
-rw-r--r--editor/plugins/shader_graph_editor_plugin.cpp2
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp26
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp10
-rw-r--r--editor/plugins/theme_editor_plugin.cpp4
-rw-r--r--editor/plugins/tile_map_editor_plugin.cpp58
-rw-r--r--editor/plugins/tile_map_editor_plugin.h2
-rw-r--r--editor/plugins/tile_set_editor_plugin.cpp6
-rw-r--r--editor/project_settings_editor.cpp45
-rw-r--r--editor/project_settings_editor.h6
-rw-r--r--editor/property_editor.cpp53
-rw-r--r--editor/scene_tree_dock.cpp76
-rw-r--r--editor/settings_config_dialog.cpp16
-rw-r--r--main/SCsub2
-rw-r--r--main/input_default.cpp18
-rw-r--r--main/input_default.h4
-rw-r--r--main/tests/test_gui.cpp4
-rw-r--r--misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj34
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-568h@2x.pngbin564 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-667h@2x.pngbin817 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Landscape-1366h@2x.pngbin32836 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Landscape-736h@3x.pngbin2582 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Landscape@2x.pngbin3131 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Portrait-1366h@2x.pngbin33309 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Portrait-736h@3x.pngbin1676 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Portrait@2x.pngbin33309 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Contents.json102
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.pngbin0 -> 13558 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.pngbin0 -> 17911 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.pngbin0 -> 23513 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.pngbin0 -> 53789 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.pngbin0 -> 51462 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.pngbin0 -> 18386 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.pngbin0 -> 61558 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.pngbin0 -> 56053 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.pngbin0 -> 53983 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.pngbin0 -> 18753 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.pngbin0 -> 62336 bytes
-rwxr-xr-xmisc/travis/scons-local-osx.sh4
-rw-r--r--modules/bullet/area_bullet.cpp24
-rw-r--r--modules/bullet/area_bullet.h4
-rw-r--r--modules/bullet/collision_object_bullet.cpp6
-rw-r--r--modules/bullet/godot_result_callbacks.cpp22
-rw-r--r--modules/bullet/godot_result_callbacks.h5
-rw-r--r--modules/bullet/shape_bullet.cpp54
-rw-r--r--modules/bullet/shape_bullet.h7
-rw-r--r--modules/bullet/space_bullet.cpp14
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp148
-rw-r--r--modules/enet/networked_multiplayer_enet.h5
-rw-r--r--modules/gdnative/doc_classes/GDNativeLibrary.xml8
-rw-r--r--modules/gdnative/gdnative.cpp231
-rw-r--r--modules/gdnative/gdnative.h6
-rw-r--r--modules/gdnative/gdnative/string.cpp18
-rw-r--r--modules/gdnative/gdnative_api.json31
-rw-r--r--modules/gdnative/include/gdnative/string.h7
-rw-r--r--modules/gdnative/include/nativescript/godot_nativescript.h5
-rw-r--r--modules/gdnative/nativescript/godot_nativescript.cpp22
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp44
-rw-r--r--modules/gdnative/nativescript/nativescript.h6
-rw-r--r--modules/gdnative/pluginscript/register_types.cpp4
-rw-r--r--modules/gdscript/gdscript_editor.cpp43
-rw-r--r--modules/gdscript/gdscript_highlighter.cpp262
-rw-r--r--modules/gdscript/gdscript_highlighter.h56
-rw-r--r--modules/gdscript/register_types.cpp2
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp12
-rwxr-xr-xmodules/mbedtls/stream_peer_mbed_tls.cpp26
-rwxr-xr-xmodules/mbedtls/stream_peer_mbed_tls.h2
-rw-r--r--modules/mono/editor/mono_bottom_panel.cpp9
-rw-r--r--modules/visual_script/visual_script_editor.cpp6
-rw-r--r--modules/visual_script/visual_script_editor.h3
-rw-r--r--modules/websocket/emws_peer.cpp6
-rw-r--r--modules/websocket/emws_server.cpp13
-rw-r--r--modules/websocket/emws_server.h3
-rw-r--r--modules/websocket/lws_client.cpp20
-rw-r--r--modules/websocket/lws_peer.cpp39
-rw-r--r--modules/websocket/lws_server.cpp18
-rw-r--r--modules/websocket/lws_server.h3
-rw-r--r--modules/websocket/websocket_client.cpp16
-rw-r--r--modules/websocket/websocket_client.h4
-rw-r--r--modules/websocket/websocket_peer.cpp2
-rw-r--r--modules/websocket/websocket_server.cpp3
-rw-r--r--modules/websocket/websocket_server.h4
-rw-r--r--platform/android/java_glue.cpp27
-rw-r--r--platform/iphone/app_delegate.mm103
-rw-r--r--platform/iphone/export/export.cpp67
-rw-r--r--platform/iphone/gl_view.h2
-rw-r--r--platform/iphone/gl_view.mm15
-rw-r--r--platform/iphone/os_iphone.cpp6
-rw-r--r--platform/iphone/os_iphone.h1
-rw-r--r--platform/javascript/detect.py7
-rw-r--r--platform/javascript/engine.js40
-rw-r--r--platform/javascript/pre.js2
-rw-r--r--platform/osx/godot_main_osx.mm2
-rw-r--r--platform/osx/os_osx.mm118
-rw-r--r--platform/windows/os_windows.cpp23
-rw-r--r--platform/x11/detect.py4
-rw-r--r--platform/x11/os_x11.cpp12
-rw-r--r--scene/2d/canvas_item.cpp14
-rw-r--r--scene/2d/parallax_layer.cpp2
-rw-r--r--scene/2d/ray_cast_2d.cpp10
-rw-r--r--scene/3d/particles.cpp4
-rw-r--r--scene/3d/physics_body.cpp2
-rw-r--r--scene/3d/ray_cast.cpp10
-rw-r--r--scene/3d/sprite_3d.cpp29
-rw-r--r--scene/3d/vehicle_body.cpp12
-rw-r--r--scene/3d/vehicle_body.h2
-rw-r--r--scene/animation/animation_player.cpp14
-rw-r--r--scene/animation/animation_player.h1
-rw-r--r--scene/animation/animation_tree_player.cpp2
-rw-r--r--scene/gui/base_button.cpp2
-rw-r--r--scene/gui/control.cpp33
-rw-r--r--scene/gui/control.h5
-rw-r--r--scene/gui/item_list.cpp24
-rw-r--r--scene/gui/item_list.h4
-rw-r--r--scene/gui/line_edit.cpp20
-rw-r--r--scene/gui/line_edit.h1
-rw-r--r--scene/gui/menu_button.cpp2
-rw-r--r--scene/gui/option_button.cpp4
-rw-r--r--scene/gui/popup_menu.cpp159
-rw-r--r--scene/gui/popup_menu.h18
-rw-r--r--scene/gui/rich_text_label.cpp33
-rw-r--r--scene/gui/scroll_bar.cpp16
-rw-r--r--scene/gui/scroll_container.cpp80
-rw-r--r--scene/gui/scroll_container.h6
-rw-r--r--scene/gui/tab_container.cpp161
-rw-r--r--scene/gui/tab_container.h12
-rw-r--r--scene/gui/tabs.cpp118
-rw-r--r--scene/gui/tabs.h7
-rw-r--r--scene/gui/text_edit.cpp549
-rw-r--r--scene/gui/text_edit.h169
-rw-r--r--scene/gui/texture_progress.cpp72
-rw-r--r--scene/gui/texture_progress.h12
-rw-r--r--scene/gui/tree.cpp16
-rw-r--r--scene/gui/video_player.cpp15
-rw-r--r--scene/main/canvas_layer.cpp40
-rw-r--r--scene/main/canvas_layer.h7
-rw-r--r--scene/main/node.cpp259
-rw-r--r--scene/main/node.h13
-rw-r--r--scene/main/scene_tree.cpp481
-rw-r--r--scene/main/scene_tree.h44
-rw-r--r--scene/main/viewport.cpp25
-rw-r--r--scene/main/viewport.h2
-rw-r--r--scene/resources/default_theme/default_theme.cpp2
-rw-r--r--scene/resources/packed_scene.cpp2
-rw-r--r--scene/resources/scene_format_text.cpp13
-rw-r--r--scene/resources/surface_tool.cpp10
-rw-r--r--scene/resources/surface_tool.h2
-rw-r--r--scene/resources/tile_set.cpp7
-rw-r--r--scene/resources/tile_set.h1
-rw-r--r--servers/arvr_server.cpp2
-rw-r--r--servers/audio_server.cpp30
-rw-r--r--servers/audio_server.h7
-rw-r--r--servers/physics/collision_object_sw.h3
-rw-r--r--servers/physics_2d/collision_object_2d_sw.cpp28
-rw-r--r--servers/physics_2d/collision_object_2d_sw.h5
-rw-r--r--servers/visual/shader_language.cpp5
-rw-r--r--servers/visual/shader_types.cpp4
-rw-r--r--thirdparty/README.md20
-rw-r--r--thirdparty/fonts/Hack_Regular.ttfbin307420 -> 309408 bytes
-rw-r--r--thirdparty/fonts/LICENSE_Hack.md4
-rw-r--r--thirdparty/lws/client/client.c20
-rw-r--r--thirdparty/lws/client/ssl-client.c14
-rw-r--r--thirdparty/lws/context.c2
-rw-r--r--thirdparty/lws/libwebsockets.c11
-rw-r--r--thirdparty/lws/libwebsockets.h7
-rw-r--r--thirdparty/lws/lws_config.h36
-rw-r--r--thirdparty/lws/mbedtls_verify.diff74
-rw-r--r--thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h1
-rw-r--r--thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h4
-rw-r--r--thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c31
-rw-r--r--thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c79
-rw-r--r--thirdparty/lws/misc/lejp.c2
-rw-r--r--thirdparty/lws/misc/sha-1.c2
-rw-r--r--thirdparty/lws/output.c10
-rw-r--r--thirdparty/lws/pollfd.c7
-rw-r--r--thirdparty/lws/private-libwebsockets.h5
-rw-r--r--thirdparty/lws/server/ssl-server.c2
-rw-r--r--thirdparty/lws/service.c17
-rw-r--r--thirdparty/lws/ssl.c15
-rw-r--r--thirdparty/mbedtls/1453.diff120
-rw-r--r--thirdparty/mbedtls/include/mbedtls/asn1.h15
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ccm.h5
-rw-r--r--thirdparty/mbedtls/include/mbedtls/check_config.h4
-rw-r--r--thirdparty/mbedtls/include/mbedtls/config.h30
-rw-r--r--thirdparty/mbedtls/include/mbedtls/dhm.h2
-rw-r--r--thirdparty/mbedtls/include/mbedtls/md2.h44
-rw-r--r--thirdparty/mbedtls/include/mbedtls/md4.h46
-rw-r--r--thirdparty/mbedtls/include/mbedtls/md5.h46
-rw-r--r--thirdparty/mbedtls/include/mbedtls/oid.h18
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ripemd160.h43
-rw-r--r--thirdparty/mbedtls/include/mbedtls/rsa.h12
-rw-r--r--thirdparty/mbedtls/include/mbedtls/sha1.h46
-rw-r--r--thirdparty/mbedtls/include/mbedtls/sha256.h50
-rw-r--r--thirdparty/mbedtls/include/mbedtls/sha512.h49
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ssl.h18
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ssl_internal.h3
-rw-r--r--thirdparty/mbedtls/include/mbedtls/version.h8
-rw-r--r--thirdparty/mbedtls/library/aes.c4
-rw-r--r--thirdparty/mbedtls/library/bignum.c2
-rw-r--r--thirdparty/mbedtls/library/ctr_drbg.c2
-rw-r--r--thirdparty/mbedtls/library/debug.c2
-rw-r--r--thirdparty/mbedtls/library/entropy_poll.c16
-rw-r--r--thirdparty/mbedtls/library/md2.c40
-rw-r--r--thirdparty/mbedtls/library/md4.c41
-rw-r--r--thirdparty/mbedtls/library/md5.c41
-rw-r--r--thirdparty/mbedtls/library/memory_buffer_alloc.c41
-rw-r--r--thirdparty/mbedtls/library/net_sockets.c9
-rw-r--r--thirdparty/mbedtls/library/oid.c45
-rw-r--r--thirdparty/mbedtls/library/pem.c5
-rw-r--r--thirdparty/mbedtls/library/pkcs5.c4
-rw-r--r--thirdparty/mbedtls/library/pkparse.c73
-rw-r--r--thirdparty/mbedtls/library/platform.c2
-rw-r--r--thirdparty/mbedtls/library/ripemd160.c41
-rw-r--r--thirdparty/mbedtls/library/rsa.c124
-rw-r--r--thirdparty/mbedtls/library/sha1.c41
-rw-r--r--thirdparty/mbedtls/library/sha256.c43
-rw-r--r--thirdparty/mbedtls/library/sha512.c43
-rw-r--r--thirdparty/mbedtls/library/ssl_cli.c23
-rw-r--r--thirdparty/mbedtls/library/ssl_tls.c50
-rw-r--r--thirdparty/mbedtls/library/version.c2
-rw-r--r--thirdparty/mbedtls/library/version_features.c3
-rw-r--r--thirdparty/mbedtls/library/x509_crl.c65
-rw-r--r--thirdparty/mbedtls/library/x509_crt.c27
332 files changed, 8422 insertions, 2955 deletions
diff --git a/.travis.yml b/.travis.yml
index 4bad241c53..57580c1c41 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,7 +8,8 @@ env:
global:
- SCONS_CACHE=$HOME/.scons_cache
- SCONS_CACHE_LIMIT=1024
- - OPTIONS="verbose=yes progress=no openmp=no gdnative_wrapper=yes"
+ - OPTIONS="verbose=yes progress=no gdnative_wrapper=yes"
+ - secure: AnjB84ZZYDxDQdWwsT9PJrD5tEnwcauzvVeSG+us2GxgfyJ2HFArkZ/IjlvbsfYIiiHghJ2Ssy9yPtUT921BtNNHoWuN/8YQziGrlwiSfLGmS/n8GFH22OYwzDSa7UC7ODts5La2I4+JzrdtF933TwE+4QzH4E3GyaKbznh402E=
cache:
directories:
@@ -18,7 +19,7 @@ matrix:
include:
- env: STATIC_CHECKS=yes
os: linux
- compiler: clang
+ compiler: gcc
- env: GODOT_TARGET=x11 TOOLS=yes CACHE_NAME=${GODOT_TARGET}-gcc-tools"
os: linux
compiler: gcc
@@ -31,12 +32,14 @@ matrix:
- env: GODOT_TARGET=android TOOLS=no CACHE_NAME=${GODOT_TARGET}-gcc
os: linux
compiler: gcc
- #- env: GODOT_TARGET=osx TOOLS=yes CACHE_NAME=${GODOT_TARGET}-clang-tools
- # os: osx
- # compiler: clang
- #- env: GODOT_TARGET=iphone TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang
- # os: osx
- # compiler: clang
+ - env: GODOT_TARGET=osx TOOLS=yes CACHE_NAME=${GODOT_TARGET}-clang-tools
+ os: osx
+ osx_image: xcode9.3
+ compiler: clang
+ - env: GODOT_TARGET=iphone TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang
+ os: osx
+ osx_image: xcode9.3
+ compiler: clang
addons:
apt:
@@ -69,6 +72,25 @@ addons:
# For style checks.
- clang-format-5.0
+ coverity_scan:
+ project:
+ name: "godotengine/godot"
+ description: "Godot Engine Coverity scans"
+ notification_email: coverity@godotengine.org
+ build_command_prepend: ""
+ build_command: "scons p=x11 -j2 $OPTIONS"
+ branch_pattern: coverity_scan
+
+before_install:
+ - if [ "$STATIC_CHECKS" = "yes" ]; then
+ unset SCONS_CACHE;
+ else
+ if [ "$TRAVIS_BRANCH" = "coverity_scan" ]; then
+ echo "This job runs in the Coverity Scan branch and is not the STATIC_CHECKS job meant for it, so aborting with exit code 0.";
+ travis_terminate 0;
+ fi;
+ fi
+
install:
- if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$GODOT_TARGET" = "android" ]; then
misc/travis/android-tools-linux.sh;
diff --git a/AUTHORS.md b/AUTHORS.md
index 3d7a6adf60..a5ef24e825 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -117,7 +117,8 @@ name is available.
m4nu3lf
marynate
mrezai
- rraallvv
+ robfram
romulox-x
+ rraallvv
sersoong
yg2f (SuperUserNameMan)
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 0f421741b1..6b6ee4c509 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -157,7 +157,7 @@ License: OFL-1.1
Files: ./thirdparty/fonts/Hack_Regular.ttf
Comment: Hack font
-Copyright: 2017, Source Foundry Authors
+Copyright: 2018, Source Foundry Authors
2003, Bitstream Inc.
License: Expat and Bitstream Vera Fonts Copyright
diff --git a/DONORS.md b/DONORS.md
index 52c8da4c0c..1a129e36d7 100644
--- a/DONORS.md
+++ b/DONORS.md
@@ -30,9 +30,11 @@ generous deed immortalized in the next stable release of Godot Engine.
codetrotter
E Hewert
Hein-Pieter van Braam
+ Igors Vaitkus
Jamal Alyafei
Jay Sistar
Matthieu Huvé
+ Mike King
Nathan Warden
Neal Gompa (Conan Kudo)
Pascal Julien
@@ -50,6 +52,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Asdf
cheese65536
Jake Bo
+ Kris Michael
Manuele Finocchiaro
Officine Pixel S.n.c.
Rémi Verschelde
@@ -60,11 +63,15 @@ generous deed immortalized in the next stable release of Godot Engine.
Austen McRae
Benjamin Botwin
Bernhard Liebl
+ Catalin Moldovan
+ DeepSquid
+ Florian Breisch
+ Gary Oberbrunner
Johannes Wuensch
Josep G. Camarasa
- Kris Michael
+ Joshua Lesperance
Libre-Dépanne
- Mike King
+ Matthew Bennett
Ranoller
Rob Messick
Svenne Krap
@@ -81,12 +88,14 @@ generous deed immortalized in the next stable release of Godot Engine.
Garrett Dockins
Guilherme Felipe de C. G. da Silva
Harman Bains
- Henrique Alves
+ John
+ Justo Delgado Baudí
Karsten Bock
Laurence Bannister
Rami
Robert Willes
Robin Arys
+ Rufus Sasparilla
ScottMakesGames
Testus Maximus
Thomas Bjarnelöf
@@ -94,9 +103,9 @@ generous deed immortalized in the next stable release of Godot Engine.
Xavier Tan
Zaq Poi
+ Alessandra Pereyra
Alexey Dyadchenko
Amanda Haldy
- Arnaud Verstuyf
Chris Brown
Chris Wilson
Cody Parker
@@ -111,23 +120,26 @@ generous deed immortalized in the next stable release of Godot Engine.
Jeppe Zapp
Jeremi Biernacki
joe513
+ John O'Mahoney
Jordan M Lucas
Juraj Móza
Justin Arnold
- Justo Delgado Baudí
Leandro Voltolino
Lisandro Lorea
+ Marco Andrew Cafolla
Markus Wiesner
- Marty Plumbo
Marvin
Nick Nikitin
Pablo Cholaky
Patrick Schnorbus
Pete Goodwin
Phyronnaz
+ Simon De Greve
+ Sofox
Ted
Travis Womack
Trent McPheron
+ Vladimir
## Silver donors
@@ -142,13 +154,13 @@ generous deed immortalized in the next stable release of Godot Engine.
Anthony Bongiovanni
Arda Erol
Arthur S. Muszynski
+ Aubrey Falconer
Avencherus
Bastian Böhm
Benedikt
Benjamin Beshara
Ben Vercammen
Blair Allen
- Bryan Crow
Bryanna M
Bryan Stevenson
Carwyn Edwards
@@ -161,6 +173,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Daniel Kaplan
Daniel Langegger
Daniel Mircea
+ David
David Cravens
David May
Dominik Wetzel
@@ -168,21 +181,23 @@ generous deed immortalized in the next stable release of Godot Engine.
Fabian Becker
fengjiongmax
Francesco Lisi
+ Frédéric Alix
G3Dev sàrl
Geequlim
Gerrit Großkopf
Gerrit Procee
Gilberto K. Otubo
Guldoman
- HeartBeast
Heribert Hirth
+ hubert jenkins
Hunter Jones
ialex32x
+ Ivan Vodopiviz
Jaime Ruiz-Borau Vizárraga
- Jed Rose
+ Jed
Jeff Hungerford
Joel Fivat
- Johannes du Randt
+ Johan Lindberg
Jonas Yamazaki
Jonathan Martin
Jonathan Nieto
@@ -199,9 +214,9 @@ generous deed immortalized in the next stable release of Godot Engine.
Klavdij Voncina
Lars pfeffer
Linus Lind Lundgren
+ Macil
magodev
Martin Eigel
- Martin Novák
Matthew Fitzpatrick
Matthias Hölzl
Max R.R. Collada
@@ -229,15 +244,14 @@ generous deed immortalized in the next stable release of Godot Engine.
Patric Vormstein
Paul Mason
Paweł Kowal
- Pedro Luz
Pierre-Igor Berthet
Pietro Vertechi
Piotr Kaczmarski
Richman Stewart
- Rodolfo Baeza
Roger Burgess
Roger Smith
Roman Tinkov
+ Ryan Whited
Sasori Olkof
Scott D. Yelich
Sootstone
@@ -245,10 +259,14 @@ generous deed immortalized in the next stable release of Godot Engine.
Thibault Barbaroux
Thomas Bell
Thomas Herzog & Xananax
+ Thomas Kurz
Tom Larrow
Tyler Stafos
UltyX
+ Victor Gonzalez Fernandez
Victor Holt
+ Viktor Ferenczi
+ werner mendizabal
Wout Standaert
Yu He
diff --git a/core/array.cpp b/core/array.cpp
index 0ddac1662c..9e3250fd47 100644
--- a/core/array.cpp
+++ b/core/array.cpp
@@ -35,8 +35,8 @@
#include "variant.h"
#include "vector.h"
-struct ArrayPrivate {
-
+class ArrayPrivate {
+public:
SafeRefCount refcount;
Vector<Variant> array;
};
@@ -211,13 +211,13 @@ const Variant &Array::get(int p_idx) const {
return operator[](p_idx);
}
-Array Array::duplicate() const {
+Array Array::duplicate(bool p_deep) const {
Array new_arr;
int element_count = size();
new_arr.resize(element_count);
for (int i = 0; i < element_count; i++) {
- new_arr[i] = get(i);
+ new_arr[i] = p_deep ? get(i).duplicate(p_deep) : get(i);
}
return new_arr;
diff --git a/core/array.h b/core/array.h
index 684a8e265d..e549a886e6 100644
--- a/core/array.h
+++ b/core/array.h
@@ -88,7 +88,7 @@ public:
Variant pop_back();
Variant pop_front();
- Array duplicate() const;
+ Array duplicate(bool p_deep = false) const;
Array(const Array &p_from);
Array();
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index 20adf7ff02..85793f127d 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -288,6 +288,10 @@ void _OS::set_window_size(const Size2 &p_size) {
OS::get_singleton()->set_window_size(p_size);
}
+Rect2 _OS::get_window_safe_area() const {
+ return OS::get_singleton()->get_window_safe_area();
+}
+
void _OS::set_window_fullscreen(bool p_enabled) {
OS::get_singleton()->set_window_fullscreen(p_enabled);
}
@@ -1032,9 +1036,9 @@ void _OS::_bind_methods() {
//ClassDB::bind_method(D_METHOD("get_fullscreen_mode_list","screen"),&_OS::get_fullscreen_mode_list,DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_video_driver_count"), &_OS::get_video_driver_count);
- ClassDB::bind_method(D_METHOD("get_video_driver_name"), &_OS::get_video_driver_name);
+ ClassDB::bind_method(D_METHOD("get_video_driver_name", "driver"), &_OS::get_video_driver_name);
ClassDB::bind_method(D_METHOD("get_audio_driver_count"), &_OS::get_audio_driver_count);
- ClassDB::bind_method(D_METHOD("get_audio_driver_name"), &_OS::get_audio_driver_name);
+ ClassDB::bind_method(D_METHOD("get_audio_driver_name", "driver"), &_OS::get_audio_driver_name);
ClassDB::bind_method(D_METHOD("get_screen_count"), &_OS::get_screen_count);
ClassDB::bind_method(D_METHOD("get_current_screen"), &_OS::get_current_screen);
@@ -1046,6 +1050,7 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_window_position", "position"), &_OS::set_window_position);
ClassDB::bind_method(D_METHOD("get_window_size"), &_OS::get_window_size);
ClassDB::bind_method(D_METHOD("set_window_size", "size"), &_OS::set_window_size);
+ ClassDB::bind_method(D_METHOD("get_window_safe_area"), &_OS::get_window_safe_area);
ClassDB::bind_method(D_METHOD("set_window_fullscreen", "enabled"), &_OS::set_window_fullscreen);
ClassDB::bind_method(D_METHOD("is_window_fullscreen"), &_OS::is_window_fullscreen);
ClassDB::bind_method(D_METHOD("set_window_resizable", "enabled"), &_OS::set_window_resizable);
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index c40abd51f6..1790c68757 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -162,6 +162,7 @@ public:
virtual void set_window_position(const Point2 &p_position);
virtual Size2 get_window_size() const;
virtual Size2 get_real_window_size() const;
+ virtual Rect2 get_window_safe_area() const;
virtual void set_window_size(const Size2 &p_size);
virtual void set_window_fullscreen(bool p_enabled);
virtual bool is_window_fullscreen() const;
diff --git a/core/class_db.cpp b/core/class_db.cpp
index 291dc87e1c..92aa131e2d 100644
--- a/core/class_db.cpp
+++ b/core/class_db.cpp
@@ -651,7 +651,6 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName
}
type->constant_map[p_name] = p_constant;
-#ifdef DEBUG_METHODS_ENABLED
String enum_name = p_enum;
if (enum_name != String()) {
@@ -670,6 +669,7 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName
}
}
+#ifdef DEBUG_METHODS_ENABLED
type->constant_order.push_back(p_name);
#endif
}
@@ -725,7 +725,6 @@ int ClassDB::get_integer_constant(const StringName &p_class, const StringName &p
return 0;
}
-#ifdef DEBUG_METHODS_ENABLED
StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) {
OBJTYPE_RLOCK;
@@ -794,7 +793,6 @@ void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_
type = type->inherits_ptr;
}
}
-#endif
void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) {
diff --git a/core/class_db.h b/core/class_db.h
index d74317239b..2c77ffe65f 100644
--- a/core/class_db.h
+++ b/core/class_db.h
@@ -116,10 +116,10 @@ public:
ClassInfo *inherits_ptr;
HashMap<StringName, MethodBind *, StringNameHasher> method_map;
HashMap<StringName, int, StringNameHasher> constant_map;
+ HashMap<StringName, List<StringName> > enum_map;
HashMap<StringName, MethodInfo, StringNameHasher> signal_map;
List<PropertyInfo> property_list;
#ifdef DEBUG_METHODS_ENABLED
- HashMap<StringName, List<StringName> > enum_map;
List<StringName> constant_order;
List<StringName> method_order;
Set<StringName> methods_in_properties;
@@ -344,11 +344,9 @@ public:
static void get_integer_constant_list(const StringName &p_class, List<String> *p_constants, bool p_no_inheritance = false);
static int get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success = NULL);
-#ifdef DEBUG_METHODS_ENABLED
static StringName get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
static void get_enum_list(const StringName &p_class, List<StringName> *p_enums, bool p_no_inheritance = false);
static void get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance = false);
-#endif
static StringName get_category(const StringName &p_node);
diff --git a/core/dictionary.cpp b/core/dictionary.cpp
index e3f4aa5f28..ba0de95861 100644
--- a/core/dictionary.cpp
+++ b/core/dictionary.cpp
@@ -211,7 +211,7 @@ const Variant *Dictionary::next(const Variant *p_key) const {
return NULL;
}
-Dictionary Dictionary::duplicate() const {
+Dictionary Dictionary::duplicate(bool p_deep) const {
Dictionary n;
@@ -219,7 +219,7 @@ Dictionary Dictionary::duplicate() const {
get_key_list(&keys);
for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- n[E->get()] = operator[](E->get());
+ n[E->get()] = p_deep ? operator[](E->get()).duplicate(p_deep) : operator[](E->get());
}
return n;
diff --git a/core/dictionary.h b/core/dictionary.h
index f001f2d5e1..9eef265d5b 100644
--- a/core/dictionary.h
+++ b/core/dictionary.h
@@ -75,7 +75,7 @@ public:
Array keys() const;
Array values() const;
- Dictionary duplicate() const;
+ Dictionary duplicate(bool p_deep = false) const;
Dictionary(const Dictionary &p_from);
Dictionary();
diff --git a/core/image.cpp b/core/image.cpp
index 07e705265d..2ac8ffea56 100644
--- a/core/image.cpp
+++ b/core/image.cpp
@@ -1077,61 +1077,29 @@ Error Image::generate_mipmaps() {
PoolVector<uint8_t>::Write wp = data.write();
- if (next_power_of_2(width) == uint32_t(width) && next_power_of_2(height) == uint32_t(height)) {
- //use fast code for powers of 2
- int prev_ofs = 0;
- int prev_h = height;
- int prev_w = width;
+ int prev_ofs = 0;
+ int prev_h = height;
+ int prev_w = width;
- for (int i = 1; i < mmcount; i++) {
+ for (int i = 1; i < mmcount; i++) {
- int ofs, w, h;
- _get_mipmap_offset_and_size(i, ofs, w, h);
+ int ofs, w, h;
+ _get_mipmap_offset_and_size(i, ofs, w, h);
- switch (format) {
+ switch (format) {
- case FORMAT_L8:
- case FORMAT_R8: _generate_po2_mipmap<1>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break;
- case FORMAT_LA8:
- case FORMAT_RG8: _generate_po2_mipmap<2>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break;
- case FORMAT_RGB8: _generate_po2_mipmap<3>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break;
- case FORMAT_RGBA8: _generate_po2_mipmap<4>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break;
- default: {}
- }
-
- prev_ofs = ofs;
- prev_w = w;
- prev_h = h;
+ case FORMAT_L8:
+ case FORMAT_R8: _generate_po2_mipmap<1>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break;
+ case FORMAT_LA8:
+ case FORMAT_RG8: _generate_po2_mipmap<2>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break;
+ case FORMAT_RGB8: _generate_po2_mipmap<3>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break;
+ case FORMAT_RGBA8: _generate_po2_mipmap<4>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break;
+ default: {}
}
- } else {
- //use slow code..
-
- //use bilinear filtered code for non powers of 2
- int prev_ofs = 0;
- int prev_h = height;
- int prev_w = width;
-
- for (int i = 1; i < mmcount; i++) {
-
- int ofs, w, h;
- _get_mipmap_offset_and_size(i, ofs, w, h);
-
- switch (format) {
-
- case FORMAT_L8:
- case FORMAT_R8: _scale_bilinear<1>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h, w, h); break;
- case FORMAT_LA8:
- case FORMAT_RG8: _scale_bilinear<2>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h, w, h); break;
- case FORMAT_RGB8: _scale_bilinear<3>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h, w, h); break;
- case FORMAT_RGBA8: _scale_bilinear<4>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h, w, h); break;
- default: {}
- }
-
- prev_ofs = ofs;
- prev_w = w;
- prev_h = h;
- }
+ prev_ofs = ofs;
+ prev_w = w;
+ prev_h = h;
}
mipmaps = true;
@@ -2271,6 +2239,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("premultiply_alpha"), &Image::premultiply_alpha);
ClassDB::bind_method(D_METHOD("srgb_to_linear"), &Image::srgb_to_linear);
ClassDB::bind_method(D_METHOD("normalmap_to_xy"), &Image::normalmap_to_xy);
+ ClassDB::bind_method(D_METHOD("bumpmap_to_normalmap", "bump_scale"), &Image::bumpmap_to_normalmap, DEFVAL(1.0));
ClassDB::bind_method(D_METHOD("blit_rect", "src", "src_rect", "dst"), &Image::blit_rect);
ClassDB::bind_method(D_METHOD("blit_rect_mask", "src", "mask", "src_rect", "dst"), &Image::blit_rect_mask);
@@ -2379,6 +2348,47 @@ void Image::normalmap_to_xy() {
convert(Image::FORMAT_LA8);
}
+void Image::bumpmap_to_normalmap(float bump_scale) {
+ ERR_FAIL_COND(!_can_modify(format));
+ convert(Image::FORMAT_RF);
+
+ PoolVector<uint8_t> result_image; //rgba output
+ result_image.resize(width * height * 4);
+
+ {
+ PoolVector<uint8_t>::Read rp = data.read();
+ PoolVector<uint8_t>::Write wp = result_image.write();
+
+ unsigned char *write_ptr = wp.ptr();
+ float *read_ptr = (float *)rp.ptr();
+
+ for (int ty = 0; ty < height; ty++) {
+ int py = ty + 1;
+ if (py >= height) py -= height;
+
+ for (int tx = 0; tx < width; tx++) {
+ int px = tx + 1;
+ if (px >= width) px -= width;
+ float here = read_ptr[ty * width + tx];
+ float to_right = read_ptr[ty * width + px];
+ float above = read_ptr[py * width + tx];
+ Vector3 up = Vector3(0, 1, (here - above) * bump_scale);
+ Vector3 across = Vector3(1, 0, (to_right - here) * bump_scale);
+
+ Vector3 normal = across.cross(up);
+ normal.normalize();
+
+ write_ptr[((ty * width + tx) << 2) + 0] = (127.5 + normal.x * 127.5);
+ write_ptr[((ty * width + tx) << 2) + 1] = (127.5 + normal.y * 127.5);
+ write_ptr[((ty * width + tx) << 2) + 2] = (127.5 + normal.z * 127.5);
+ write_ptr[((ty * width + tx) << 2) + 3] = 255;
+ }
+ }
+ }
+ format = FORMAT_RGBA8;
+ data = result_image;
+}
+
void Image::srgb_to_linear() {
if (data.size() == 0)
diff --git a/core/image.h b/core/image.h
index e962787ae9..17477d88ea 100644
--- a/core/image.h
+++ b/core/image.h
@@ -284,6 +284,7 @@ public:
void premultiply_alpha();
void srgb_to_linear();
void normalmap_to_xy();
+ void bumpmap_to_normalmap(float bump_scale = 1.0);
void blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest);
void blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest);
diff --git a/core/input_map.cpp b/core/input_map.cpp
index bd03d61196..ea724d2595 100644
--- a/core/input_map.cpp
+++ b/core/input_map.cpp
@@ -35,6 +35,8 @@
InputMap *InputMap::singleton = NULL;
+int InputMap::ALL_DEVICES = -1;
+
void InputMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_action", "action"), &InputMap::has_action);
@@ -103,10 +105,10 @@ List<Ref<InputEvent> >::Element *InputMap::_find_event(List<Ref<InputEvent> > &p
//if (e.type != Ref<InputEvent>::KEY && e.device != p_event.device) -- unsure about the KEY comparison, why is this here?
// continue;
- if (e->get_device() != p_event->get_device())
- continue;
- if (e->action_match(p_event))
- return E;
+ int device = e->get_device();
+ if (device == ALL_DEVICES || device == p_event->get_device())
+ if (e->action_match(p_event))
+ return E;
}
return NULL;
diff --git a/core/input_map.h b/core/input_map.h
index 84d90f6f2a..9f3c13c2cf 100644
--- a/core/input_map.h
+++ b/core/input_map.h
@@ -39,6 +39,11 @@ class InputMap : public Object {
GDCLASS(InputMap, Object);
public:
+ /**
+ * A special value used to signify that a given Action can be triggered by any device
+ */
+ static int ALL_DEVICES;
+
struct Action {
int id;
List<Ref<InputEvent> > inputs;
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 4d72f744e1..c787b7ec4c 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -618,7 +618,27 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) {
String query = "";
Array keys = p_dict.keys();
for (int i = 0; i < keys.size(); ++i) {
- query += "&" + String(keys[i]).http_escape() + "=" + String(p_dict[keys[i]]).http_escape();
+ String encoded_key = String(keys[i]).percent_encode();
+ Variant value = p_dict[keys[i]];
+ switch (value.get_type()) {
+ case Variant::ARRAY: {
+ // Repeat the key with every values
+ Array values = value;
+ for (int j = 0; j < values.size(); ++j) {
+ query += "&" + encoded_key + "=" + String(values[j]).percent_encode();
+ }
+ break;
+ }
+ case Variant::NIL: {
+ // Add the key with no value
+ query += "&" + encoded_key;
+ break;
+ }
+ default: {
+ // Add the key-value pair
+ query += "&" + encoded_key + "=" + String(value).percent_encode();
+ }
+ }
}
query.erase(0, 1);
return query;
diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp
new file mode 100644
index 0000000000..cbe7f87d92
--- /dev/null
+++ b/core/io/multiplayer_api.cpp
@@ -0,0 +1,722 @@
+#include "core/io/multiplayer_api.h"
+#include "core/io/marshalls.h"
+#include "scene/main/node.h"
+
+void MultiplayerAPI::poll() {
+
+ if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED)
+ return;
+
+ network_peer->poll();
+
+ if (!network_peer.is_valid()) //it's possible that polling might have resulted in a disconnection, so check here
+ return;
+
+ while (network_peer->get_available_packet_count()) {
+
+ int sender = network_peer->get_packet_peer();
+ const uint8_t *packet;
+ int len;
+
+ Error err = network_peer->get_packet(&packet, len);
+ if (err != OK) {
+ ERR_PRINT("Error getting packet!");
+ }
+
+ rpc_sender_id = sender;
+ _process_packet(sender, packet, len);
+ rpc_sender_id = 0;
+
+ if (!network_peer.is_valid()) {
+ break; //it's also possible that a packet or RPC caused a disconnection, so also check here
+ }
+ }
+}
+
+void MultiplayerAPI::clear() {
+ connected_peers.clear();
+ path_get_cache.clear();
+ path_send_cache.clear();
+ last_send_cache_id = 1;
+}
+
+void MultiplayerAPI::set_root_node(Node *p_node) {
+ root_node = p_node;
+}
+
+void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer) {
+
+ if (network_peer.is_valid()) {
+ network_peer->disconnect("peer_connected", this, "add_peer");
+ network_peer->disconnect("peer_disconnected", this, "del_peer");
+ network_peer->disconnect("connection_succeeded", this, "connected_to_server");
+ network_peer->disconnect("connection_failed", this, "connection_failed");
+ network_peer->disconnect("server_disconnected", this, "server_disconnected");
+ clear();
+ }
+
+ network_peer = p_peer;
+
+ ERR_EXPLAIN("Supplied NetworkedNetworkPeer must be connecting or connected.");
+ ERR_FAIL_COND(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED);
+
+ if (network_peer.is_valid()) {
+ network_peer->connect("peer_connected", this, "add_peer");
+ network_peer->connect("peer_disconnected", this, "del_peer");
+ network_peer->connect("connection_succeeded", this, "connected_to_server");
+ network_peer->connect("connection_failed", this, "connection_failed");
+ network_peer->connect("server_disconnected", this, "server_disconnected");
+ }
+}
+
+Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const {
+ return network_peer;
+}
+
+void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
+
+ ERR_FAIL_COND(root_node == NULL);
+ ERR_FAIL_COND(p_packet_len < 5);
+
+ uint8_t packet_type = p_packet[0];
+
+ switch (packet_type) {
+
+ case NETWORK_COMMAND_SIMPLIFY_PATH: {
+
+ _process_simplify_path(p_from, p_packet, p_packet_len);
+ } break;
+
+ case NETWORK_COMMAND_CONFIRM_PATH: {
+
+ _process_confirm_path(p_from, p_packet, p_packet_len);
+ } break;
+
+ case NETWORK_COMMAND_REMOTE_CALL:
+ case NETWORK_COMMAND_REMOTE_SET: {
+
+ ERR_FAIL_COND(p_packet_len < 6);
+
+ Node *node = _process_get_node(p_from, p_packet, p_packet_len);
+
+ ERR_FAIL_COND(node == NULL);
+
+ //detect cstring end
+ int len_end = 5;
+ for (; len_end < p_packet_len; len_end++) {
+ if (p_packet[len_end] == 0) {
+ break;
+ }
+ }
+
+ ERR_FAIL_COND(len_end >= p_packet_len);
+
+ StringName name = String::utf8((const char *)&p_packet[5]);
+
+ if (packet_type == NETWORK_COMMAND_REMOTE_CALL) {
+
+ _process_rpc(node, name, p_from, p_packet, p_packet_len, len_end + 1);
+
+ } else {
+
+ _process_rset(node, name, p_from, p_packet, p_packet_len, len_end + 1);
+ }
+
+ } break;
+ }
+}
+
+Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int p_packet_len) {
+
+ uint32_t target = decode_uint32(&p_packet[1]);
+ Node *node = NULL;
+
+ if (target & 0x80000000) {
+ //use full path (not cached yet)
+
+ int ofs = target & 0x7FFFFFFF;
+ ERR_FAIL_COND_V(ofs >= p_packet_len, NULL);
+
+ String paths;
+ paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs);
+
+ NodePath np = paths;
+
+ node = root_node->get_node(np);
+
+ if (!node)
+ ERR_PRINTS("Failed to get path from RPC: " + String(np));
+ } else {
+ //use cached path
+ int id = target;
+
+ Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from);
+ ERR_FAIL_COND_V(!E, NULL);
+
+ Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id);
+ ERR_FAIL_COND_V(!F, NULL);
+
+ PathGetCache::NodeInfo *ni = &F->get();
+ //do proper caching later
+
+ node = root_node->get_node(ni->path);
+ if (!node)
+ ERR_PRINTS("Failed to get cached path from RPC: " + String(ni->path));
+ }
+ return node;
+}
+
+void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
+ if (!p_node->can_call_rpc(p_name, p_from))
+ return;
+
+ ERR_FAIL_COND(p_offset >= p_packet_len);
+
+ int argc = p_packet[p_offset];
+ Vector<Variant> args;
+ Vector<const Variant *> argp;
+ args.resize(argc);
+ argp.resize(argc);
+
+ p_offset++;
+
+ for (int i = 0; i < argc; i++) {
+
+ ERR_FAIL_COND(p_offset >= p_packet_len);
+ int vlen;
+ Error err = decode_variant(args[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen);
+ ERR_FAIL_COND(err != OK);
+ //args[i]=p_packet[3+i];
+ argp[i] = &args[i];
+ p_offset += vlen;
+ }
+
+ Variant::CallError ce;
+
+ p_node->call(p_name, (const Variant **)argp.ptr(), argc, ce);
+ if (ce.error != Variant::CallError::CALL_OK) {
+ String error = Variant::get_call_error_text(p_node, p_name, (const Variant **)argp.ptr(), argc, ce);
+ error = "RPC - " + error;
+ ERR_PRINTS(error);
+ }
+}
+
+void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
+
+ if (!p_node->can_call_rset(p_name, p_from))
+ return;
+
+ ERR_FAIL_COND(p_offset >= p_packet_len);
+
+ Variant value;
+ decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset);
+
+ bool valid;
+
+ p_node->set(p_name, value, &valid);
+ if (!valid) {
+ String error = "Error setting remote property '" + String(p_name) + "', not found in object of type " + p_node->get_class();
+ ERR_PRINTS(error);
+ }
+}
+
+void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
+
+ ERR_FAIL_COND(p_packet_len < 5);
+ int id = decode_uint32(&p_packet[1]);
+
+ String paths;
+ paths.parse_utf8((const char *)&p_packet[5], p_packet_len - 5);
+
+ NodePath path = paths;
+
+ if (!path_get_cache.has(p_from)) {
+ path_get_cache[p_from] = PathGetCache();
+ }
+
+ PathGetCache::NodeInfo ni;
+ ni.path = path;
+ ni.instance = 0;
+
+ path_get_cache[p_from].nodes[id] = ni;
+
+ //send ack
+
+ //encode path
+ CharString pname = String(path).utf8();
+ int len = encode_cstring(pname.get_data(), NULL);
+
+ Vector<uint8_t> packet;
+
+ packet.resize(1 + len);
+ packet[0] = NETWORK_COMMAND_CONFIRM_PATH;
+ encode_cstring(pname.get_data(), &packet[1]);
+
+ network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
+ network_peer->set_target_peer(p_from);
+ network_peer->put_packet(packet.ptr(), packet.size());
+}
+
+void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
+
+ String paths;
+ paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1);
+
+ NodePath path = paths;
+
+ PathSentCache *psc = path_send_cache.getptr(path);
+ ERR_FAIL_COND(!psc);
+
+ Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from);
+ ERR_FAIL_COND(!E);
+ E->get() = true;
+}
+
+bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int p_target) {
+ bool has_all_peers = true;
+ List<int> peers_to_add; //if one is missing, take note to add it
+
+ for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
+
+ if (p_target < 0 && E->get() == -p_target)
+ continue; //continue, excluded
+
+ if (p_target > 0 && E->get() != p_target)
+ continue; //continue, not for this peer
+
+ Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get());
+
+ if (!F || F->get() == false) {
+ //path was not cached, or was cached but is unconfirmed
+ if (!F) {
+ //not cached at all, take note
+ peers_to_add.push_back(E->get());
+ }
+
+ has_all_peers = false;
+ }
+ }
+
+ //those that need to be added, send a message for this
+
+ for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) {
+
+ //encode function name
+ CharString pname = String(p_path).utf8();
+ int len = encode_cstring(pname.get_data(), NULL);
+
+ Vector<uint8_t> packet;
+
+ packet.resize(1 + 4 + len);
+ packet[0] = NETWORK_COMMAND_SIMPLIFY_PATH;
+ encode_uint32(psc->id, &packet[1]);
+ encode_cstring(pname.get_data(), &packet[5]);
+
+ network_peer->set_target_peer(E->get()); //to all of you
+ network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
+ network_peer->put_packet(packet.ptr(), packet.size());
+
+ psc->confirmed_peers.insert(E->get(), false); //insert into confirmed, but as false since it was not confirmed
+ }
+
+ return has_all_peers;
+}
+
+void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount) {
+
+ if (network_peer.is_null()) {
+ ERR_EXPLAIN("Attempt to remote call/set when networking is not active in SceneTree.");
+ ERR_FAIL();
+ }
+
+ if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING) {
+ ERR_EXPLAIN("Attempt to remote call/set when networking is not connected yet in SceneTree.");
+ ERR_FAIL();
+ }
+
+ if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) {
+ ERR_EXPLAIN("Attempt to remote call/set when networking is disconnected.");
+ ERR_FAIL();
+ }
+
+ if (p_argcount > 255) {
+ ERR_EXPLAIN("Too many arguments >255.");
+ ERR_FAIL();
+ }
+
+ if (p_to != 0 && !connected_peers.has(ABS(p_to))) {
+ if (p_to == network_peer->get_unique_id()) {
+ ERR_EXPLAIN("Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id()));
+ } else {
+ ERR_EXPLAIN("Attempt to remote call unexisting ID: " + itos(p_to));
+ }
+
+ ERR_FAIL();
+ }
+
+ NodePath from_path = (root_node->get_path()).rel_path_to(p_from->get_path());
+ ERR_FAIL_COND(from_path.is_empty());
+
+ //see if the path is cached
+ PathSentCache *psc = path_send_cache.getptr(from_path);
+ if (!psc) {
+ //path is not cached, create
+ path_send_cache[from_path] = PathSentCache();
+ psc = path_send_cache.getptr(from_path);
+ psc->id = last_send_cache_id++;
+ }
+
+ //create base packet, lots of hardcode because it must be tight
+
+ int ofs = 0;
+
+#define MAKE_ROOM(m_amount) \
+ if (packet_cache.size() < m_amount) packet_cache.resize(m_amount);
+
+ //encode type
+ MAKE_ROOM(1);
+ packet_cache[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL;
+ ofs += 1;
+
+ //encode ID
+ MAKE_ROOM(ofs + 4);
+ encode_uint32(psc->id, &(packet_cache[ofs]));
+ ofs += 4;
+
+ //encode function name
+ CharString name = String(p_name).utf8();
+ int len = encode_cstring(name.get_data(), NULL);
+ MAKE_ROOM(ofs + len);
+ encode_cstring(name.get_data(), &(packet_cache[ofs]));
+ ofs += len;
+
+ if (p_set) {
+ //set argument
+ Error err = encode_variant(*p_arg[0], NULL, len);
+ ERR_FAIL_COND(err != OK);
+ MAKE_ROOM(ofs + len);
+ encode_variant(*p_arg[0], &(packet_cache[ofs]), len);
+ ofs += len;
+
+ } else {
+ //call arguments
+ MAKE_ROOM(ofs + 1);
+ packet_cache[ofs] = p_argcount;
+ ofs += 1;
+ for (int i = 0; i < p_argcount; i++) {
+ Error err = encode_variant(*p_arg[i], NULL, len);
+ ERR_FAIL_COND(err != OK);
+ MAKE_ROOM(ofs + len);
+ encode_variant(*p_arg[i], &(packet_cache[ofs]), len);
+ ofs += len;
+ }
+ }
+
+ //see if all peers have cached path (is so, call can be fast)
+ bool has_all_peers = _send_confirm_path(from_path, psc, p_to);
+
+ //take chance and set transfer mode, since all send methods will use it
+ network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
+
+ if (has_all_peers) {
+
+ //they all have verified paths, so send fast
+ network_peer->set_target_peer(p_to); //to all of you
+ network_peer->put_packet(packet_cache.ptr(), ofs); //a message with love
+ } else {
+ //not all verified path, so send one by one
+
+ //apend path at the end, since we will need it for some packets
+ CharString pname = String(from_path).utf8();
+ int path_len = encode_cstring(pname.get_data(), NULL);
+ MAKE_ROOM(ofs + path_len);
+ encode_cstring(pname.get_data(), &(packet_cache[ofs]));
+
+ for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
+
+ if (p_to < 0 && E->get() == -p_to)
+ continue; //continue, excluded
+
+ if (p_to > 0 && E->get() != p_to)
+ continue; //continue, not for this peer
+
+ Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get());
+ ERR_CONTINUE(!F); //should never happen
+
+ network_peer->set_target_peer(E->get()); //to this one specifically
+
+ if (F->get() == true) {
+ //this one confirmed path, so use id
+ encode_uint32(psc->id, &(packet_cache[1]));
+ network_peer->put_packet(packet_cache.ptr(), ofs);
+ } else {
+ //this one did not confirm path yet, so use entire path (sorry!)
+ encode_uint32(0x80000000 | ofs, &(packet_cache[1])); //offset to path and flag
+ network_peer->put_packet(packet_cache.ptr(), ofs + path_len);
+ }
+ }
+ }
+}
+
+void MultiplayerAPI::add_peer(int p_id) {
+ connected_peers.insert(p_id);
+ path_get_cache.insert(p_id, PathGetCache());
+ emit_signal("network_peer_connected", p_id);
+}
+
+void MultiplayerAPI::del_peer(int p_id) {
+ connected_peers.erase(p_id);
+ path_get_cache.erase(p_id); //I no longer need your cache, sorry
+ emit_signal("network_peer_disconnected", p_id);
+}
+
+void MultiplayerAPI::connected_to_server() {
+
+ emit_signal("connected_to_server");
+}
+
+void MultiplayerAPI::connection_failed() {
+
+ emit_signal("connection_failed");
+}
+
+void MultiplayerAPI::server_disconnected() {
+
+ emit_signal("server_disconnected");
+}
+
+bool _should_call_native(Node::RPCMode mode, bool is_master, bool &r_skip_rpc) {
+
+ switch (mode) {
+
+ case Node::RPC_MODE_DISABLED: {
+ //do nothing
+ } break;
+ case Node::RPC_MODE_REMOTE: {
+ //do nothing also, no need to call local
+ } break;
+ case Node::RPC_MODE_SYNC: {
+ //call it, sync always results in call
+ return true;
+ } break;
+ case Node::RPC_MODE_MASTER: {
+ if (is_master)
+ r_skip_rpc = true; //no other master so..
+ return is_master;
+ } break;
+ case Node::RPC_MODE_SLAVE: {
+ return !is_master;
+ } break;
+ }
+ return false;
+}
+
+bool _should_call_script(ScriptInstance::RPCMode mode, bool is_master, bool &r_skip_rpc) {
+ switch (mode) {
+
+ case ScriptInstance::RPC_MODE_DISABLED: {
+ //do nothing
+ } break;
+ case ScriptInstance::RPC_MODE_REMOTE: {
+ //do nothing also, no need to call local
+ } break;
+ case ScriptInstance::RPC_MODE_SYNC: {
+ //call it, sync always results in call
+ return true;
+ } break;
+ case ScriptInstance::RPC_MODE_MASTER: {
+ if (is_master)
+ r_skip_rpc = true; //no other master so..
+ return is_master;
+ } break;
+ case ScriptInstance::RPC_MODE_SLAVE: {
+ return !is_master;
+ } break;
+ }
+ return false;
+}
+
+void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) {
+
+ ERR_FAIL_COND(!p_node->is_inside_tree());
+ ERR_FAIL_COND(!network_peer.is_valid());
+
+ int node_id = network_peer->get_unique_id();
+ bool skip_rpc = false;
+ bool call_local_native = false;
+ bool call_local_script = false;
+ bool is_master = p_node->is_network_master();
+
+ if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
+ //check that send mode can use local call
+
+ const Map<StringName, Node::RPCMode>::Element *E = p_node->get_node_rpc_mode(p_method);
+ if (E) {
+ call_local_native = _should_call_native(E->get(), is_master, skip_rpc);
+ }
+
+ if (call_local_native) {
+ // done below
+ } else if (p_node->get_script_instance()) {
+ //attempt with script
+ ScriptInstance::RPCMode rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method);
+ call_local_script = _should_call_script(rpc_mode, is_master, skip_rpc);
+ }
+ }
+
+ if (!skip_rpc) {
+ _send_rpc(p_node, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount);
+ }
+
+ if (call_local_native) {
+ Variant::CallError ce;
+ p_node->call(p_method, p_arg, p_argcount, ce);
+ if (ce.error != Variant::CallError::CALL_OK) {
+ String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
+ error = "rpc() aborted in local call: - " + error;
+ ERR_PRINTS(error);
+ return;
+ }
+ }
+
+ if (call_local_script) {
+ Variant::CallError ce;
+ ce.error = Variant::CallError::CALL_OK;
+ p_node->get_script_instance()->call(p_method, p_arg, p_argcount, ce);
+ if (ce.error != Variant::CallError::CALL_OK) {
+ String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
+ error = "rpc() aborted in script local call: - " + error;
+ ERR_PRINTS(error);
+ return;
+ }
+ }
+}
+
+void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) {
+
+ ERR_FAIL_COND(!p_node->is_inside_tree());
+ ERR_FAIL_COND(!network_peer.is_valid());
+
+ int node_id = network_peer->get_unique_id();
+ bool is_master = p_node->is_network_master();
+ bool skip_rset = false;
+
+ if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
+ //check that send mode can use local call
+
+ bool set_local = false;
+
+ const Map<StringName, Node::RPCMode>::Element *E = p_node->get_node_rset_mode(p_property);
+ if (E) {
+
+ set_local = _should_call_native(E->get(), is_master, skip_rset);
+ }
+
+ if (set_local) {
+ bool valid;
+ p_node->set(p_property, p_value, &valid);
+
+ if (!valid) {
+ String error = "rset() aborted in local set, property not found: - " + String(p_property);
+ ERR_PRINTS(error);
+ return;
+ }
+ } else if (p_node->get_script_instance()) {
+ //attempt with script
+ ScriptInstance::RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property);
+
+ set_local = _should_call_script(rpc_mode, is_master, skip_rset);
+
+ if (set_local) {
+
+ bool valid = p_node->get_script_instance()->set(p_property, p_value);
+
+ if (!valid) {
+ String error = "rset() aborted in local script set, property not found: - " + String(p_property);
+ ERR_PRINTS(error);
+ return;
+ }
+ }
+ }
+ }
+
+ if (skip_rset)
+ return;
+
+ const Variant *vptr = &p_value;
+
+ _send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1);
+}
+
+int MultiplayerAPI::get_network_unique_id() const {
+
+ ERR_FAIL_COND_V(!network_peer.is_valid(), 0);
+ return network_peer->get_unique_id();
+}
+
+bool MultiplayerAPI::is_network_server() const {
+
+ ERR_FAIL_COND_V(!network_peer.is_valid(), false);
+ return network_peer->is_server();
+}
+
+void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) {
+
+ ERR_FAIL_COND(!network_peer.is_valid());
+ network_peer->set_refuse_new_connections(p_refuse);
+}
+
+bool MultiplayerAPI::is_refusing_new_network_connections() const {
+
+ ERR_FAIL_COND_V(!network_peer.is_valid(), false);
+ return network_peer->is_refusing_new_connections();
+}
+
+Vector<int> MultiplayerAPI::get_network_connected_peers() const {
+
+ ERR_FAIL_COND_V(!network_peer.is_valid(), Vector<int>());
+
+ Vector<int> ret;
+ for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
+ ret.push_back(E->get());
+ }
+
+ return ret;
+}
+
+void MultiplayerAPI::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node);
+ ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer);
+ ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer);
+ ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id);
+ ClassDB::bind_method(D_METHOD("is_network_server"), &MultiplayerAPI::is_network_server);
+ ClassDB::bind_method(D_METHOD("get_rpc_sender_id"), &MultiplayerAPI::get_rpc_sender_id);
+ ClassDB::bind_method(D_METHOD("add_peer", "id"), &MultiplayerAPI::add_peer);
+ ClassDB::bind_method(D_METHOD("del_peer", "id"), &MultiplayerAPI::del_peer);
+ ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &MultiplayerAPI::set_network_peer);
+ ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll);
+ ClassDB::bind_method(D_METHOD("clear"), &MultiplayerAPI::clear);
+
+ ClassDB::bind_method(D_METHOD("connected_to_server"), &MultiplayerAPI::connected_to_server);
+ ClassDB::bind_method(D_METHOD("connection_failed"), &MultiplayerAPI::connection_failed);
+ ClassDB::bind_method(D_METHOD("server_disconnected"), &MultiplayerAPI::server_disconnected);
+ ClassDB::bind_method(D_METHOD("get_network_connected_peers"), &MultiplayerAPI::get_network_connected_peers);
+ ClassDB::bind_method(D_METHOD("set_refuse_new_network_connections", "refuse"), &MultiplayerAPI::set_refuse_new_network_connections);
+ ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", 0), "set_network_peer", "get_network_peer");
+
+ ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("connected_to_server"));
+ ADD_SIGNAL(MethodInfo("connection_failed"));
+ ADD_SIGNAL(MethodInfo("server_disconnected"));
+}
+
+MultiplayerAPI::MultiplayerAPI() {
+ clear();
+}
+
+MultiplayerAPI::~MultiplayerAPI() {
+ clear();
+}
diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h
new file mode 100644
index 0000000000..e7c6ffbea6
--- /dev/null
+++ b/core/io/multiplayer_api.h
@@ -0,0 +1,87 @@
+#ifndef MULTIPLAYER_PROTOCOL_H
+#define MULTIPLAYER_PROTOCOL_H
+
+#include "core/io/networked_multiplayer_peer.h"
+#include "core/reference.h"
+
+class MultiplayerAPI : public Reference {
+
+ GDCLASS(MultiplayerAPI, Reference);
+
+private:
+ //path sent caches
+ struct PathSentCache {
+ Map<int, bool> confirmed_peers;
+ int id;
+ };
+
+ //path get caches
+ struct PathGetCache {
+ struct NodeInfo {
+ NodePath path;
+ ObjectID instance;
+ };
+
+ Map<int, NodeInfo> nodes;
+ };
+
+ Ref<NetworkedMultiplayerPeer> network_peer;
+ int rpc_sender_id;
+ Set<int> connected_peers;
+ HashMap<NodePath, PathSentCache> path_send_cache;
+ Map<int, PathGetCache> path_get_cache;
+ int last_send_cache_id;
+ Vector<uint8_t> packet_cache;
+ Node *root_node;
+
+protected:
+ static void _bind_methods();
+
+ void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len);
+ void _process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len);
+ void _process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len);
+ Node *_process_get_node(int p_from, const uint8_t *p_packet, int p_packet_len);
+ void _process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
+ void _process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
+
+ void _send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount);
+ bool _send_confirm_path(NodePath p_path, PathSentCache *psc, int p_from);
+
+public:
+ enum NetworkCommands {
+ NETWORK_COMMAND_REMOTE_CALL,
+ NETWORK_COMMAND_REMOTE_SET,
+ NETWORK_COMMAND_SIMPLIFY_PATH,
+ NETWORK_COMMAND_CONFIRM_PATH,
+ };
+
+ void poll();
+ void clear();
+ void set_root_node(Node *p_node);
+ void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer);
+ Ref<NetworkedMultiplayerPeer> get_network_peer() const;
+
+ // Called by Node.rpc
+ void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount);
+ // Called by Node.rset
+ void rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value);
+
+ void add_peer(int p_id);
+ void del_peer(int p_id);
+ void connected_to_server();
+ void connection_failed();
+ void server_disconnected();
+
+ bool has_network_peer() const { return network_peer.is_valid(); }
+ Vector<int> get_network_connected_peers() const;
+ int get_rpc_sender_id() const { return rpc_sender_id; }
+ int get_network_unique_id() const;
+ bool is_network_server() const;
+ void set_refuse_new_network_connections(bool p_refuse);
+ bool is_refusing_new_network_connections() const;
+
+ MultiplayerAPI();
+ ~MultiplayerAPI();
+};
+
+#endif // MULTIPLAYER_PROTOCOL_H
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 5dfe067902..0c626c197b 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -1162,9 +1162,11 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
ERR_FAIL_V(ERR_FILE_UNRECOGNIZED);
}
- fw->store_32(VERSION_MAJOR); //current version
- fw->store_32(VERSION_MINOR);
- fw->store_32(FORMAT_VERSION);
+ // Since we're not actually converting the file contents, leave the version
+ // numbers in the file untouched.
+ fw->store_32(ver_major);
+ fw->store_32(ver_minor);
+ fw->store_32(ver_format);
save_ustring(fw, get_ustring(f)); //type
diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp
index 07a01ff99f..012ba78c6d 100644
--- a/core/io/stream_peer_ssl.cpp
+++ b/core/io/stream_peer_ssl.cpp
@@ -29,6 +29,8 @@
/*************************************************************************/
#include "stream_peer_ssl.h"
+#include "os/file_access.h"
+#include "project_settings.h"
StreamPeerSSL *(*StreamPeerSSL::_create)() = NULL;
@@ -50,6 +52,35 @@ bool StreamPeerSSL::is_available() {
return available;
}
+PoolByteArray StreamPeerSSL::get_project_cert_array() {
+
+ PoolByteArray out;
+ String certs_path = GLOBAL_DEF("network/ssl/certificates", "");
+ ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt"));
+
+ if (certs_path != "") {
+
+ FileAccess *f = FileAccess::open(certs_path, FileAccess::READ);
+ if (f) {
+ int flen = f->get_len();
+ out.resize(flen + 1);
+ {
+ PoolByteArray::Write w = out.write();
+ f->get_buffer(w.ptr(), flen);
+ w[flen] = 0; //end f string
+ }
+
+ memdelete(f);
+
+#ifdef DEBUG_ENABLED
+ print_line("Loaded certs from '" + certs_path);
+#endif
+ }
+ }
+
+ return out;
+}
+
void StreamPeerSSL::_bind_methods() {
ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll);
diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h
index f903438c28..77301a7c87 100644
--- a/core/io/stream_peer_ssl.h
+++ b/core/io/stream_peer_ssl.h
@@ -66,6 +66,7 @@ public:
static StreamPeerSSL *create();
+ static PoolByteArray get_project_cert_array();
static void load_certs_from_memory(const PoolByteArray &p_memory);
static bool is_available();
diff --git a/core/math/geometry.h b/core/math/geometry.h
index ca4363e129..73a53c53b6 100644
--- a/core/math/geometry.h
+++ b/core/math/geometry.h
@@ -502,16 +502,15 @@ public:
}
static bool is_point_in_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) {
- int as_x = s.x - a.x;
- int as_y = s.y - a.y;
+ Vector2 an = a - s;
+ Vector2 bn = b - s;
+ Vector2 cn = c - s;
- bool s_ab = (b.x - a.x) * as_y - (b.y - a.y) * as_x > 0;
+ bool orientation = an.cross(bn) > 0;
- if (((c.x - a.x) * as_y - (c.y - a.y) * as_x > 0) == s_ab) return false;
+ if ((bn.cross(cn) > 0) != orientation) return false;
- if (((c.x - b.x) * (s.y - b.y) - (c.y - b.y) * (s.x - b.x) > 0) != s_ab) return false;
-
- return true;
+ return (cn.cross(an) > 0) == orientation;
}
static bool is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon);
diff --git a/core/math/quat.cpp b/core/math/quat.cpp
index 9aa8b537d2..4f61401ac7 100644
--- a/core/math/quat.cpp
+++ b/core/math/quat.cpp
@@ -89,7 +89,7 @@ void Quat::set_euler_yxz(const Vector3 &p_euler) {
set(sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3,
sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3,
- -sin_a1 * sin_a2 * cos_a3 + cos_a1 * sin_a2 * sin_a3,
+ -sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3,
sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3);
}
diff --git a/core/os/input.cpp b/core/os/input.cpp
index 3089ab2ce3..e8a635e1b5 100644
--- a/core/os/input.cpp
+++ b/core/os/input.cpp
@@ -85,6 +85,7 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("warp_mouse_position", "to"), &Input::warp_mouse_position);
ClassDB::bind_method(D_METHOD("action_press", "action"), &Input::action_press);
ClassDB::bind_method(D_METHOD("action_release", "action"), &Input::action_release);
+ ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &Input::set_default_cursor_shape, DEFVAL(CURSOR_ARROW));
ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2()));
ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &Input::parse_input_event);
@@ -111,7 +112,7 @@ void Input::_bind_methods() {
BIND_ENUM_CONSTANT(CURSOR_HSPLIT);
BIND_ENUM_CONSTANT(CURSOR_HELP);
- ADD_SIGNAL(MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "connected")));
+ ADD_SIGNAL(MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "device"), PropertyInfo(Variant::BOOL, "connected")));
}
void Input::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
diff --git a/core/os/input.h b/core/os/input.h
index 9c7595ff7f..fca68f27b7 100644
--- a/core/os/input.h
+++ b/core/os/input.h
@@ -119,6 +119,8 @@ public:
virtual bool is_emulating_touchscreen() const = 0;
+ virtual CursorShape get_default_cursor_shape() = 0;
+ virtual void set_default_cursor_shape(CursorShape p_shape) = 0;
virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) = 0;
virtual void set_mouse_in_window(bool p_in_window) = 0;
diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp
index fa53cc85c8..9dfc91e308 100644
--- a/core/os/keyboard.cpp
+++ b/core/os/keyboard.cpp
@@ -461,99 +461,6 @@ const char *find_keycode_name(int p_keycode) {
return "";
}
-struct _KeyCodeReplace {
- int from;
- int to;
-};
-
-static const _KeyCodeReplace _keycode_replace_qwertz[] = {
- { KEY_Y, KEY_Z },
- { KEY_Z, KEY_Y },
- { 0, 0 }
-};
-
-static const _KeyCodeReplace _keycode_replace_azerty[] = {
- { KEY_W, KEY_Z },
- { KEY_Z, KEY_W },
- { KEY_A, KEY_Q },
- { KEY_Q, KEY_A },
- { KEY_SEMICOLON, KEY_M },
- { KEY_M, KEY_SEMICOLON },
- { 0, 0 }
-};
-
-static const _KeyCodeReplace _keycode_replace_qzerty[] = {
- { KEY_W, KEY_Z },
- { KEY_Z, KEY_W },
- { KEY_SEMICOLON, KEY_M },
- { KEY_M, KEY_SEMICOLON },
- { 0, 0 }
-};
-
-static const _KeyCodeReplace _keycode_replace_dvorak[] = {
- { KEY_UNDERSCORE, KEY_BRACELEFT },
- { KEY_EQUAL, KEY_BRACERIGHT },
- { KEY_Q, KEY_APOSTROPHE },
- { KEY_W, KEY_COMMA },
- { KEY_E, KEY_PERIOD },
- { KEY_R, KEY_P },
- { KEY_T, KEY_Y },
- { KEY_Y, KEY_F },
- { KEY_U, KEY_G },
- { KEY_I, KEY_C },
- { KEY_O, KEY_R },
- { KEY_P, KEY_L },
- { KEY_BRACELEFT, KEY_SLASH },
- { KEY_BRACERIGHT, KEY_EQUAL },
- { KEY_A, KEY_A },
- { KEY_S, KEY_O },
- { KEY_D, KEY_E },
- { KEY_F, KEY_U },
- { KEY_G, KEY_I },
- { KEY_H, KEY_D },
- { KEY_J, KEY_H },
- { KEY_K, KEY_T },
- { KEY_L, KEY_N },
- { KEY_SEMICOLON, KEY_S },
- { KEY_APOSTROPHE, KEY_UNDERSCORE },
- { KEY_Z, KEY_SEMICOLON },
- { KEY_X, KEY_Q },
- { KEY_C, KEY_J },
- { KEY_V, KEY_K },
- { KEY_B, KEY_X },
- { KEY_N, KEY_B },
- { KEY_M, KEY_M },
- { KEY_COMMA, KEY_W },
- { KEY_PERIOD, KEY_V },
- { KEY_SLASH, KEY_Z },
- { 0, 0 }
-};
-
-static const _KeyCodeReplace _keycode_replace_neo[] = {
- { 0, 0 }
-};
-
-static const _KeyCodeReplace _keycode_replace_colemak[] = {
- { KEY_E, KEY_F },
- { KEY_R, KEY_P },
- { KEY_T, KEY_G },
- { KEY_Y, KEY_J },
- { KEY_U, KEY_L },
- { KEY_I, KEY_U },
- { KEY_O, KEY_Y },
- { KEY_P, KEY_SEMICOLON },
- { KEY_S, KEY_R },
- { KEY_D, KEY_S },
- { KEY_F, KEY_T },
- { KEY_G, KEY_D },
- { KEY_J, KEY_N },
- { KEY_K, KEY_E },
- { KEY_L, KEY_I },
- { KEY_SEMICOLON, KEY_O },
- { KEY_N, KEY_K },
- { 0, 0 }
-};
-
int keycode_get_count() {
const _KeyCodeText *kct = &_keycodes[0];
@@ -574,31 +481,3 @@ int keycode_get_value_by_index(int p_index) {
const char *keycode_get_name_by_index(int p_index) {
return _keycodes[p_index].text;
}
-
-int latin_keyboard_keycode_convert(int p_keycode) {
-
- const _KeyCodeReplace *kcr = NULL;
- switch (OS::get_singleton()->get_latin_keyboard_variant()) {
-
- case OS::LATIN_KEYBOARD_QWERTY: return p_keycode; break;
- case OS::LATIN_KEYBOARD_QWERTZ: kcr = _keycode_replace_qwertz; break;
- case OS::LATIN_KEYBOARD_AZERTY: kcr = _keycode_replace_azerty; break;
- case OS::LATIN_KEYBOARD_QZERTY: kcr = _keycode_replace_qzerty; break;
- case OS::LATIN_KEYBOARD_DVORAK: kcr = _keycode_replace_dvorak; break;
- case OS::LATIN_KEYBOARD_NEO: kcr = _keycode_replace_neo; break;
- case OS::LATIN_KEYBOARD_COLEMAK: kcr = _keycode_replace_colemak; break;
- default: return p_keycode;
- }
-
- if (!kcr) {
- return p_keycode;
- }
-
- while (kcr->from) {
- if (kcr->from == p_keycode)
- return kcr->to;
- kcr++;
- }
-
- return p_keycode;
-}
diff --git a/core/os/keyboard.h b/core/os/keyboard.h
index 4c253fa4ce..a0e6f8b2ef 100644
--- a/core/os/keyboard.h
+++ b/core/os/keyboard.h
@@ -331,6 +331,5 @@ const char *find_keycode_name(int p_keycode);
int keycode_get_count();
int keycode_get_value_by_index(int p_index);
const char *keycode_get_name_by_index(int p_index);
-int latin_keyboard_keycode_convert(int p_keycode);
#endif
diff --git a/core/os/os.h b/core/os/os.h
index 5a2c998782..943c0498f1 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -205,6 +205,18 @@ public:
virtual void request_attention() {}
virtual void center_window();
+ // Returns window area free of hardware controls and other obstacles.
+ // The application should use this to determine where to place UI elements.
+ //
+ // Keep in mind the area returned is in window coordinates rather than
+ // viewport coordinates - you should perform the conversion on your own.
+ //
+ // The maximum size of the area is Rect2(0, 0, window_size.width, window_size.height).
+ virtual Rect2 get_window_safe_area() const {
+ Size2 window_size = get_window_size();
+ return Rect2(0, 0, window_size.width, window_size.height);
+ }
+
virtual void set_borderless_window(bool p_borderless) {}
virtual bool get_borderless_window() { return 0; }
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index 006459c5f6..2a611ccf6a 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -42,6 +42,7 @@
#include "io/config_file.h"
#include "io/http_client.h"
#include "io/marshalls.h"
+#include "io/multiplayer_api.h"
#include "io/networked_multiplayer_peer.h"
#include "io/packet_peer.h"
#include "io/packet_peer_udp.h"
@@ -145,6 +146,7 @@ void register_core_types() {
ClassDB::register_virtual_class<PacketPeer>();
ClassDB::register_class<PacketPeerStream>();
ClassDB::register_virtual_class<NetworkedMultiplayerPeer>();
+ ClassDB::register_class<MultiplayerAPI>();
ClassDB::register_class<MainLoop>();
//ClassDB::register_type<OptimizedSaver>();
ClassDB::register_class<Translation>();
diff --git a/core/resource.cpp b/core/resource.cpp
index 2eeed50d9d..179333aa14 100644
--- a/core/resource.cpp
+++ b/core/resource.cpp
@@ -226,7 +226,7 @@ Ref<Resource> Resource::duplicate(bool p_subresources) const {
if (!(E->get().usage & PROPERTY_USAGE_STORAGE))
continue;
- Variant p = get(E->get().name);
+ Variant p = get(E->get().name).duplicate(true);
if (p.get_type() == Variant::OBJECT && p_subresources) {
RES sr = p;
diff --git a/core/script_language.h b/core/script_language.h
index 6d32fc054c..0c1f99cea6 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -221,7 +221,9 @@ public:
RESULT_CLASS,
RESULT_CLASS_CONSTANT,
RESULT_CLASS_PROPERTY,
- RESULT_CLASS_METHOD
+ RESULT_CLASS_METHOD,
+ RESULT_CLASS_ENUM,
+ RESULT_CLASS_TBD_GLOBALSCOPE
};
Type type;
Ref<Script> script;
diff --git a/core/ustring.cpp b/core/ustring.cpp
index a7a7810837..d749146998 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -3165,11 +3165,11 @@ String String::word_wrap(int p_chars_per_line) const {
return ret;
}
-String String::http_escape() const {
+String String::percent_encode() const {
const CharString temp = utf8();
String res;
- for (int i = 0; i < length(); ++i) {
- CharType ord = temp[i];
+ for (int i = 0; i < temp.length(); ++i) {
+ char ord = temp[i];
if (ord == '.' || ord == '-' || ord == '_' || ord == '~' ||
(ord >= 'a' && ord <= 'z') ||
(ord >= 'A' && ord <= 'Z') ||
@@ -3178,9 +3178,9 @@ String String::http_escape() const {
} else {
char h_Val[3];
#if defined(__GNUC__) || defined(_MSC_VER)
- snprintf(h_Val, 3, "%.2X", ord);
+ snprintf(h_Val, 3, "%hhX", ord);
#else
- sprintf(h_Val, "%.2X", ord);
+ sprintf(h_Val, "%hhX", ord);
#endif
res += "%";
res += h_Val;
@@ -3189,7 +3189,7 @@ String String::http_escape() const {
return res;
}
-String String::http_unescape() const {
+String String::percent_decode() const {
String res;
for (int i = 0; i < length(); ++i) {
if (ord_at(i) == '%' && i + 2 < length()) {
@@ -3727,68 +3727,6 @@ String String::plus_file(const String &p_file) const {
return *this + "/" + p_file;
}
-String String::percent_encode() const {
-
- CharString cs = utf8();
- String encoded;
- for (int i = 0; i < cs.length(); i++) {
- uint8_t c = cs[i];
- if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '~' || c == '.') {
-
- char p[2] = { (char)c, 0 };
- encoded += p;
- } else {
- char p[4] = { '%', 0, 0, 0 };
- static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
-
- p[1] = hex[c >> 4];
- p[2] = hex[c & 0xF];
- encoded += p;
- }
- }
-
- return encoded;
-}
-String String::percent_decode() const {
-
- CharString pe;
-
- CharString cs = utf8();
- for (int i = 0; i < cs.length(); i++) {
-
- uint8_t c = cs[i];
- if (c == '%' && i < length() - 2) {
-
- uint8_t a = LOWERCASE(cs[i + 1]);
- uint8_t b = LOWERCASE(cs[i + 2]);
-
- c = 0;
- if (a >= '0' && a <= '9')
- c = (a - '0') << 4;
- else if (a >= 'a' && a <= 'f')
- c = (a - 'a' + 10) << 4;
- else
- continue;
-
- uint8_t d = 0;
-
- if (b >= '0' && b <= '9')
- d = (b - '0');
- else if (b >= 'a' && b <= 'f')
- d = (b - 'a' + 10);
- else
- continue;
- c += d;
- i += 2;
- }
- pe.push_back(c);
- }
-
- pe.push_back(0);
-
- return String::utf8(pe.ptr());
-}
-
String String::get_basename() const {
int pos = find_last(".");
diff --git a/core/ustring.h b/core/ustring.h
index bb676ce623..8023c9b95d 100644
--- a/core/ustring.h
+++ b/core/ustring.h
@@ -226,17 +226,14 @@ public:
String xml_escape(bool p_escape_quotes = false) const;
String xml_unescape() const;
- String http_escape() const;
- String http_unescape() const;
+ String percent_encode() const;
+ String percent_decode() const;
String c_escape() const;
String c_escape_multiline() const;
String c_unescape() const;
String json_escape() const;
String word_wrap(int p_chars_per_line) const;
- String percent_encode() const;
- String percent_decode() const;
-
bool is_valid_identifier() const;
bool is_valid_integer() const;
bool is_valid_float() const;
diff --git a/core/variant.h b/core/variant.h
index 0a4afada5b..2cdb5c9ab6 100644
--- a/core/variant.h
+++ b/core/variant.h
@@ -338,6 +338,7 @@ public:
}
void zero();
+ Variant duplicate(bool deep = false) const;
static void blend(const Variant &a, const Variant &b, float c, Variant &r_dst);
static void interpolate(const Variant &a, const Variant &b, float c, Variant &r_dst);
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index cda7dccf0c..c6e093010d 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -465,7 +465,7 @@ struct _VariantCall {
VCALL_LOCALMEM0R(Dictionary, hash);
VCALL_LOCALMEM0R(Dictionary, keys);
VCALL_LOCALMEM0R(Dictionary, values);
- VCALL_LOCALMEM0R(Dictionary, duplicate);
+ VCALL_LOCALMEM1R(Dictionary, duplicate);
VCALL_LOCALMEM2(Array, set);
VCALL_LOCALMEM1R(Array, get);
@@ -494,7 +494,7 @@ struct _VariantCall {
VCALL_LOCALMEM0(Array, shuffle);
VCALL_LOCALMEM2R(Array, bsearch);
VCALL_LOCALMEM4R(Array, bsearch_custom);
- VCALL_LOCALMEM0R(Array, duplicate);
+ VCALL_LOCALMEM1R(Array, duplicate);
VCALL_LOCALMEM0(Array, invert);
static void _call_PoolByteArray_get_string_from_ascii(Variant &r_ret, Variant &p_self, const Variant **p_args) {
@@ -1613,7 +1613,7 @@ void register_variant_methods() {
ADDFUNC0R(DICTIONARY, INT, Dictionary, hash, varray());
ADDFUNC0R(DICTIONARY, ARRAY, Dictionary, keys, varray());
ADDFUNC0R(DICTIONARY, ARRAY, Dictionary, values, varray());
- ADDFUNC0R(DICTIONARY, DICTIONARY, Dictionary, duplicate, varray());
+ ADDFUNC1R(DICTIONARY, DICTIONARY, Dictionary, duplicate, BOOL, "deep", varray(false));
ADDFUNC0R(ARRAY, INT, Array, size, varray());
ADDFUNC0R(ARRAY, BOOL, Array, empty, varray());
@@ -1641,7 +1641,7 @@ void register_variant_methods() {
ADDFUNC2R(ARRAY, INT, Array, bsearch, NIL, "value", BOOL, "before", varray(true));
ADDFUNC4R(ARRAY, INT, Array, bsearch_custom, NIL, "value", OBJECT, "obj", STRING, "func", BOOL, "before", varray(true));
ADDFUNC0NC(ARRAY, NIL, Array, invert, varray());
- ADDFUNC0RNC(ARRAY, ARRAY, Array, duplicate, varray());
+ ADDFUNC1R(ARRAY, ARRAY, Array, duplicate, BOOL, "deep", varray(false));
ADDFUNC0R(POOL_BYTE_ARRAY, INT, PoolByteArray, size, varray());
ADDFUNC2(POOL_BYTE_ARRAY, NIL, PoolByteArray, set, INT, "idx", INT, "byte", varray());
diff --git a/core/variant_op.cpp b/core/variant_op.cpp
index 842f5f0af6..8ad981ed0e 100644
--- a/core/variant_op.cpp
+++ b/core/variant_op.cpp
@@ -3415,6 +3415,19 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const {
return Variant();
}
+Variant Variant::duplicate(bool deep) const {
+ switch (type) {
+ // case OBJECT:
+ // return operator Object *()->duplicate();
+ case DICTIONARY:
+ return operator Dictionary().duplicate(deep);
+ case ARRAY:
+ return operator Array().duplicate(deep);
+ default:
+ return *this;
+ }
+}
+
void Variant::blend(const Variant &a, const Variant &b, float c, Variant &r_dst) {
if (a.type != b.type) {
if (a.is_num() && b.is_num()) {
diff --git a/doc/classes/ARVRServer.xml b/doc/classes/ARVRServer.xml
index 965fad1961..a7d3e46684 100644
--- a/doc/classes/ARVRServer.xml
+++ b/doc/classes/ARVRServer.xml
@@ -36,6 +36,13 @@
Find an interface by its name. Say that you're making a game that uses specific capabilities of an AR/VR platform you can find the interface for that platform by name and initialize it.
</description>
</method>
+ <method name="get_hmd_transform">
+ <return type="Transform">
+ </return>
+ <description>
+ Returns the primary interface's transformation.
+ </description>
+ </method>
<method name="get_interface" qualifiers="const">
<return type="ARVRInterface">
</return>
@@ -59,6 +66,24 @@
Returns a list of available interfaces with both id and name of the interface.
</description>
</method>
+ <method name="get_last_commit_usec">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_last_frame_usec">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_last_process_usec">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="get_reference_frame" qualifiers="const">
<return type="Transform">
</return>
@@ -82,17 +107,10 @@
Get the number of trackers currently registered.
</description>
</method>
- <method name="set_primary_interface">
- <return type="void">
- </return>
- <argument index="0" name="interface" type="ARVRInterface">
- </argument>
- <description>
- Changes the primary interface to the specified interface. Again mostly exposed for GDNative interfaces.
- </description>
- </method>
</methods>
<members>
+ <member name="primary_interface" type="ARVRInterface" setter="set_primary_interface" getter="get_primary_interface">
+ </member>
<member name="world_scale" type="float" setter="set_world_scale" getter="get_world_scale">
Allows you to adjust the scale to your game's units. Most AR/VR platforms assume a scale of 1 game world unit = 1 meter in the real world.
</member>
diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml
index 178c714a20..f3280e7a27 100644
--- a/doc/classes/AnimationPlayer.xml
+++ b/doc/classes/AnimationPlayer.xml
@@ -103,6 +103,13 @@
Get the blend time (in seconds) between two animations, referenced by their names.
</description>
</method>
+ <method name="get_playing_speed" qualifiers="const">
+ <return type="float">
+ </return>
+ <description>
+ Get the actual playing speed of current animation or 0 if not playing. This speed is the [code]playback_speed[/code] property multiplied by [code]custom_speed[/code] argument specified when calling the [code]play[/code] method.
+ </description>
+ </method>
<method name="has_animation" qualifiers="const">
<return type="bool">
</return>
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index 2e2176743f..5f85751c13 100644
--- a/doc/classes/Array.xml
+++ b/doc/classes/Array.xml
@@ -131,8 +131,11 @@
<method name="duplicate">
<return type="Array">
</return>
+ <argument index="0" name="deep" type="bool" default="False">
+ </argument>
<description>
- Returns a copy of this [code]Array[/code].
+ Returns a copy of the array.
+ If [code]deep[/code] is [code]true[/code], a deep copy is be performed: all nested arrays and dictionaries are duplicated and will not be shared with the original array. If [code]false[/code], a shallow copy is made and references to the original nested arrays and dictionaries are kept, so that modifying a sub-array or dictionary in the copy will also impact those referenced in the source array.
</description>
</method>
<method name="empty">
diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml
index 0be91427b1..7b3ba632cc 100644
--- a/doc/classes/AudioServer.xml
+++ b/doc/classes/AudioServer.xml
@@ -126,6 +126,18 @@
Returns the volume of the bus at index [code]bus_idx[/code] in dB.
</description>
</method>
+ <method name="get_device">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_device_list">
+ <return type="Array">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="get_mix_rate" qualifiers="const">
<return type="float">
</return>
@@ -313,6 +325,14 @@
Sets the volume of the bus at index [code]bus_idx[/code] to [code]volume_db[/code].
</description>
</method>
+ <method name="set_device">
+ <return type="void">
+ </return>
+ <argument index="0" name="arg0" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="swap_bus_effects">
<return type="void">
</return>
diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml
index f7270fa5f9..554ed99964 100644
--- a/doc/classes/Basis.xml
+++ b/doc/classes/Basis.xml
@@ -8,6 +8,7 @@
For such use, it is composed of a scaling and a rotation matrix, in that order (M = R.S).
</description>
<tutorials>
+ http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Camera.xml b/doc/classes/Camera.xml
index 0b7cd4ef7a..7f7f152ae9 100644
--- a/doc/classes/Camera.xml
+++ b/doc/classes/Camera.xml
@@ -14,15 +14,17 @@
<method name="clear_current">
<return type="void">
</return>
+ <argument index="0" name="enable_next" type="bool" default="true">
+ </argument>
<description>
- If this is the current Camera, remove it from being current. If it is inside the node tree, request to make the next Camera current, if any.
+ If this is the current Camera, remove it from being current. If [code]enable_next[/code] is true, request to make the next Camera current, if any.
</description>
</method>
<method name="get_camera_transform" qualifiers="const">
<return type="Transform">
</return>
<description>
- Get the camera transform. Subclassed cameras (such as CharacterCamera) may provide different transforms than the [Node] transform.
+ Gets the camera transform. Subclassed cameras (such as CharacterCamera) may provide different transforms than the [Node] transform.
</description>
</method>
<method name="is_position_behind" qualifiers="const">
@@ -31,14 +33,14 @@
<argument index="0" name="world_point" type="Vector3">
</argument>
<description>
- Returns [code]true[/code] if the given position is behind the Camera.
+ Returns [code]true[/code] if the given position is behind the Camera. Note that a position which returns [code]false[/code] may still be outside the Camera's field of view.
</description>
</method>
<method name="make_current">
<return type="void">
</return>
<description>
- Make this camera the current Camera for the [Viewport] (see class description). If the Camera Node is outside the scene tree, it will attempt to become current once it's added.
+ Makes this camera the current Camera for the [Viewport] (see class description). If the Camera Node is outside the scene tree, it will attempt to become current once it's added.
</description>
</method>
<method name="project_local_ray_normal" qualifiers="const">
@@ -56,7 +58,7 @@
<argument index="0" name="screen_point" type="Vector2">
</argument>
<description>
- Returns how a 2D coordinate in the Viewport rectangle maps to a 3D point in worldspace.
+ Returns the 3D point in worldspace that maps to the given 2D coordinate in the [Viewport] rectangle.
</description>
</method>
<method name="project_ray_normal" qualifiers="const">
@@ -87,7 +89,7 @@
<argument index="2" name="z_far" type="float">
</argument>
<description>
- Set the camera projection to orthogonal mode, by specifying a width and the [i]near[/i] and [i]far[/i] clip planes in worldspace units. (As a hint, 2D games often use this projection, with values specified in pixels)
+ Sets the camera projection to orthogonal mode, by specifying a width and the [i]near[/i] and [i]far[/i] clip planes in worldspace units. (As a hint, 2D games often use this projection, with values specified in pixels)
</description>
</method>
<method name="set_perspective">
@@ -100,7 +102,7 @@
<argument index="2" name="z_far" type="float">
</argument>
<description>
- Set the camera projection to perspective mode, by specifying a [i]FOV[/i] Y angle in degrees (FOV means Field of View), and the [i]near[/i] and [i]far[/i] clip planes in worldspace units.
+ Sets the camera projection to perspective mode, by specifying a [i]FOV[/i] Y angle in degrees (FOV means Field of View), and the [i]near[/i] and [i]far[/i] clip planes in worldspace units.
</description>
</method>
<method name="unproject_position" qualifiers="const">
@@ -109,7 +111,7 @@
<argument index="0" name="world_point" type="Vector3">
</argument>
<description>
- Returns how a 3D point in worldspace maps to a 2D coordinate in the [Viewport] rectangle.
+ Returns the 2D coordinate in the [Viewport] rectangle that maps to the given 3D point in worldspace.
</description>
</method>
</methods>
@@ -124,7 +126,7 @@
If not [code]DOPPLER_TRACKING_DISABLED[/code] this Camera will simulate the Doppler effect for objects changed in particular [code]_process[/code] methods. Default value: [code]DOPPLER_TRACKING_DISABLED[/code].
</member>
<member name="environment" type="Environment" setter="set_environment" getter="get_environment">
- Set the [Environment] to use for this Camera.
+ The [Environment] to use for this Camera.
</member>
<member name="far" type="float" setter="set_zfar" getter="get_zfar">
The distance to the far culling boundary for this Camera relative to its local z-axis.
@@ -136,7 +138,7 @@
The horizontal (X) offset of the Camera viewport.
</member>
<member name="keep_aspect" type="int" setter="set_keep_aspect_mode" getter="get_keep_aspect_mode" enum="Camera.KeepAspect">
- The axis to lock during [member fov]/[member size] adjustments.
+ The axis to lock during [member fov]/[member size] adjustments. Can be either [code]KEEP_WIDTH[/code] or [code]KEEP_HEIGHT[/code].
</member>
<member name="near" type="float" setter="set_znear" getter="get_znear">
The distance to the near culling boundary for this Camera relative to its local z-axis.
@@ -148,7 +150,7 @@
The camera's size measured as 1/2 the width or height. Only applicable in orthogonal mode. Since [member keep_aspect] locks on axis, [code]size[/code] sets the other axis' size length.
</member>
<member name="v_offset" type="float" setter="set_v_offset" getter="get_v_offset">
- The horizontal (Y) offset of the Camera viewport.
+ The vertical (Y) offset of the Camera viewport.
</member>
</members>
<constants>
diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml
index b2b0938822..3e4d1b29f7 100644
--- a/doc/classes/CanvasLayer.xml
+++ b/doc/classes/CanvasLayer.xml
@@ -13,11 +13,11 @@
<demos>
</demos>
<methods>
- <method name="get_world_2d" qualifiers="const">
- <return type="World2D">
+ <method name="get_canvas" qualifiers="const">
+ <return type="RID">
</return>
<description>
- Return the [World2D] used by this layer.
+ Returns the RID of the canvas used by this layer.
</description>
</method>
</methods>
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index 95e8622cf4..4b6a9cca8a 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -773,6 +773,10 @@
<constant name="NOTIFICATION_MODAL_CLOSE" value="46">
Sent when an open modal dialog closes. See [member show_modal].
</constant>
+ <constant name="NOTIFICATION_SCROLL_BEGIN" value="47">
+ </constant>
+ <constant name="NOTIFICATION_SCROLL_END" value="48">
+ </constant>
<constant name="CURSOR_ARROW" value="0" enum="CursorShape">
Show the system's arrow mouse cursor when the user hovers the node. Use with [method set_default_cursor_shape].
</constant>
@@ -908,6 +912,8 @@
</constant>
<constant name="GROW_DIRECTION_END" value="1" enum="GrowDirection">
</constant>
+ <constant name="GROW_DIRECTION_BOTH" value="2" enum="GrowDirection">
+ </constant>
<constant name="ANCHOR_BEGIN" value="0" enum="Anchor">
Snaps one of the 4 anchor's sides to the origin of the node's [code]Rect[/code], in the top left. Use it with one of the [code]anchor_*[/code] member variables, like [member anchor_left]. To change all 4 anchors at once, use [method set_anchors_preset].
</constant>
diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml
index d7118265f1..3c1413b028 100644
--- a/doc/classes/Dictionary.xml
+++ b/doc/classes/Dictionary.xml
@@ -19,6 +19,8 @@
<method name="duplicate">
<return type="Dictionary">
</return>
+ <argument index="0" name="deep" type="bool" default="False">
+ </argument>
<description>
</description>
</method>
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index 0a8d95d25d..846d6f18ff 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -102,6 +102,21 @@
<description>
</description>
</method>
+ <method name="add_tool_menu_item">
+ <return type="void">
+ </return>
+ <argument index="0" name="name" type="String">
+ </argument>
+ <argument index="1" name="handler" type="Object">
+ </argument>
+ <argument index="2" name="callback" type="String">
+ </argument>
+ <argument index="3" name="ud" type="Variant" default="null">
+ </argument>
+ <description>
+ Adds a custom menu to 'Project > Tools' as [code]name[/code] that calls [code]callback[/code] on an instance of [code]handler[/code] with a parameter [code]ud[/code] when user activates it.
+ </description>
+ </method>
<method name="add_tool_submenu_item">
<return type="void">
</return>
@@ -347,6 +362,15 @@
<description>
</description>
</method>
+ <method name="remove_tool_menu_item">
+ <return type="void">
+ </return>
+ <argument index="0" name="name" type="String">
+ </argument>
+ <description>
+ Removes a menu [code]name[/code] from 'Project > Tools'.
+ </description>
+ </method>
<method name="save_external_data" qualifiers="virtual">
<return type="void">
</return>
diff --git a/doc/classes/File.xml b/doc/classes/File.xml
index 534323348e..bd368e967a 100644
--- a/doc/classes/File.xml
+++ b/doc/classes/File.xml
@@ -163,14 +163,14 @@
Returns a [String] saved in Pascal format from the file.
</description>
</method>
- <method name="get_path">
+ <method name="get_path" qualifiers="const">
<return type="String">
</return>
<description>
Returns the path as a [String] for the current open file.
</description>
</method>
- <method name="get_path_absolute">
+ <method name="get_path_absolute" qualifiers="const">
<return type="String">
</return>
<description>
diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml
index 5bd7c4cd3e..018b548ef1 100644
--- a/doc/classes/HTTPClient.xml
+++ b/doc/classes/HTTPClient.xml
@@ -111,6 +111,12 @@
String queryString = httpClient.query_string_from_dict(fields)
returns:= "username=user&amp;password=pass"
[/codeblock]
+ Furthermore, if a key has a null value, only the key itself is added, without equal sign and value. If the value is an array, for each value in it a pair with the same key is added.
+ [codeblock]
+ var fields = {"single": 123, "not_valued": null, "multiple": [22, 33, 44]}
+ String queryString = httpClient.query_string_from_dict(fields)
+ returns:= "single=123&amp;not_valued&amp;multiple=22&amp;multiple=33&amp;multiple=44"
+ [/codeblock]
</description>
</method>
<method name="read_response_body_chunk">
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index c1bd8d34eb..ea61aced83 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -67,6 +67,14 @@
Blits [code]src_rect[/code] area from [code]src[/code] image to this image at the coordinates given by [code]dst[/code]. [code]src[/code] pixel is copied onto [code]dst[/code] if the corresponding [code]mask[/code] pixel's alpha value is not 0. [code]src[/code] image and [code]mask[/code] image [b]must[/b] have the same size (width and height) but they can have different formats.
</description>
</method>
+ <method name="bumpmap_to_normalmap">
+ <return type="void">
+ </return>
+ <argument index="0" name="bump_scale" type="float" default="1.0">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="clear_mipmaps">
<return type="void">
</return>
@@ -436,109 +444,109 @@
<constant name="FORMAT_LA8" value="1" enum="Format">
</constant>
<constant name="FORMAT_R8" value="2" enum="Format">
- OpenGL texture format RED with a single component and a bitdepth of 8.
+ OpenGL texture format RED with a single component and a bitdepth of 8.
</constant>
<constant name="FORMAT_RG8" value="3" enum="Format">
- OpenGL texture format RG with two components and a bitdepth of 8 for each.
+ OpenGL texture format RG with two components and a bitdepth of 8 for each.
</constant>
<constant name="FORMAT_RGB8" value="4" enum="Format">
- OpenGL texture format RGB with three components, each with a bitdepth of 8.
+ OpenGL texture format RGB with three components, each with a bitdepth of 8.
</constant>
<constant name="FORMAT_RGBA8" value="5" enum="Format">
- OpenGL texture format RGBA with four components, each with a bitdepth of 8.
+ OpenGL texture format RGBA with four components, each with a bitdepth of 8.
</constant>
<constant name="FORMAT_RGBA4444" value="6" enum="Format">
- OpenGL texture format RGBA with four components, each with a bitdepth of 4.
+ OpenGL texture format RGBA with four components, each with a bitdepth of 4.
</constant>
<constant name="FORMAT_RGBA5551" value="7" enum="Format">
- OpenGL texture format GL_RGB5_A1 where 5 bits of depth for each component of RGB and one bit for alpha.
+ OpenGL texture format GL_RGB5_A1 where 5 bits of depth for each component of RGB and one bit for alpha.
</constant>
<constant name="FORMAT_RF" value="8" enum="Format">
- OpenGL texture format GL_R32F where there's one component, a 32-bit floating-point value.
+ OpenGL texture format GL_R32F where there's one component, a 32-bit floating-point value.
</constant>
<constant name="FORMAT_RGF" value="9" enum="Format">
- OpenGL texture format GL_RG32F where there are two components, each a 32-bit floating-point values.
+ OpenGL texture format GL_RG32F where there are two components, each a 32-bit floating-point values.
</constant>
<constant name="FORMAT_RGBF" value="10" enum="Format">
- OpenGL texture format GL_RGB32F where there are three components, each a 32-bit floating-point values.
+ OpenGL texture format GL_RGB32F where there are three components, each a 32-bit floating-point values.
</constant>
<constant name="FORMAT_RGBAF" value="11" enum="Format">
- OpenGL texture format GL_RGBA32F where there are four components, each a 32-bit floating-point values.
+ OpenGL texture format GL_RGBA32F where there are four components, each a 32-bit floating-point values.
</constant>
<constant name="FORMAT_RH" value="12" enum="Format">
- OpenGL texture format GL_R32F where there's one component, a 16-bit "half-precision" floating-point value.
+ OpenGL texture format GL_R32F where there's one component, a 16-bit "half-precision" floating-point value.
</constant>
<constant name="FORMAT_RGH" value="13" enum="Format">
- OpenGL texture format GL_RG32F where there's two components, each a 16-bit "half-precision" floating-point value.
+ OpenGL texture format GL_RG32F where there's two components, each a 16-bit "half-precision" floating-point value.
</constant>
<constant name="FORMAT_RGBH" value="14" enum="Format">
- OpenGL texture format GL_RGB32F where there's three components, each a 16-bit "half-precision" floating-point value.
+ OpenGL texture format GL_RGB32F where there's three components, each a 16-bit "half-precision" floating-point value.
</constant>
<constant name="FORMAT_RGBAH" value="15" enum="Format">
- OpenGL texture format GL_RGBA32F where there's four components, each a 16-bit "half-precision" floating-point value.
+ OpenGL texture format GL_RGBA32F where there's four components, each a 16-bit "half-precision" floating-point value.
</constant>
<constant name="FORMAT_RGBE9995" value="16" enum="Format">
- A special OpenGL texture format where the three color components have 9 bits of precision and all three share a single exponent.
+ A special OpenGL texture format where the three color components have 9 bits of precision and all three share a single exponent.
</constant>
<constant name="FORMAT_DXT1" value="17" enum="Format">
- The S3TC texture format that uses Block Compression 1, and is the smallest variation of S3TC, only providing 1 bit of alpha and color data being premultiplied with alpha. More information can be found at https://www.khronos.org/opengl/wiki/S3_Texture_Compression.
+ The S3TC texture format that uses Block Compression 1, and is the smallest variation of S3TC, only providing 1 bit of alpha and color data being premultiplied with alpha. More information can be found at https://www.khronos.org/opengl/wiki/S3_Texture_Compression.
</constant>
<constant name="FORMAT_DXT3" value="18" enum="Format">
- The S3TC texture format that uses Block Compression 2, and color data is interpreted as not having been premultiplied by alpha. Well suited for images with sharp alpha transitions between translucent and opaque areas.
+ The S3TC texture format that uses Block Compression 2, and color data is interpreted as not having been premultiplied by alpha. Well suited for images with sharp alpha transitions between translucent and opaque areas.
</constant>
<constant name="FORMAT_DXT5" value="19" enum="Format">
- The S3TC texture format also known as Block Compression 3 or BC3 that contains 64 bits of alpha channel data followed by 64 bits of DXT1-encoded color data. Color data is not premultiplied by alpha, same as DXT3. DXT5 generally produces superior results for transparency gradients than DXT3.
+ The S3TC texture format also known as Block Compression 3 or BC3 that contains 64 bits of alpha channel data followed by 64 bits of DXT1-encoded color data. Color data is not premultiplied by alpha, same as DXT3. DXT5 generally produces superior results for transparency gradients than DXT3.
</constant>
<constant name="FORMAT_RGTC_R" value="20" enum="Format">
- Texture format that uses Red Green Texture Compression, normalizing the red channel data using the same compression algorithm that DXT5 uses for the alpha channel. More information can be found here https://www.khronos.org/opengl/wiki/Red_Green_Texture_Compression.
+ Texture format that uses Red Green Texture Compression, normalizing the red channel data using the same compression algorithm that DXT5 uses for the alpha channel. More information can be found here https://www.khronos.org/opengl/wiki/Red_Green_Texture_Compression.
</constant>
<constant name="FORMAT_RGTC_RG" value="21" enum="Format">
- Texture format that uses Red Green Texture Compression, normalizing the red and green channel data using the same compression algorithm that DXT5 uses for the alpha channel.
+ Texture format that uses Red Green Texture Compression, normalizing the red and green channel data using the same compression algorithm that DXT5 uses for the alpha channel.
</constant>
<constant name="FORMAT_BPTC_RGBA" value="22" enum="Format">
- Texture format that uses BPTC compression with unsigned normalized RGBA components. More information can be found at https://www.khronos.org/opengl/wiki/BPTC_Texture_Compression.
+ Texture format that uses BPTC compression with unsigned normalized RGBA components. More information can be found at https://www.khronos.org/opengl/wiki/BPTC_Texture_Compression.
</constant>
<constant name="FORMAT_BPTC_RGBF" value="23" enum="Format">
- Texture format that uses BPTC compression with signed floating-point RGB components.
+ Texture format that uses BPTC compression with signed floating-point RGB components.
</constant>
<constant name="FORMAT_BPTC_RGBFU" value="24" enum="Format">
- Texture format that uses BPTC compression with unsigned floating-point RGB components.
+ Texture format that uses BPTC compression with unsigned floating-point RGB components.
</constant>
<constant name="FORMAT_PVRTC2" value="25" enum="Format">
- Texture format used on PowerVR-supported mobile platforms, uses 2 bit color depth with no alpha. More information on PVRTC can be found here https://en.wikipedia.org/wiki/PVRTC.
+ Texture format used on PowerVR-supported mobile platforms, uses 2 bit color depth with no alpha. More information on PVRTC can be found here https://en.wikipedia.org/wiki/PVRTC.
</constant>
<constant name="FORMAT_PVRTC2A" value="26" enum="Format">
- Same as PVRTC2, but with an alpha component.
+ Same as PVRTC2, but with an alpha component.
</constant>
<constant name="FORMAT_PVRTC4" value="27" enum="Format">
- Similar to PVRTC2, but with 4 bit color depth and no alpha.
+ Similar to PVRTC2, but with 4 bit color depth and no alpha.
</constant>
<constant name="FORMAT_PVRTC4A" value="28" enum="Format">
- Same as PVRTC4, but with an alpha component.
+ Same as PVRTC4, but with an alpha component.
</constant>
<constant name="FORMAT_ETC" value="29" enum="Format">
- Ericsson Texture Compression format, also referred to as 'ETC1', and is part of the OpenGL ES graphics standard. An overview of the format is given at https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC1.
+ Ericsson Texture Compression format, also referred to as 'ETC1', and is part of the OpenGL ES graphics standard. An overview of the format is given at https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC1.
</constant>
<constant name="FORMAT_ETC2_R11" value="30" enum="Format">
- Ericsson Texture Compression format 2 variant R11_EAC, which provides one channel of unsigned data.
+ Ericsson Texture Compression format 2 variant R11_EAC, which provides one channel of unsigned data.
</constant>
<constant name="FORMAT_ETC2_R11S" value="31" enum="Format">
- Ericsson Texture Compression format 2 variant SIGNED_R11_EAC, which provides one channel of signed data.
+ Ericsson Texture Compression format 2 variant SIGNED_R11_EAC, which provides one channel of signed data.
</constant>
<constant name="FORMAT_ETC2_RG11" value="32" enum="Format">
- Ericsson Texture Compression format 2 variant RG11_EAC, which provides two channels of unsigned data.
+ Ericsson Texture Compression format 2 variant RG11_EAC, which provides two channels of unsigned data.
</constant>
<constant name="FORMAT_ETC2_RG11S" value="33" enum="Format">
- Ericsson Texture Compression format 2 variant SIGNED_RG11_EAC, which provides two channels of signed data.
+ Ericsson Texture Compression format 2 variant SIGNED_RG11_EAC, which provides two channels of signed data.
</constant>
<constant name="FORMAT_ETC2_RGB8" value="34" enum="Format">
- Ericsson Texture Compression format 2 variant RGB8, which is a followup of ETC1 and compresses RGB888 data.
+ Ericsson Texture Compression format 2 variant RGB8, which is a followup of ETC1 and compresses RGB888 data.
</constant>
<constant name="FORMAT_ETC2_RGBA8" value="35" enum="Format">
- Ericsson Texture Compression format 2 variant RGBA8, which compresses RGBA8888 data with full alpha support.
+ Ericsson Texture Compression format 2 variant RGBA8, which compresses RGBA8888 data with full alpha support.
</constant>
<constant name="FORMAT_ETC2_RGB8A1" value="36" enum="Format">
- Ericsson Texture Compression format 2 variant RGB8_PUNCHTHROUGH_ALPHA1, which compresses RGBA data to make alpha either fully transparent or fully opaque.
+ Ericsson Texture Compression format 2 variant RGB8_PUNCHTHROUGH_ALPHA1, which compresses RGBA data to make alpha either fully transparent or fully opaque.
</constant>
<constant name="FORMAT_MAX" value="37" enum="Format">
</constant>
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index f3bff5a146..f537908625 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -333,7 +333,7 @@
</methods>
<signals>
<signal name="joy_connection_changed">
- <argument index="0" name="index" type="int">
+ <argument index="0" name="device" type="int">
</argument>
<argument index="1" name="connected" type="bool">
</argument>
diff --git a/doc/classes/InterpolatedCamera.xml b/doc/classes/InterpolatedCamera.xml
index d47df796a2..1ac7b5107e 100644
--- a/doc/classes/InterpolatedCamera.xml
+++ b/doc/classes/InterpolatedCamera.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
InterpolatedCamera is a [Camera] which smoothly moves to match a target node's position and rotation.
- If it is not [member enabled], or does not have a valid target set, InterpolatedCamera acts like a normal Camera.
+ If it is not [member enabled] or does not have a valid target set, InterpolatedCamera acts like a normal Camera.
</description>
<tutorials>
</tutorials>
@@ -18,16 +18,16 @@
<argument index="0" name="target" type="Object">
</argument>
<description>
- Set the node to move toward.
+ Sets the node to move toward and orient with.
</description>
</method>
</methods>
<members>
<member name="enabled" type="bool" setter="set_interpolation_enabled" getter="is_interpolation_enabled">
- If [code]true[/code], and a target is set, the camera will move automatically.
+ If [code]true[/code] and a target is set, the camera will move automatically.
</member>
<member name="speed" type="float" setter="set_speed" getter="get_speed">
- How quickly the camera moves.
+ How quickly the camera moves toward its target. Higher values will result in tighter camera motion.
</member>
<member name="target" type="NodePath" setter="set_target_path" getter="get_target_path">
The target's [NodePath].
diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml
index e4db5aeb84..bd1d6be4f5 100644
--- a/doc/classes/ItemList.xml
+++ b/doc/classes/ItemList.xml
@@ -87,6 +87,15 @@
<description>
</description>
</method>
+ <method name="get_item_icon_modulate" qualifiers="const">
+ <return type="Color">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <description>
+ Returns a [Color] modulating item's icon at the specified index.
+ </description>
+ </method>
<method name="get_item_icon_region" qualifiers="const">
<return type="Rect2">
</return>
@@ -135,6 +144,13 @@
Returns the current vertical scroll bar for the List.
</description>
</method>
+ <method name="is_anything_selected">
+ <return type="bool">
+ </return>
+ <description>
+ Returns [code]true[/code] if one or more items are selected.
+ </description>
+ </method>
<method name="is_item_disabled" qualifiers="const">
<return type="bool">
</return>
@@ -171,6 +187,17 @@
Returns whether or not item at the specified index is currently selected.
</description>
</method>
+ <method name="move_item">
+ <return type="void">
+ </return>
+ <argument index="0" name="from_idx" type="int">
+ </argument>
+ <argument index="1" name="to_idx" type="int">
+ </argument>
+ <description>
+ Moves item at index [code]from_idx[/code] to [code]to_idx[/code].
+ </description>
+ </method>
<method name="remove_item">
<return type="void">
</return>
@@ -225,6 +252,17 @@
Set (or replace) icon of the item at the specified index.
</description>
</method>
+ <method name="set_item_icon_modulate">
+ <return type="void">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="modulate" type="Color">
+ </argument>
+ <description>
+ Sets a modulating [Color] for item's icon at the specified index.
+ </description>
+ </method>
<method name="set_item_icon_region">
<return type="void">
</return>
@@ -306,6 +344,13 @@
Ensure item at specified index is not selected.
</description>
</method>
+ <method name="unselect_all">
+ <return type="void">
+ </return>
+ <description>
+ Ensure there are no items selected.
+ </description>
+ </method>
</methods>
<members>
<member name="allow_reselect" type="bool" setter="set_allow_reselect" getter="get_allow_reselect">
diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml
new file mode 100644
index 0000000000..591d715174
--- /dev/null
+++ b/doc/classes/MultiplayerAPI.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="MultiplayerAPI" inherits="Reference" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="add_peer">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="clear">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="connected_to_server">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="connection_failed">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="del_peer">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_network_connected_peers" qualifiers="const">
+ <return type="PoolIntArray">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_network_unique_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_rpc_sender_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="has_network_peer" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="is_network_server" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="poll">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="server_disconnected">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="set_root_node">
+ <return type="void">
+ </return>
+ <argument index="0" name="node" type="Node">
+ </argument>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="network_peer" type="NetworkedMultiplayerPeer" setter="set_network_peer" getter="get_network_peer">
+ </member>
+ <member name="refuse_new_network_connections" type="bool" setter="set_refuse_new_network_connections" getter="is_refusing_new_network_connections">
+ </member>
+ </members>
+ <signals>
+ <signal name="connected_to_server">
+ <description>
+ </description>
+ </signal>
+ <signal name="connection_failed">
+ <description>
+ </description>
+ </signal>
+ <signal name="network_peer_connected">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="network_peer_disconnected">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="server_disconnected">
+ <description>
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/Navigation.xml b/doc/classes/Navigation.xml
index b3b9ada26c..08f22d49d3 100644
--- a/doc/classes/Navigation.xml
+++ b/doc/classes/Navigation.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Navigation" inherits="Spatial" category="Core" version="3.1">
<brief_description>
- A collection of [code]NavigationMesh[/code] resources and methods used for pathfinding.
+ Mesh-based navigation and pathfinding node.
</brief_description>
<description>
- The Navigation node is used for basic or advanced navigation. By default it will automatically collect all child [code]NavigationMesh[/code] resources, but they can also be added on the fly through scripting. It can be used for generating a simple path between two points or it can be used to ensure that a navigation agent is angled perfectly to the terrain it is navigating.
+ Provides navigation and pathfinding within a collection of [NavigationMesh]es. By default these will be automatically collected from child [NavigationMeshInstance] nodes, but they can also be added on the fly with [method navmesh_add]. In addition to basic pathfinding, this class also assists with aligning navigation agents with the meshes they are navigating on.
</description>
<tutorials>
</tutorials>
@@ -17,7 +17,7 @@
<argument index="0" name="to_point" type="Vector3">
</argument>
<description>
- Returns the closest navigation point to the point passed.
+ Returns the navigation point closest to the point given. Points are in local coordinate space.
</description>
</method>
<method name="get_closest_point_normal">
@@ -26,7 +26,7 @@
<argument index="0" name="to_point" type="Vector3">
</argument>
<description>
- Returns the surface normal of the navigation mesh at the point passed. For instance, if the point passed was at a 45 degree slope it would return something like (0.5,0.5,0). This is useful for rotating a navigation agent in accordance with the [code]NavigationMesh[/code].
+ Returns the surface normal at the navigation point closest to the point given. Useful for rotating a navigation agent according to the navigation mesh it moves on.
</description>
</method>
<method name="get_closest_point_owner">
@@ -35,7 +35,7 @@
<argument index="0" name="to_point" type="Vector3">
</argument>
<description>
- Returns the nearest [code]NavigationMeshInstance[/code] to the point passed.
+ Returns the owner of the [NavigationMesh] which contains the navigation point closest to the point given. This is usually a [NavigtionMeshInstance]. For meshes added via [method navmesh_add], returns the owner that was given (or [code]null[/code] if the [code]owner[/code] parameter was omitted).
</description>
</method>
<method name="get_closest_point_to_segment">
@@ -48,7 +48,7 @@
<argument index="2" name="use_collision" type="bool" default="false">
</argument>
<description>
- Returns the nearest point to the line segment passed. The third optional parameter takes collisions into account.
+ Returns the navigation point closest to the given line segment. When enabling [code]use_collision[/code], only considers intersection points between segment and navigation meshes. If multiple intersection points are found, the one closest to the segment start point is returned.
</description>
</method>
<method name="get_simple_path">
@@ -61,7 +61,7 @@
<argument index="2" name="optimize" type="bool" default="true">
</argument>
<description>
- Returns a path of points as a [code]PoolVector3Array[/code]. If [code]optimize[/code] is false the [code]NavigationMesh[/code] agent properties will be taken into account, otherwise it will return the nearest path and ignore agent radius, height, etc.
+ Returns the path between two given points. Points are in local coordinate space. If [code]optimize[/code] is [code]true[/code] (the default), the agent properties associated with each [NavigationMesh] (raidus, height, etc.) are considered in the path calculation, otherwise they are ignored.
</description>
</method>
<method name="navmesh_add">
@@ -74,7 +74,7 @@
<argument index="2" name="owner" type="Object" default="null">
</argument>
<description>
- Adds a [code]NavigationMesh[/code] to the list of NavigationMesh's in this node. Returns an id. Its position, rotation and scale are associated with the [code]Transform[/code] passed. The [code]Node[/code] (or [code]Object[/code]) that owns this node is an optional parameter.
+ Adds a [NavigationMesh]. Returns an ID for use with [method navmesh_remove] or [method navmesh_set_transform]. If given, a [Transform2D] is applied to the polygon. The optional [code]owner[/code] is used as return value for [method get_closest_point_owner].
</description>
</method>
<method name="navmesh_remove">
@@ -83,7 +83,7 @@
<argument index="0" name="id" type="int">
</argument>
<description>
- Removes a [code]NavigationMesh[/code] from the list of NavigationMesh's in this node.
+ Removes the [NavigationMesh] with the given ID.
</description>
</method>
<method name="navmesh_set_transform">
@@ -94,13 +94,13 @@
<argument index="1" name="xform" type="Transform">
</argument>
<description>
- Associates a [code]NavigationMesh[/code]'s id with a [code]Transform[/code]. Its position, rotation and scale are based on the [code]Transform[/code] passed.
+ Sets the transform applied to the [NavigationMesh] with the given ID.
</description>
</method>
</methods>
<members>
<member name="up_vector" type="Vector3" setter="set_up_vector" getter="get_up_vector">
- Defines which direction is up. The default defines 0,1,0 as up which is the world up direction. To make this a ceiling use 0,-1,0 to define down as up.
+ Defines which direction is up. By default this is [code](0, 1, 0)[/code], which is the world up direction.
</member>
</members>
<constants>
diff --git a/doc/classes/Navigation2D.xml b/doc/classes/Navigation2D.xml
index d2f4513372..364da55f99 100644
--- a/doc/classes/Navigation2D.xml
+++ b/doc/classes/Navigation2D.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Navigation2D" inherits="Node2D" category="Core" version="3.1">
<brief_description>
- Class to assist with character navigation and pathfinding.
+ 2D navigation and pathfinding node.
</brief_description>
<description>
+ Navigation2D provides navigation and pathfinding within a 2D area, specified as a collection of [NavigationPolygon] resources. By default these are automatically collected from child [NavigationPolygonInstance] nodes, but they can also be added on the fly with [method navpoly_add].
</description>
<tutorials>
</tutorials>
@@ -16,6 +17,7 @@
<argument index="0" name="to_point" type="Vector2">
</argument>
<description>
+ Returns the navigation point closest to the point given. Points are in local coordinate space.
</description>
</method>
<method name="get_closest_point_owner">
@@ -24,6 +26,7 @@
<argument index="0" name="to_point" type="Vector2">
</argument>
<description>
+ Returns the owner of the [NavigationPolygon] which contains the navigation point closest to the point given. This is usually a [NavigtionPolygonInstance]. For polygons added via [method navpoly_add], returns the owner that was given (or [code]null[/code] if the [code]owner[/code] parameter was omitted).
</description>
</method>
<method name="get_simple_path">
@@ -36,6 +39,7 @@
<argument index="2" name="optimize" type="bool" default="true">
</argument>
<description>
+ Returns the path between two given points. Points are in local coordinate space. If [code]optimize[/code] is [code]true[/code] (the default), the path is smoothed by merging path segments where possible.
</description>
</method>
<method name="navpoly_add">
@@ -48,6 +52,7 @@
<argument index="2" name="owner" type="Object" default="null">
</argument>
<description>
+ Adds a [NavigationPolygon]. Returns an ID for use with [method navpoly_remove] or [method navpoly_set_transform]. If given, a [Transform2D] is applied to the polygon. The optional [code]owner[/code] is used as return value for [method get_closest_point_owner].
</description>
</method>
<method name="navpoly_remove">
@@ -56,6 +61,7 @@
<argument index="0" name="id" type="int">
</argument>
<description>
+ Removes the [NavigationPolygon] with the given ID.
</description>
</method>
<method name="navpoly_set_transform">
@@ -66,6 +72,7 @@
<argument index="1" name="xform" type="Transform2D">
</argument>
<description>
+ Sets the transform applied to the [NavigationPolygon] with the given ID.
</description>
</method>
</methods>
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index 9129f64340..5d0f70bc59 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -453,7 +453,30 @@
<return type="void">
</return>
<description>
- Prints the scene hierarchy of this node and all it's children to stdout. Used mainly for debugging purposes.
+ Prints the tree to stdout. Used mainly for debugging purposes. This version displays the path relative to the current node, and is good for copy/pasting into the [method get_node] function. Example output:
+ [codeblock]
+ TheGame
+ TheGame/Menu
+ TheGame/Menu/Label
+ TheGame/Menu/Camera2D
+ TheGame/SplashScreen
+ TheGame/SplashScreen/Camera2D
+ [/codeblock]
+ </description>
+ </method>
+ <method name="print_tree_pretty">
+ <return type="void">
+ </return>
+ <description>
+ Similar to [method print_tree], this prints the tree to stdout. This version displays a more graphical representation similar to what is displayed in the scene inspector. It is useful for inspecting larger trees. Example output:
+ [codeblock]
+ â”–â•´TheGame
+ â” â•´Menu
+ ┃ ┠╴Label
+ ┃ ┖╴Camera2D
+ â”–-SplashScreen
+ â”–â•´Camera2D
+ [/codeblock]
</description>
</method>
<method name="propagate_call">
@@ -739,9 +762,13 @@
</method>
</methods>
<members>
+ <member name="custom_multiplayer_api" type="MultiplayerAPI" setter="set_custom_multiplayer_api" getter="get_custom_multiplayer_api">
+ </member>
<member name="filename" type="String" setter="set_filename" getter="get_filename">
When a scene is instanced from a file, its topmost node contains the filename from which it was loaded.
</member>
+ <member name="multiplayer_api" type="MultiplayerAPI" setter="" getter="get_multiplayer_api">
+ </member>
<member name="name" type="String" setter="set_name" getter="get_name">
The name of the node. This name is unique among the siblings (other child nodes from the same parent). When set to an existing name, the node will be automatically renamed
</member>
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index a98385f1d2..d38a89874c 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -40,6 +40,7 @@
<return type="void">
</return>
<description>
+ Centers the window on the screen if in windowed mode.
</description>
</method>
<method name="delay_msec" qualifiers="const">
@@ -48,7 +49,7 @@
<argument index="0" name="msec" type="int">
</argument>
<description>
- Delay executing of the current thread by given milliseconds.
+ Delay execution of the current thread by given milliseconds.
</description>
</method>
<method name="delay_usec" qualifiers="const">
@@ -57,7 +58,7 @@
<argument index="0" name="usec" type="int">
</argument>
<description>
- Delay executing of the current thread by given microseconds.
+ Delay execution of the current thread by given microseconds.
</description>
</method>
<method name="dump_memory_to_file">
@@ -67,7 +68,7 @@
</argument>
<description>
Dumps the memory allocation ringlist to a file (only works in debug).
- Entry format per line: "Address - Size - Description"
+ Entry format per line: "Address - Size - Description".
</description>
</method>
<method name="dump_resources_to_file">
@@ -77,7 +78,7 @@
</argument>
<description>
Dumps all used resources to file (only works in debug).
- Entry format per line: "Resource Type : Resource Location"
+ Entry format per line: "Resource Type : Resource Location".
At the end of the file is a statistic of all used Resource Types.
</description>
</method>
@@ -116,6 +117,22 @@
Returns the scancode of the given string (e.g. "Escape")
</description>
</method>
+ <method name="get_audio_driver_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ Returns the total number of available audio drivers.
+ </description>
+ </method>
+ <method name="get_audio_driver_name" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="driver" type="int">
+ </argument>
+ <description>
+ Returns the audio driver name for the given index.
+ </description>
+ </method>
<method name="get_cmdline_args">
<return type="PoolStringArray">
</return>
@@ -242,6 +259,7 @@
<return type="Vector2">
</return>
<description>
+ Returns the window size including decorations like window borders.
</description>
</method>
<method name="get_scancode_string" qualifiers="const">
@@ -366,7 +384,7 @@
<return type="int">
</return>
<description>
- Return the current unix timestamp.
+ Returns the current unix epoch timestamp.
</description>
</method>
<method name="get_unix_time_from_datetime" qualifiers="const">
@@ -391,10 +409,25 @@
If the project name is empty, [code]user://[/code] falls back to [code]res://[/code].
</description>
</method>
+ <method name="get_video_driver_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_video_driver_name" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="driver" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_virtual_keyboard_height">
<return type="int">
</return>
<description>
+ Returns the on-screen keyboard's height in pixels. Returns 0 if there is no keyboard or it is currently hidden.
</description>
</method>
<method name="has_environment" qualifiers="const">
@@ -412,6 +445,7 @@
<argument index="0" name="tag_name" type="String">
</argument>
<description>
+ Returns [code]true[/code] if the feature for the given feature tag is supported in the currently running instance, depending on platform, build etc. Can be used to check whether you're currently running a debug build, on a certain platform or arch, etc. See feature tags documentation.
</description>
</method>
<method name="has_touchscreen_ui_hint" qualifiers="const">
@@ -478,6 +512,7 @@
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if the window should always be on top of other windows.
</description>
</method>
<method name="kill">
@@ -614,6 +649,7 @@
<argument index="0" name="enabled" type="bool">
</argument>
<description>
+ Sets whether the window should always be on top.
</description>
</method>
<method name="set_window_title">
@@ -654,27 +690,31 @@
The current screen index (starting from 0).
</member>
<member name="exit_code" type="int" setter="set_exit_code" getter="get_exit_code">
+ The exit code passed to the OS when the main loop exits.
</member>
<member name="keep_screen_on" type="bool" setter="set_keep_screen_on" getter="is_keep_screen_on">
+ If [code]true[/code] the engine tries to keep the screen on while the game is running. Useful on mobile.
</member>
<member name="low_processor_usage_mode" type="bool" setter="set_low_processor_usage_mode" getter="is_in_low_processor_usage_mode">
+ If [code]true[/code] the engine optimizes for low processor usage by only refreshing the screen if needed. Can improve battery consumption on mobile.
</member>
- <member name="screen_orientation" type="int" setter="set_screen_orientation" getter="get_screen_orientation" enum="_OS.ScreenOrientation">
+ <member name="screen_orientation" type="int" setter="set_screen_orientation" getter="get_screen_orientation" enum="OS.ScreenOrientation">
The current screen orientation.
</member>
<member name="vsync_enabled" type="bool" setter="set_use_vsync" getter="is_vsync_enabled">
+ If [code]true[/code] vertical synchronization (Vsync) is enabled.
</member>
<member name="window_borderless" type="bool" setter="set_borderless_window" getter="get_borderless_window">
- If [code]true[/code], removes the window frame.
+ If [code]true[/code] removes the window frame.
</member>
<member name="window_fullscreen" type="bool" setter="set_window_fullscreen" getter="is_window_fullscreen">
- If [code]true[/code], the window is fullscreen.
+ If [code]true[/code] the window is fullscreen.
</member>
<member name="window_maximized" type="bool" setter="set_window_maximized" getter="is_window_maximized">
- If [code]true[/code], the window is maximized.
+ If [code]true[/code] the window is maximized.
</member>
<member name="window_minimized" type="bool" setter="set_window_minimized" getter="is_window_minimized">
- If [code]true[/code], the window is minimized.
+ If [code]true[/code] the window is minimized.
</member>
<member name="window_position" type="Vector2" setter="set_window_position" getter="get_window_position">
The window position relative to the screen, the origin is the top left corner, +Y axis goes to the bottom and +X axis goes to the right.
@@ -688,82 +728,121 @@
</members>
<constants>
<constant name="DAY_SUNDAY" value="0" enum="Weekday">
+ Sunday.
</constant>
<constant name="DAY_MONDAY" value="1" enum="Weekday">
+ Monday.
</constant>
<constant name="DAY_TUESDAY" value="2" enum="Weekday">
+ Tuesday.
</constant>
<constant name="DAY_WEDNESDAY" value="3" enum="Weekday">
+ Wednesday.
</constant>
<constant name="DAY_THURSDAY" value="4" enum="Weekday">
+ Thursday.
</constant>
<constant name="DAY_FRIDAY" value="5" enum="Weekday">
+ Friday.
</constant>
<constant name="DAY_SATURDAY" value="6" enum="Weekday">
+ Saturday.
</constant>
<constant name="MONTH_JANUARY" value="1" enum="Month">
+ January.
</constant>
<constant name="MONTH_FEBRUARY" value="2" enum="Month">
+ February.
</constant>
<constant name="MONTH_MARCH" value="3" enum="Month">
+ March.
</constant>
<constant name="MONTH_APRIL" value="4" enum="Month">
+ April.
</constant>
<constant name="MONTH_MAY" value="5" enum="Month">
+ May.
</constant>
<constant name="MONTH_JUNE" value="6" enum="Month">
+ June.
</constant>
<constant name="MONTH_JULY" value="7" enum="Month">
+ July.
</constant>
<constant name="MONTH_AUGUST" value="8" enum="Month">
+ August.
</constant>
<constant name="MONTH_SEPTEMBER" value="9" enum="Month">
+ September.
</constant>
<constant name="MONTH_OCTOBER" value="10" enum="Month">
+ October.
</constant>
<constant name="MONTH_NOVEMBER" value="11" enum="Month">
+ November.
</constant>
<constant name="MONTH_DECEMBER" value="12" enum="Month">
+ December.
</constant>
<constant name="SCREEN_ORIENTATION_LANDSCAPE" value="0" enum="ScreenOrientation">
+ Landscape screen orientation.
</constant>
<constant name="SCREEN_ORIENTATION_PORTRAIT" value="1" enum="ScreenOrientation">
+ Portrait screen orientation.
</constant>
<constant name="SCREEN_ORIENTATION_REVERSE_LANDSCAPE" value="2" enum="ScreenOrientation">
+ Reverse landscape screen orientation.
</constant>
<constant name="SCREEN_ORIENTATION_REVERSE_PORTRAIT" value="3" enum="ScreenOrientation">
+ Reverse portrait screen orientation.
</constant>
<constant name="SCREEN_ORIENTATION_SENSOR_LANDSCAPE" value="4" enum="ScreenOrientation">
+ Uses landscape or reverse landscape based on the hardware sensor.
</constant>
<constant name="SCREEN_ORIENTATION_SENSOR_PORTRAIT" value="5" enum="ScreenOrientation">
+ Uses portrait or reverse portrait based on the hardware sensor.
</constant>
<constant name="SCREEN_ORIENTATION_SENSOR" value="6" enum="ScreenOrientation">
+ Uses most suitable orientation based on the hardware sensor.
</constant>
<constant name="SYSTEM_DIR_DESKTOP" value="0" enum="SystemDir">
+ Desktop directory path.
</constant>
<constant name="SYSTEM_DIR_DCIM" value="1" enum="SystemDir">
+ DCIM (Digital Camera Images) directory path.
</constant>
<constant name="SYSTEM_DIR_DOCUMENTS" value="2" enum="SystemDir">
+ Documents directory path.
</constant>
<constant name="SYSTEM_DIR_DOWNLOADS" value="3" enum="SystemDir">
+ Downloads directory path.
</constant>
<constant name="SYSTEM_DIR_MOVIES" value="4" enum="SystemDir">
+ Movies directory path.
</constant>
<constant name="SYSTEM_DIR_MUSIC" value="5" enum="SystemDir">
+ Music directory path.
</constant>
<constant name="SYSTEM_DIR_PICTURES" value="6" enum="SystemDir">
+ Pictures directory path.
</constant>
<constant name="SYSTEM_DIR_RINGTONES" value="7" enum="SystemDir">
+ Ringtones directory path.
</constant>
<constant name="POWERSTATE_UNKNOWN" value="0" enum="PowerState">
+ Unknown powerstate.
</constant>
<constant name="POWERSTATE_ON_BATTERY" value="1" enum="PowerState">
+ Unplugged, running on battery.
</constant>
<constant name="POWERSTATE_NO_BATTERY" value="2" enum="PowerState">
+ Plugged in, no battery available.
</constant>
<constant name="POWERSTATE_CHARGING" value="3" enum="PowerState">
+ Plugged in, battery charging.
</constant>
<constant name="POWERSTATE_CHARGED" value="4" enum="PowerState">
+ Plugged in, battery fully charged.
</constant>
</constants>
</class>
diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml
index de6f60c384..8cb53dd98e 100644
--- a/doc/classes/OptionButton.xml
+++ b/doc/classes/OptionButton.xml
@@ -194,6 +194,13 @@
</member>
</members>
<signals>
+ <signal name="item_focused">
+ <argument index="0" name="ID" type="int">
+ </argument>
+ <description>
+ This signal is emitted when user navigated to an item using [code]ui_up[/code] or [code]ui_down[/code] action. ID of the item selected is passed as argument (if no IDs were added, ID will be just the item index).
+ </description>
+ </signal>
<signal name="item_selected">
<argument index="0" name="ID" type="int">
</argument>
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index 70e6fab5b6..166a4be2b0 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -108,6 +108,31 @@
Add a new item with text "label". An id can optionally be provided, as well as an accelerator keybinding. If no id is provided, one will be created from the index.
</description>
</method>
+ <method name="add_radio_check_item">
+ <return type="void">
+ </return>
+ <argument index="0" name="label" type="String">
+ </argument>
+ <argument index="1" name="id" type="int" default="-1">
+ </argument>
+ <argument index="2" name="accel" type="int" default="0">
+ </argument>
+ <description>
+ The same as [method add_check_item] but the inserted item will look as a radio button. Remember this is just cosmetic and you have to add the logic for checking/unchecking items in radio groups.
+ </description>
+ </method>
+ <method name="add_radio_check_shortcut">
+ <return type="void">
+ </return>
+ <argument index="0" name="shortcut" type="ShortCut">
+ </argument>
+ <argument index="1" name="id" type="int" default="-1">
+ </argument>
+ <argument index="2" name="global" type="bool" default="false">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="add_separator">
<return type="void">
</return>
@@ -239,7 +264,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
- Return whether the item at index "idx" has a checkbox. Note that checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually.
+ Return whether the item at index "idx" is checkable in some way, i.e., whether has a checkbox or radio button. Note that checkable items just display a checkmark or radio button, but don't have any built-in checking behavior and must be checked/unchecked manually.
</description>
</method>
<method name="is_item_checked" qualifiers="const">
@@ -248,7 +273,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
- Return the checkstate status of the item at index "idx".
+ Return whether the item at index "idx" is checked.
</description>
</method>
<method name="is_item_disabled" qualifiers="const">
@@ -260,6 +285,15 @@
Return whether the item at index "idx" is disabled. When it is disabled it can't be selected, or its action invoked.
</description>
</method>
+ <method name="is_item_radio_checkable" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <description>
+ Return whether the item at index "idx" has radio-button-style checkability. Remember this is just cosmetic and you have to add the logic for checking/unchecking items in radio groups.
+ </description>
+ </method>
<method name="is_item_separator" qualifiers="const">
<return type="bool">
</return>
@@ -300,6 +334,18 @@
Set whether the item at index "idx" has a checkbox. Note that checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually.
</description>
</method>
+ <method name="set_item_as_radio_checkable">
+ <return type="void">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="enable" type="bool">
+ </argument>
+ <description>
+ The same as [method set_item_as_checkable] but placing a radio button in case of enabling. If used for disabling, it's the same.
+ Remember this is just cosmetic and you have to add the logic for checking/unchecking items in radio groups.
+ </description>
+ </method>
<method name="set_item_as_separator">
<return type="void">
</return>
@@ -445,6 +491,13 @@
</member>
</members>
<signals>
+ <signal name="id_focused">
+ <argument index="0" name="ID" type="int">
+ </argument>
+ <description>
+ This event is emitted when user navigated to an item of some id using [code]ui_up[/code] or [code]ui_down[/code] action.
+ </description>
+ </signal>
<signal name="id_pressed">
<argument index="0" name="ID" type="int">
</argument>
@@ -483,6 +536,10 @@
</theme_item>
<theme_item name="panel_disabled" type="StyleBox">
</theme_item>
+ <theme_item name="radio_checked" type="Texture">
+ </theme_item>
+ <theme_item name="radio_unchecked" type="Texture">
+ </theme_item>
<theme_item name="separator" type="StyleBox">
</theme_item>
<theme_item name="submenu" type="Texture">
diff --git a/doc/classes/Quat.xml b/doc/classes/Quat.xml
index 3a258011b2..33f2b9758b 100644
--- a/doc/classes/Quat.xml
+++ b/doc/classes/Quat.xml
@@ -10,6 +10,7 @@
It can be used to perform SLERP (spherical-linear interpolation) between two rotations.
</description>
<tutorials>
+ http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html#interpolating-with-quaternions
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index be90288308..8846616851 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -1,12 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="SceneTree" inherits="MainLoop" category="Core" version="3.1">
<brief_description>
+ SceneTree manages a hierarchy of nodes.
</brief_description>
<description>
+ As one of the most important classes, the [code]SceneTree[/code] manages the hierarchy of nodes in a scene as well as scenes themselves. Nodes can be added, retrieved and removed. The whole scene tree (and thus the current scene) can be paused. Scenes can be loaded, switched and reloaded. You can also use the SceneTree to organize your nodes into groups: every node can be assigned as many groups as you want to create, e.g. a "enemy" group. You can then iterate these groups or even call methods and set properties on all the group's members at once.
</description>
<tutorials>
http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scene_tree.html
- http://docs.godotengine.org/en/3.0/tutorials/viewports/multiple_resolutions.html
+ http://docs.godotengine.org/en/3.0/tutorials/viewports/multiple_resolutions.html
</tutorials>
<demos>
</demos>
@@ -19,6 +21,7 @@
<argument index="1" name="method" type="String">
</argument>
<description>
+ Calls [code]method[/code] on each member of the given group.
</description>
</method>
<method name="call_group_flags" qualifiers="vararg">
@@ -31,6 +34,7 @@
<argument index="2" name="method" type="String">
</argument>
<description>
+ Calls [code]method[/code] on each member of the given group, respecting the given [enum GROUP_CALL] flags.
</description>
</method>
<method name="change_scene">
@@ -39,6 +43,7 @@
<argument index="0" name="path" type="String">
</argument>
<description>
+ Changes to the scene at the given [code]path[/code].
</description>
</method>
<method name="change_scene_to">
@@ -47,6 +52,7 @@
<argument index="0" name="packed_scene" type="PackedScene">
</argument>
<description>
+ Changes to the given [PackedScene].
</description>
</method>
<method name="create_timer">
@@ -57,6 +63,7 @@
<argument index="1" name="pause_mode_process" type="bool" default="true">
</argument>
<description>
+ Returns a [SceneTreeTimer] which will [signal SceneTreeTimer.timeout] after the given time in seconds elapsed in this SceneTree. If [code]pause_mode_process[/code] is set to false, pausing the SceneTree will also pause the timer.
</description>
</method>
<method name="get_frame" qualifiers="const">
@@ -69,18 +76,21 @@
<return type="PoolIntArray">
</return>
<description>
+ Returns the peer IDs of all connected peers of this SceneTree's [member network_peer].
</description>
</method>
<method name="get_network_unique_id" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the unique peer ID of this SceneTree's [member network_peer].
</description>
</method>
<method name="get_node_count" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the number of nodes in this SceneTree.
</description>
</method>
<method name="get_nodes_in_group">
@@ -89,12 +99,14 @@
<argument index="0" name="group" type="String">
</argument>
<description>
+ Returns all nodes assigned to the given group.
</description>
</method>
<method name="get_rpc_sender_id" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the sender's peer ID for the most recently received RPC call.
</description>
</method>
<method name="has_group" qualifiers="const">
@@ -103,26 +115,28 @@
<argument index="0" name="name" type="String">
</argument>
<description>
+ Returns [code]true[/code] if the given group exists.
</description>
</method>
<method name="has_network_peer" qualifiers="const">
<return type="bool">
</return>
<description>
- Returns true if there is a [NetworkedMultiplayerPeer] set (with [method SceneTree.set_network_peer]).
+ Returns [code]true[/code] if there is a [member network_peer] set.
</description>
</method>
<method name="is_input_handled">
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if the most recent InputEvent was marked as handled with [method set_input_as_handled].
</description>
</method>
<method name="is_network_server" qualifiers="const">
<return type="bool">
</return>
<description>
- Returns true if this SceneTree's [NetworkedMultiplayerPeer] is in server mode (listening for connections).
+ Returns [code]true[/code] if this SceneTree's [member network_peer] is in server mode (listening for connections).
</description>
</method>
<method name="notify_group">
@@ -133,6 +147,7 @@
<argument index="1" name="notification" type="int">
</argument>
<description>
+ Sends the given notification to all members of the [code]group[/code].
</description>
</method>
<method name="notify_group_flags">
@@ -145,6 +160,7 @@
<argument index="2" name="notification" type="int">
</argument>
<description>
+ Sends the given notification to all members of the [code]group[/code], respecting the given [enum GROUP_CALL] flags.
</description>
</method>
<method name="queue_delete">
@@ -153,18 +169,21 @@
<argument index="0" name="obj" type="Object">
</argument>
<description>
+ Queues the given object for deletion, delaying the call to [method Object.free] to after the current frame.
</description>
</method>
<method name="quit">
<return type="void">
</return>
<description>
+ Quits the application.
</description>
</method>
<method name="reload_current_scene">
<return type="int" enum="Error">
</return>
<description>
+ Reloads the currently active scene.
</description>
</method>
<method name="set_auto_accept_quit">
@@ -173,6 +192,7 @@
<argument index="0" name="enabled" type="bool">
</argument>
<description>
+ If [code]true[/code] the application automatically accepts quitting.
</description>
</method>
<method name="set_group">
@@ -185,6 +205,7 @@
<argument index="2" name="value" type="Variant">
</argument>
<description>
+ Sets the given [code]property[/code] to [code]value[/code] on all members of the given group.
</description>
</method>
<method name="set_group_flags">
@@ -199,12 +220,14 @@
<argument index="3" name="value" type="Variant">
</argument>
<description>
+ Sets the given [code]property[/code] to [code]value[/code] on all members of the given group, respecting the given [enum GROUP_CALL] flags.
</description>
</method>
<method name="set_input_as_handled">
<return type="void">
</return>
<description>
+ Marks the most recent input event as handled.
</description>
</method>
<method name="set_quit_on_go_back">
@@ -213,6 +236,7 @@
<argument index="0" name="enabled" type="bool">
</argument>
<description>
+ If [code]true[/code] the application quits automatically on going back (e.g. on Android).
</description>
</method>
<method name="set_screen_stretch">
@@ -227,37 +251,48 @@
<argument index="3" name="shrink" type="float" default="1">
</argument>
<description>
+ Configures screen stretching to the given [enum StretchMode], [enum StretchAspect], minimum size and [code]shrink[/code].
</description>
</method>
</methods>
<members>
<member name="current_scene" type="Node" setter="set_current_scene" getter="get_current_scene">
+ The current scene.
</member>
<member name="debug_collisions_hint" type="bool" setter="set_debug_collisions_hint" getter="is_debugging_collisions_hint">
</member>
<member name="debug_navigation_hint" type="bool" setter="set_debug_navigation_hint" getter="is_debugging_navigation_hint">
</member>
<member name="edited_scene_root" type="Node" setter="set_edited_scene_root" getter="get_edited_scene_root">
+ The root of the edited scene.
+ </member>
+ <member name="multiplayer_api" type="MultiplayerAPI" setter="set_multiplayer_api" getter="get_multiplayer_api">
</member>
<member name="network_peer" type="NetworkedMultiplayerPeer" setter="set_network_peer" getter="get_network_peer">
The peer object to handle the RPC system (effectively enabling networking when set). Depending on the peer itself, the SceneTree will become a network server (check with [method is_network_server()]) and will set root node's network mode to master (see NETWORK_MODE_* constants in [Node]), or it will become a regular peer with root node set to slave. All child nodes are set to inherit the network mode by default. Handling of networking-related events (connection, disconnection, new clients) is done by connecting to SceneTree's signals.
</member>
<member name="paused" type="bool" setter="set_pause" getter="is_paused">
+ If [code]true[/code] the SceneTree is paused.
</member>
<member name="refuse_new_network_connections" type="bool" setter="set_refuse_new_network_connections" getter="is_refusing_new_network_connections">
+ If [code]true[/code] the SceneTree's [member network_peer] refuses new incoming connections.
</member>
<member name="root" type="Viewport" setter="" getter="get_root">
+ The SceneTree's [Viewport].
</member>
<member name="use_font_oversampling" type="bool" setter="set_use_font_oversampling" getter="is_using_font_oversampling">
+ If [code]true[/code] font oversampling is used.
</member>
</members>
<signals>
<signal name="connected_to_server">
<description>
+ Emitted whenever this SceneTree's [member network_peer] successfully connected to a server. Only emitted on clients.
</description>
</signal>
<signal name="connection_failed">
<description>
+ Emitted whenever this SceneTree's [member network_peer] fails to establish a connection to a server. Only emitted on clients.
</description>
</signal>
<signal name="files_dropped">
@@ -266,56 +301,67 @@
<argument index="1" name="screen" type="int">
</argument>
<description>
+ Emitted whenever files are drag-and-dropped onto the window.
</description>
</signal>
<signal name="idle_frame">
<description>
+ Emitted immediately before [method Node._process] is called on every node in the SceneTree.
</description>
</signal>
<signal name="network_peer_connected">
<argument index="0" name="id" type="int">
</argument>
<description>
+ Emitted whenever this SceneTree's [member network_peer] connects with a new peer. ID is the peer ID of the new peer. Clients get notified when other clients connect to the same server. Upon connecting to a server, a client also receives this signal for the server (with ID being 1).
</description>
</signal>
<signal name="network_peer_disconnected">
<argument index="0" name="id" type="int">
</argument>
<description>
+ Emitted whenever this SceneTree's [member network_peer] disconnects from a peer. Clients get notified when other clients disconnect from the same server.
</description>
</signal>
<signal name="node_added">
<argument index="0" name="node" type="Object">
</argument>
<description>
+ Emitted whenever a node is added to the SceneTree.
</description>
</signal>
<signal name="node_configuration_warning_changed">
<argument index="0" name="node" type="Object">
</argument>
<description>
+ Emitted when a node's configuration changed. Only emitted in tool mode.
</description>
</signal>
<signal name="node_removed">
<argument index="0" name="node" type="Object">
</argument>
<description>
+ Emitted whenever a node is removed from the SceneTree.
</description>
</signal>
<signal name="physics_frame">
<description>
+ Emitted immediately before [method Node._physics_process] is called on every node in the SceneTree.
</description>
</signal>
<signal name="screen_resized">
<description>
+ Emitted whenever the screen resolution (fullscreen) or window size (windowed) changes.
</description>
</signal>
<signal name="server_disconnected">
<description>
+ Emitted whenever this SceneTree's [member network_peer] disconnected from server. Only emitted on clients.
</description>
</signal>
<signal name="tree_changed">
<description>
+ Emitted whenever the SceneTree hierarchy changed (children being moved or renamed, etc.).
</description>
</signal>
</signals>
diff --git a/doc/classes/ScrollContainer.xml b/doc/classes/ScrollContainer.xml
index 79b70fec99..28fbdea58d 100644
--- a/doc/classes/ScrollContainer.xml
+++ b/doc/classes/ScrollContainer.xml
@@ -13,6 +13,8 @@
<methods>
</methods>
<members>
+ <member name="scroll_deadzone" type="int" setter="set_deadzone" getter="get_deadzone">
+ </member>
<member name="scroll_horizontal" type="int" setter="set_h_scroll" getter="get_h_scroll">
The current horizontal scroll value.
</member>
@@ -26,6 +28,16 @@
If [code]true[/code], enables vertical scrolling.
</member>
</members>
+ <signals>
+ <signal name="scroll_ended">
+ <description>
+ </description>
+ </signal>
+ <signal name="scroll_started">
+ <description>
+ </description>
+ </signal>
+ </signals>
<constants>
</constants>
</class>
diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml
index d8644c5419..7d78d71330 100644
--- a/doc/classes/SurfaceTool.xml
+++ b/doc/classes/SurfaceTool.xml
@@ -196,8 +196,11 @@
<method name="generate_normals">
<return type="void">
</return>
+ <argument index="0" name="flip" type="bool" default="false">
+ </argument>
<description>
Generates normals from Vertices so you do not have to do it manually.
+ Setting "flip" [code]true[/code] inverts resulting normals.
</description>
</method>
<method name="generate_tangents">
diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml
index 330487adf7..11e94abc03 100644
--- a/doc/classes/TabContainer.xml
+++ b/doc/classes/TabContainer.xml
@@ -78,6 +78,12 @@
Returns the title of the tab at index [code]tab_idx[/code]. Tab titles default to the name of the indexed child node, but this can be overridden with [method set_tab_title].
</description>
</method>
+ <method name="get_tabs_rearrange_group" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="set_popup">
<return type="void">
</return>
@@ -120,11 +126,21 @@
Sets a title for the tab at index [code]tab_idx[/code]. Tab titles default to the name of the indexed child node, but this can be overridden with [method set_tab_title].
</description>
</method>
+ <method name="set_tabs_rearrange_group">
+ <return type="void">
+ </return>
+ <argument index="0" name="group_id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<members>
<member name="current_tab" type="int" setter="set_current_tab" getter="get_current_tab">
The current tab index. When set, this index's [Control] node's [code]visible[/code] property is set to [code]true[/code] and all others are set to [code]false[/code].
</member>
+ <member name="drag_to_rearrange_enabled" type="bool" setter="set_drag_to_rearrange_enabled" getter="get_drag_to_rearrange_enabled">
+ </member>
<member name="tab_align" type="int" setter="set_tab_align" getter="get_tab_align" enum="TabContainer.TabAlign">
The alignment of all tabs in the tab container. See the [code]ALIGN_*[/code] constants for details.
</member>
diff --git a/doc/classes/Tabs.xml b/doc/classes/Tabs.xml
index 615bc83c2d..0d5f1f0ba9 100644
--- a/doc/classes/Tabs.xml
+++ b/doc/classes/Tabs.xml
@@ -80,6 +80,12 @@
<description>
</description>
</method>
+ <method name="get_tabs_rearrange_group" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="move_tab">
<return type="void">
</return>
@@ -129,10 +135,20 @@
<description>
</description>
</method>
+ <method name="set_tabs_rearrange_group">
+ <return type="void">
+ </return>
+ <argument index="0" name="group_id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<members>
<member name="current_tab" type="int" setter="set_current_tab" getter="get_current_tab">
</member>
+ <member name="drag_to_rearrange_enabled" type="bool" setter="set_drag_to_rearrange_enabled" getter="get_drag_to_rearrange_enabled">
+ </member>
<member name="scrolling_enabled" type="bool" setter="set_scrolling_enabled" getter="get_scrolling_enabled">
</member>
<member name="tab_align" type="int" setter="set_tab_align" getter="get_tab_align" enum="Tabs.TabAlign">
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index 8a71cb2059..76d94fbfbc 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -130,6 +130,14 @@
<description>
</description>
</method>
+ <method name="get_keyword_color" qualifiers="const">
+ <return type="Color">
+ </return>
+ <argument index="0" name="keyword" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_line" qualifiers="const">
<return type="String">
</return>
@@ -193,6 +201,14 @@
<description>
</description>
</method>
+ <method name="has_keyword_color" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="keyword" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="insert_text_at_cursor">
<return type="void">
</return>
diff --git a/doc/classes/TextureProgress.xml b/doc/classes/TextureProgress.xml
index 18bd886b0b..2ae4ef2cf8 100644
--- a/doc/classes/TextureProgress.xml
+++ b/doc/classes/TextureProgress.xml
@@ -51,6 +51,12 @@
<member name="texture_under" type="Texture" setter="set_under_texture" getter="get_under_texture">
[Texture] that draws under the progress bar. The bar's background.
</member>
+ <member name="tint_over" type="Color" setter="set_tint_over" getter="get_tint_over">
+ </member>
+ <member name="tint_progress" type="Color" setter="set_tint_progress" getter="get_tint_progress">
+ </member>
+ <member name="tint_under" type="Color" setter="set_tint_under" getter="get_tint_under">
+ </member>
</members>
<constants>
<constant name="FILL_LEFT_TO_RIGHT" value="0" enum="FillMode">
diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml
index 10978970e9..4e218f5595 100644
--- a/doc/classes/TileSet.xml
+++ b/doc/classes/TileSet.xml
@@ -254,6 +254,14 @@
Return the texture offset of the tile.
</description>
</method>
+ <method name="tile_get_tile_mode" qualifiers="const">
+ <return type="int" enum="TileSet.TileMode">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="tile_set_light_occluder">
<return type="void">
</return>
@@ -410,6 +418,16 @@
Set the texture offset of the tile.
</description>
</method>
+ <method name="tile_set_tile_mode">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <argument index="1" name="tilemode" type="int" enum="TileSet.TileMode">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<constants>
<constant name="BITMASK_2X2" value="0" enum="BitmaskMode">
@@ -432,5 +450,11 @@
</constant>
<constant name="BIND_BOTTOMRIGHT" value="256" enum="AutotileBindings">
</constant>
+ <constant name="SINGLE_TILE" value="0" enum="TileMode">
+ </constant>
+ <constant name="AUTO_TILE" value="1" enum="TileMode">
+ </constant>
+ <constant name="ANIMATED_TILE" value="2" enum="TileMode">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/Transform.xml b/doc/classes/Transform.xml
index df5c2ab3ae..d9f9d8cc73 100644
--- a/doc/classes/Transform.xml
+++ b/doc/classes/Transform.xml
@@ -8,6 +8,7 @@
</description>
<tutorials>
http://docs.godotengine.org/en/3.0/tutorials/math/index.html
+ http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml
index b293ec3368..5ffe807606 100644
--- a/doc/classes/Vector2.xml
+++ b/doc/classes/Vector2.xml
@@ -84,10 +84,10 @@
<method name="cross">
<return type="float">
</return>
- <argument index="0" name="b" type="Vector2">
+ <argument index="0" name="with" type="Vector2">
</argument>
<description>
- Returns the 2-dimensional analog of the cross product with [code]b[/code].
+ Returns the 2-dimensional analog of the cross product with the given Vector2.
</description>
</method>
<method name="cubic_interpolate">
diff --git a/doc/classes/VideoPlayer.xml b/doc/classes/VideoPlayer.xml
index 48d09ccd95..d2639590a1 100644
--- a/doc/classes/VideoPlayer.xml
+++ b/doc/classes/VideoPlayer.xml
@@ -72,6 +72,12 @@
<member name="volume_db" type="float" setter="set_volume_db" getter="get_volume_db">
</member>
</members>
+ <signals>
+ <signal name="finished">
+ <description>
+ </description>
+ </signal>
+ </signals>
<constants>
</constants>
</class>
diff --git a/doc/classes/WebSocketClient.xml b/doc/classes/WebSocketClient.xml
index 79313c90c7..496baa5787 100644
--- a/doc/classes/WebSocketClient.xml
+++ b/doc/classes/WebSocketClient.xml
@@ -28,6 +28,10 @@
</description>
</method>
</methods>
+ <members>
+ <member name="verify_ssl" type="bool" setter="set_verify_ssl_enabled" getter="is_verify_ssl_enabled">
+ </member>
+ </members>
<signals>
<signal name="connection_closed">
<description>
diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp
index 0bb03d23ea..1e17e72532 100644
--- a/drivers/alsa/audio_driver_alsa.cpp
+++ b/drivers/alsa/audio_driver_alsa.cpp
@@ -37,19 +37,20 @@
#include <errno.h>
-Error AudioDriverALSA::init() {
-
- active = false;
- thread_exited = false;
- exit_thread = false;
- pcm_open = false;
- samples_in = NULL;
- samples_out = NULL;
-
+Error AudioDriverALSA::init_device() {
mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE);
speaker_mode = SPEAKER_MODE_STEREO;
channels = 2;
+ // If there is a specified device check that it is really present
+ if (device_name != "Default") {
+ Array list = get_device_list();
+ if (list.find(device_name) == -1) {
+ device_name = "Default";
+ new_device = "Default";
+ }
+ }
+
int status;
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
@@ -65,7 +66,16 @@ Error AudioDriverALSA::init() {
//6 chans - "plug:surround51"
//4 chans - "plug:surround40";
- status = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ if (device_name == "Default") {
+ status = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ } else {
+ String device = device_name;
+ int pos = device.find(";");
+ if (pos != -1) {
+ device = device.substr(0, pos);
+ }
+ status = snd_pcm_open(&pcm_handle, device.utf8().get_data(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ }
ERR_FAIL_COND_V(status < 0, ERR_CANT_OPEN);
@@ -129,15 +139,28 @@ Error AudioDriverALSA::init() {
status = snd_pcm_sw_params(pcm_handle, swparams);
CHECK_FAIL(status < 0);
- samples_in = memnew_arr(int32_t, period_size * channels);
- samples_out = memnew_arr(int16_t, period_size * channels);
+ samples_in.resize(period_size * channels);
+ samples_out.resize(period_size * channels);
snd_pcm_nonblock(pcm_handle, 0);
- mutex = Mutex::create();
- thread = Thread::create(AudioDriverALSA::thread_func, this);
-
return OK;
+}
+
+Error AudioDriverALSA::init() {
+
+ active = false;
+ thread_exited = false;
+ exit_thread = false;
+ pcm_open = false;
+
+ Error err = init_device();
+ if (err == OK) {
+ mutex = Mutex::create();
+ thread = Thread::create(AudioDriverALSA::thread_func, this);
+ }
+
+ return err;
};
void AudioDriverALSA::thread_func(void *p_udata) {
@@ -152,7 +175,7 @@ void AudioDriverALSA::thread_func(void *p_udata) {
} else {
ad->lock();
- ad->audio_server_process(ad->period_size, ad->samples_in);
+ ad->audio_server_process(ad->period_size, ad->samples_in.ptrw());
ad->unlock();
@@ -167,7 +190,7 @@ void AudioDriverALSA::thread_func(void *p_udata) {
while (todo) {
if (ad->exit_thread)
break;
- uint8_t *src = (uint8_t *)ad->samples_out;
+ uint8_t *src = (uint8_t *)ad->samples_out.ptr();
int wrote = snd_pcm_writei(ad->pcm_handle, (void *)(src + (total * ad->channels)), todo);
if (wrote < 0) {
@@ -193,6 +216,26 @@ void AudioDriverALSA::thread_func(void *p_udata) {
total += wrote;
todo -= wrote;
};
+
+ // User selected a new device, finish the current one so we'll init the new device
+ if (ad->device_name != ad->new_device) {
+ ad->device_name = ad->new_device;
+ ad->finish_device();
+
+ Error err = ad->init_device();
+ if (err != OK) {
+ ERR_PRINT("ALSA: init_device error");
+ ad->device_name = "Default";
+ ad->new_device = "Default";
+
+ err = ad->init_device();
+ if (err != OK) {
+ ad->active = false;
+ ad->exit_thread = true;
+ break;
+ }
+ }
+ }
};
ad->thread_exited = true;
@@ -213,6 +256,49 @@ AudioDriver::SpeakerMode AudioDriverALSA::get_speaker_mode() const {
return speaker_mode;
};
+Array AudioDriverALSA::get_device_list() {
+
+ Array list;
+
+ list.push_back("Default");
+
+ void **hints;
+
+ if (snd_device_name_hint(-1, "pcm", &hints) < 0)
+ return list;
+
+ for (void **n = hints; *n != NULL; n++) {
+ char *name = snd_device_name_get_hint(*n, "NAME");
+ char *desc = snd_device_name_get_hint(*n, "DESC");
+
+ if (name != NULL && !strncmp(name, "plughw", 6)) {
+ if (desc) {
+ list.push_back(String(name) + ";" + String(desc));
+ } else {
+ list.push_back(String(name));
+ }
+ }
+
+ if (desc != NULL)
+ free(desc);
+ if (name != NULL)
+ free(name);
+ }
+ snd_device_name_free_hint(hints);
+
+ return list;
+}
+
+String AudioDriverALSA::get_device() {
+
+ return device_name;
+}
+
+void AudioDriverALSA::set_device(String device) {
+
+ new_device = device;
+}
+
void AudioDriverALSA::lock() {
if (!thread || !mutex)
@@ -227,6 +313,14 @@ void AudioDriverALSA::unlock() {
mutex->unlock();
};
+void AudioDriverALSA::finish_device() {
+
+ if (pcm_open) {
+ snd_pcm_close(pcm_handle);
+ pcm_open = NULL;
+ }
+}
+
void AudioDriverALSA::finish() {
if (!thread)
@@ -235,17 +329,13 @@ void AudioDriverALSA::finish() {
exit_thread = true;
Thread::wait_to_finish(thread);
- if (pcm_open)
- snd_pcm_close(pcm_handle);
-
- if (samples_in) {
- memdelete_arr(samples_in);
- memdelete_arr(samples_out);
- };
+ finish_device();
memdelete(thread);
- if (mutex)
+ if (mutex) {
memdelete(mutex);
+ mutex = NULL;
+ }
thread = NULL;
};
@@ -254,6 +344,9 @@ AudioDriverALSA::AudioDriverALSA() {
mutex = NULL;
thread = NULL;
pcm_handle = NULL;
+
+ device_name = "Default";
+ new_device = "Default";
};
AudioDriverALSA::~AudioDriverALSA(){
diff --git a/drivers/alsa/audio_driver_alsa.h b/drivers/alsa/audio_driver_alsa.h
index 8ed60dfdc7..2878e100a2 100644
--- a/drivers/alsa/audio_driver_alsa.h
+++ b/drivers/alsa/audio_driver_alsa.h
@@ -44,8 +44,14 @@ class AudioDriverALSA : public AudioDriver {
snd_pcm_t *pcm_handle;
- int32_t *samples_in;
- int16_t *samples_out;
+ String device_name;
+ String new_device;
+
+ Vector<int32_t> samples_in;
+ Vector<int16_t> samples_out;
+
+ Error init_device();
+ void finish_device();
static void thread_func(void *p_udata);
@@ -71,6 +77,9 @@ public:
virtual void start();
virtual int get_mix_rate() const;
virtual SpeakerMode get_speaker_mode() const;
+ virtual Array get_device_list();
+ virtual String get_device();
+ virtual void set_device(String device);
virtual void lock();
virtual void unlock();
virtual void finish();
diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp
index 0b39f9ebc3..c84469f26f 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.cpp
+++ b/drivers/coreaudio/audio_driver_coreaudio.cpp
@@ -37,16 +37,22 @@
#define kOutputBus 0
#ifdef OSX_ENABLED
-static OSStatus outputDeviceAddressCB(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData) {
+OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID,
+ UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses,
+ void *inClientData) {
AudioDriverCoreAudio *driver = (AudioDriverCoreAudio *)inClientData;
- driver->reopen();
+ // If our selected device is the Default call set_device to update the
+ // kAudioOutputUnitProperty_CurrentDevice property
+ if (driver->device_name == "Default") {
+ driver->set_device("Default");
+ }
return noErr;
}
#endif
-Error AudioDriverCoreAudio::initDevice() {
+Error AudioDriverCoreAudio::init_device() {
AudioComponentDescription desc;
zeromem(&desc, sizeof(desc));
desc.componentType = kAudioUnitType_Output;
@@ -129,7 +135,7 @@ Error AudioDriverCoreAudio::initDevice() {
return OK;
}
-Error AudioDriverCoreAudio::finishDevice() {
+Error AudioDriverCoreAudio::finish_device() {
OSStatus result;
if (active) {
@@ -153,49 +159,18 @@ Error AudioDriverCoreAudio::init() {
channels = 2;
#ifdef OSX_ENABLED
- outputDeviceAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
- outputDeviceAddress.mScope = kAudioObjectPropertyScopeGlobal;
- outputDeviceAddress.mElement = kAudioObjectPropertyElementMaster;
+ AudioObjectPropertyAddress prop;
+ prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
+ prop.mScope = kAudioObjectPropertyScopeGlobal;
+ prop.mElement = kAudioObjectPropertyElementMaster;
- result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &outputDeviceAddress, &outputDeviceAddressCB, this);
+ result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this);
ERR_FAIL_COND_V(result != noErr, FAILED);
#endif
- return initDevice();
+ return init_device();
};
-Error AudioDriverCoreAudio::reopen() {
- bool restart = false;
-
- lock();
-
- if (active) {
- restart = true;
- }
-
- Error err = finishDevice();
- if (err != OK) {
- ERR_PRINT("finishDevice failed");
- unlock();
- return err;
- }
-
- err = initDevice();
- if (err != OK) {
- ERR_PRINT("initDevice failed");
- unlock();
- return err;
- }
-
- if (restart) {
- start();
- }
-
- unlock();
-
- return OK;
-}
-
OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
@@ -257,6 +232,150 @@ AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const {
return get_speaker_mode_by_total_channels(channels);
};
+#ifdef OSX_ENABLED
+
+Array AudioDriverCoreAudio::get_device_list() {
+
+ Array list;
+
+ list.push_back("Default");
+
+ AudioObjectPropertyAddress prop;
+
+ prop.mSelector = kAudioHardwarePropertyDevices;
+ prop.mScope = kAudioObjectPropertyScopeGlobal;
+ prop.mElement = kAudioObjectPropertyElementMaster;
+
+ UInt32 size = 0;
+ AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, NULL, &size);
+ AudioDeviceID *audioDevices = (AudioDeviceID *)malloc(size);
+ AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &size, audioDevices);
+
+ UInt32 deviceCount = size / sizeof(AudioDeviceID);
+ for (UInt32 i = 0; i < deviceCount; i++) {
+ prop.mScope = kAudioDevicePropertyScopeOutput;
+ prop.mSelector = kAudioDevicePropertyStreamConfiguration;
+
+ AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size);
+ AudioBufferList *bufferList = (AudioBufferList *)malloc(size);
+ AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList);
+
+ UInt32 outputChannelCount = 0;
+ for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++)
+ outputChannelCount += bufferList->mBuffers[j].mNumberChannels;
+
+ free(bufferList);
+
+ if (outputChannelCount >= 1) {
+ CFStringRef cfname;
+
+ size = sizeof(CFStringRef);
+ prop.mSelector = kAudioObjectPropertyName;
+
+ AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, &cfname);
+
+ CFIndex length = CFStringGetLength(cfname);
+ CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
+ char *buffer = (char *)malloc(maxSize);
+ if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) {
+ // Append the ID to the name in case we have devices with duplicate name
+ list.push_back(String(buffer) + " (" + itos(audioDevices[i]) + ")");
+ }
+
+ free(buffer);
+ }
+ }
+
+ free(audioDevices);
+
+ return list;
+}
+
+String AudioDriverCoreAudio::get_device() {
+
+ return device_name;
+}
+
+void AudioDriverCoreAudio::set_device(String device) {
+
+ device_name = device;
+ if (!active) {
+ return;
+ }
+
+ AudioDeviceID deviceId;
+ bool found = false;
+ if (device_name != "Default") {
+ AudioObjectPropertyAddress prop;
+
+ prop.mSelector = kAudioHardwarePropertyDevices;
+ prop.mScope = kAudioObjectPropertyScopeGlobal;
+ prop.mElement = kAudioObjectPropertyElementMaster;
+
+ UInt32 size = 0;
+ AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, NULL, &size);
+ AudioDeviceID *audioDevices = (AudioDeviceID *)malloc(size);
+ AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &size, audioDevices);
+
+ UInt32 deviceCount = size / sizeof(AudioDeviceID);
+ for (UInt32 i = 0; i < deviceCount && !found; i++) {
+ prop.mScope = kAudioDevicePropertyScopeOutput;
+ prop.mSelector = kAudioDevicePropertyStreamConfiguration;
+
+ AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size);
+ AudioBufferList *bufferList = (AudioBufferList *)malloc(size);
+ AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList);
+
+ UInt32 outputChannelCount = 0;
+ for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++)
+ outputChannelCount += bufferList->mBuffers[j].mNumberChannels;
+
+ free(bufferList);
+
+ if (outputChannelCount >= 1) {
+ CFStringRef cfname;
+
+ size = sizeof(CFStringRef);
+ prop.mSelector = kAudioObjectPropertyName;
+
+ AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, &cfname);
+
+ CFIndex length = CFStringGetLength(cfname);
+ CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
+ char *buffer = (char *)malloc(maxSize);
+ if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) {
+ String name = String(buffer) + " (" + itos(audioDevices[i]) + ")";
+ if (name == device_name) {
+ deviceId = audioDevices[i];
+ found = true;
+ }
+ }
+
+ free(buffer);
+ }
+ }
+
+ free(audioDevices);
+ }
+
+ if (!found) {
+ UInt32 size = sizeof(AudioDeviceID);
+ AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+
+ OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, &size, &deviceId);
+ ERR_FAIL_COND(result != noErr);
+
+ found = true;
+ }
+
+ if (found) {
+ OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID));
+ ERR_FAIL_COND(result != noErr);
+ }
+}
+
+#endif
+
void AudioDriverCoreAudio::lock() {
if (mutex)
mutex->lock();
@@ -276,10 +395,15 @@ bool AudioDriverCoreAudio::try_lock() {
void AudioDriverCoreAudio::finish() {
OSStatus result;
- finishDevice();
+ finish_device();
#ifdef OSX_ENABLED
- result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &outputDeviceAddress, &outputDeviceAddressCB, this);
+ AudioObjectPropertyAddress prop;
+ prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
+ prop.mScope = kAudioObjectPropertyScopeGlobal;
+ prop.mElement = kAudioObjectPropertyElementMaster;
+
+ result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this);
if (result != noErr) {
ERR_PRINT("AudioObjectRemovePropertyListener failed");
}
@@ -309,6 +433,8 @@ AudioDriverCoreAudio::AudioDriverCoreAudio() {
buffer_frames = 0;
samples_in.clear();
+
+ device_name = "Default";
};
AudioDriverCoreAudio::~AudioDriverCoreAudio(){};
diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h
index 51256085d8..9891920263 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.h
+++ b/drivers/coreaudio/audio_driver_coreaudio.h
@@ -43,12 +43,12 @@
class AudioDriverCoreAudio : public AudioDriver {
AudioComponentInstance audio_unit;
-#ifdef OSX_ENABLED
- AudioObjectPropertyAddress outputDeviceAddress;
-#endif
+
bool active;
Mutex *mutex;
+ String device_name;
+
int mix_rate;
unsigned int channels;
unsigned int buffer_frames;
@@ -56,14 +56,20 @@ class AudioDriverCoreAudio : public AudioDriver {
Vector<int32_t> samples_in;
+#ifdef OSX_ENABLED
+ static OSStatus output_device_address_cb(AudioObjectID inObjectID,
+ UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses,
+ void *inClientData);
+#endif
+
static OSStatus output_callback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList *ioData);
- Error initDevice();
- Error finishDevice();
+ Error init_device();
+ Error finish_device();
public:
const char *get_name() const {
@@ -74,12 +80,16 @@ public:
virtual void start();
virtual int get_mix_rate() const;
virtual SpeakerMode get_speaker_mode() const;
+#ifdef OSX_ENABLED
+ virtual Array get_device_list();
+ virtual String get_device();
+ virtual void set_device(String device);
+#endif
virtual void lock();
virtual void unlock();
virtual void finish();
bool try_lock();
- Error reopen();
AudioDriverCoreAudio();
~AudioDriverCoreAudio();
diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp
index f41bfe838b..ad6c2f850a 100644
--- a/drivers/gles2/shader_compiler_gles2.cpp
+++ b/drivers/gles2/shader_compiler_gles2.cpp
@@ -872,9 +872,9 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() {
actions[VS::SHADER_PARTICLES].renames["EMISSION_TRANSFORM"] = "emission_transform";
actions[VS::SHADER_PARTICLES].renames["RANDOM_SEED"] = "random_seed";
- actions[VS::SHADER_SPATIAL].render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n";
- actions[VS::SHADER_SPATIAL].render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n";
- actions[VS::SHADER_SPATIAL].render_mode_defines["keep_data"] = "#define ENABLE_KEEP_DATA\n";
+ actions[VS::SHADER_PARTICLES].render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n";
+ actions[VS::SHADER_PARTICLES].render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n";
+ actions[VS::SHADER_PARTICLES].render_mode_defines["keep_data"] = "#define ENABLE_KEEP_DATA\n";
vertex_name = "vertex";
fragment_name = "fragment";
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 02d851288a..5b6b3d44f2 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -2085,9 +2085,9 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
case RasterizerStorageGLES3::Shader::Spatial::BLEND_MODE_MUL: {
glBlendEquation(GL_FUNC_ADD);
if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) {
- glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_DST_ALPHA, GL_ZERO);
} else {
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE);
}
} break;
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index a287dca1ed..f91ed35331 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "rasterizer_storage_gles3.h"
+#include "engine.h"
#include "project_settings.h"
#include "rasterizer_canvas_gles3.h"
#include "rasterizer_scene_gles3.h"
@@ -5855,6 +5856,8 @@ void RasterizerStorageGLES3::update_particles() {
shaders.particles.set_uniform(ParticlesShaderGLES3::EMITTING, particles->emitting);
shaders.particles.set_uniform(ParticlesShaderGLES3::RANDOMNESS, particles->randomness);
+ bool zero_time_scale = Engine::get_singleton()->get_time_scale() <= 0.0;
+
if (particles->clear && particles->pre_process_time > 0.0) {
float frame_time;
@@ -5872,7 +5875,15 @@ void RasterizerStorageGLES3::update_particles() {
}
if (particles->fixed_fps > 0) {
- float frame_time = 1.0 / particles->fixed_fps;
+ float frame_time;
+ float decr;
+ if (zero_time_scale) {
+ frame_time = 0.0;
+ decr = 1.0 / particles->fixed_fps;
+ } else {
+ frame_time = 1.0 / particles->fixed_fps;
+ decr = frame_time;
+ }
float delta = frame.delta;
if (delta > 0.1) { //avoid recursive stalls if fps goes below 10
delta = 0.1;
@@ -5883,13 +5894,16 @@ void RasterizerStorageGLES3::update_particles() {
while (todo >= frame_time) {
_particles_process(particles, frame_time);
- todo -= frame_time;
+ todo -= decr;
}
particles->frame_remainder = todo;
} else {
- _particles_process(particles, frame.delta);
+ if (zero_time_scale)
+ _particles_process(particles, 0.0);
+ else
+ _particles_process(particles, frame.delta);
}
particle_update_list.remove(particle_update_list.first());
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 9639f06345..0f91c94539 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -37,166 +37,144 @@
#include "os/os.h"
#include "project_settings.h"
-void pa_state_cb(pa_context *c, void *userdata) {
- pa_context_state_t state;
- int *pa_ready = (int *)userdata;
+void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) {
+ AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata;
- state = pa_context_get_state(c);
- switch (state) {
- case PA_CONTEXT_FAILED:
+ switch (pa_context_get_state(c)) {
case PA_CONTEXT_TERMINATED:
- *pa_ready = 2;
+ case PA_CONTEXT_FAILED:
+ ad->pa_ready = -1;
break;
case PA_CONTEXT_READY:
- *pa_ready = 1;
+ ad->pa_ready = 1;
break;
}
}
-void sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {
- unsigned int *channels = (unsigned int *)userdata;
+void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {
+ AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata;
// If eol is set to a positive number, you're at the end of the list
if (eol > 0) {
return;
}
- *channels = l->channel_map.channels;
+ ad->pa_map = l->channel_map;
+ ad->pa_status++;
}
-void server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) {
- char *default_output = (char *)userdata;
+void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) {
+ AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata;
- strncpy(default_output, i->default_sink_name, 1024);
+ ad->default_device = i->default_sink_name;
+ ad->pa_status++;
}
-static unsigned int detect_channels() {
-
- pa_mainloop *pa_ml;
- pa_mainloop_api *pa_mlapi;
- pa_operation *pa_op;
- pa_context *pa_ctx;
+void AudioDriverPulseAudio::detect_channels() {
- int state = 0;
- int pa_ready = 0;
-
- char default_output[1024];
- unsigned int channels = 2;
-
- pa_ml = pa_mainloop_new();
- pa_mlapi = pa_mainloop_get_api(pa_ml);
- pa_ctx = pa_context_new(pa_mlapi, "Godot");
-
- int ret = pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFLAGS, NULL);
- if (ret < 0) {
- pa_context_unref(pa_ctx);
- pa_mainloop_free(pa_ml);
+ pa_channel_map_init_stereo(&pa_map);
- return 2;
- }
-
- pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready);
+ if (device_name == "Default") {
+ // Get the default output device name
+ pa_status = 0;
+ pa_operation *pa_op = pa_context_get_server_info(pa_ctx, &AudioDriverPulseAudio::pa_server_info_cb, (void *)this);
+ if (pa_op) {
+ while (pa_status == 0) {
+ int ret = pa_mainloop_iterate(pa_ml, 1, NULL);
+ if (ret < 0) {
+ ERR_PRINT("pa_mainloop_iterate error");
+ }
+ }
- // Wait until the pa server is ready
- while (pa_ready == 0) {
- pa_mainloop_iterate(pa_ml, 1, NULL);
+ pa_operation_unref(pa_op);
+ } else {
+ ERR_PRINT("pa_context_get_server_info error");
+ }
}
- // Check if there was an error connecting to the pa server
- if (pa_ready == 2) {
- pa_context_disconnect(pa_ctx);
- pa_context_unref(pa_ctx);
- pa_mainloop_free(pa_ml);
-
- return 2;
+ char device[1024];
+ if (device_name == "Default") {
+ strcpy(device, default_device.utf8().get_data());
+ } else {
+ strcpy(device, device_name.utf8().get_data());
}
- // Get the default output device name
- pa_op = pa_context_get_server_info(pa_ctx, &server_info_cb, (void *)default_output);
+ // Now using the device name get the amount of channels
+ pa_status = 0;
+ pa_operation *pa_op = pa_context_get_sink_info_by_name(pa_ctx, device, &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this);
if (pa_op) {
- while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) {
- ret = pa_mainloop_iterate(pa_ml, 1, NULL);
+ while (pa_status == 0) {
+ int ret = pa_mainloop_iterate(pa_ml, 1, NULL);
if (ret < 0) {
ERR_PRINT("pa_mainloop_iterate error");
}
}
pa_operation_unref(pa_op);
-
- // Now using the device name get the amount of channels
- pa_op = pa_context_get_sink_info_by_name(pa_ctx, default_output, &sink_info_cb, (void *)&channels);
- if (pa_op) {
- while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) {
- ret = pa_mainloop_iterate(pa_ml, 1, NULL);
- if (ret < 0) {
- ERR_PRINT("pa_mainloop_iterate error");
- }
- }
-
- pa_operation_unref(pa_op);
- } else {
- ERR_PRINT("pa_context_get_sink_info_by_name error");
- }
} else {
- ERR_PRINT("pa_context_get_server_info error");
+ ERR_PRINT("pa_context_get_sink_info_by_name error");
}
-
- pa_context_disconnect(pa_ctx);
- pa_context_unref(pa_ctx);
- pa_mainloop_free(pa_ml);
-
- return channels;
}
-Error AudioDriverPulseAudio::init() {
-
- active = false;
- thread_exited = false;
- exit_thread = false;
+Error AudioDriverPulseAudio::init_device() {
- mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE);
+ // If there is a specified device check that it is really present
+ if (device_name != "Default") {
+ Array list = get_device_list();
+ if (list.find(device_name) == -1) {
+ device_name = "Default";
+ new_device = "Default";
+ }
+ }
// Detect the amount of channels PulseAudio is using
- // Note: If using an even amount of channels (2, 4, etc) channels and pa_channels will be equal,
- // if not then pa_channels will have the real amount of channels PulseAudio is using and channels
- // will have the amount of channels Godot is using (in this case it's pa_channels + 1)
- pa_channels = detect_channels();
- switch (pa_channels) {
+ // Note: If using an even amount of channels (2, 4, etc) channels and pa_map.channels will be equal,
+ // if not then pa_map.channels will have the real amount of channels PulseAudio is using and channels
+ // will have the amount of channels Godot is using (in this case it's pa_map.channels + 1)
+ detect_channels();
+ switch (pa_map.channels) {
case 1: // Mono
case 3: // Surround 2.1
case 5: // Surround 5.0
case 7: // Surround 7.0
- channels = pa_channels + 1;
+ channels = pa_map.channels + 1;
break;
case 2: // Stereo
case 4: // Surround 4.0
case 6: // Surround 5.1
case 8: // Surround 7.1
- channels = pa_channels;
+ channels = pa_map.channels;
break;
default:
- ERR_PRINTS("PulseAudio: Unsupported number of channels: " + itos(pa_channels));
- ERR_FAIL_V(ERR_CANT_OPEN);
+ WARN_PRINTS("PulseAudio: Unsupported number of channels: " + itos(pa_map.channels));
+ pa_channel_map_init_stereo(&pa_map);
+ channels = 2;
break;
}
- pa_sample_spec spec;
- spec.format = PA_SAMPLE_S16LE;
- spec.channels = pa_channels;
- spec.rate = mix_rate;
-
int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
- pa_buffer_size = buffer_frames * pa_channels;
+ pa_buffer_size = buffer_frames * pa_map.channels;
if (OS::get_singleton()->is_stdout_verbose()) {
- print_line("PulseAudio: detected " + itos(pa_channels) + " channels");
+ print_line("PulseAudio: detected " + itos(pa_map.channels) + " channels");
print_line("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
}
+ pa_sample_spec spec;
+ spec.format = PA_SAMPLE_S16LE;
+ spec.channels = pa_map.channels;
+ spec.rate = mix_rate;
+
+ pa_str = pa_stream_new(pa_ctx, "Sound", &spec, &pa_map);
+ if (pa_str == NULL) {
+ ERR_PRINTS("PulseAudio: pa_stream_new error: " + String(pa_strerror(pa_context_errno(pa_ctx))));
+ ERR_FAIL_V(ERR_CANT_OPEN);
+ }
+
pa_buffer_attr attr;
// set to appropriate buffer length (in bytes) from global settings
attr.tlength = pa_buffer_size * sizeof(int16_t);
@@ -205,27 +183,73 @@ Error AudioDriverPulseAudio::init() {
attr.maxlength = (uint32_t)-1;
attr.minreq = (uint32_t)-1;
- int error_code;
- pulse = pa_simple_new(NULL, // default server
- "Godot", // application name
- PA_STREAM_PLAYBACK,
- NULL, // default device
- "Sound", // stream description
- &spec,
- NULL, // use default channel map
- &attr, // use buffering attributes from above
- &error_code);
-
- if (pulse == NULL) {
- fprintf(stderr, "PulseAudio ERR: %s\n", pa_strerror(error_code));
- ERR_FAIL_COND_V(pulse == NULL, ERR_CANT_OPEN);
- }
+ const char *dev = device_name == "Default" ? NULL : device_name.utf8().get_data();
+ pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);
+ int error_code = pa_stream_connect_playback(pa_str, dev, &attr, flags, NULL, NULL);
+ ERR_FAIL_COND_V(error_code < 0, ERR_CANT_OPEN);
samples_in.resize(buffer_frames * channels);
samples_out.resize(pa_buffer_size);
- mutex = Mutex::create();
- thread = Thread::create(AudioDriverPulseAudio::thread_func, this);
+ return OK;
+}
+
+Error AudioDriverPulseAudio::init() {
+
+ active = false;
+ thread_exited = false;
+ exit_thread = false;
+
+ mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE);
+
+ pa_ml = pa_mainloop_new();
+ ERR_FAIL_COND_V(pa_ml == NULL, ERR_CANT_OPEN);
+
+ pa_ctx = pa_context_new(pa_mainloop_get_api(pa_ml), "Godot");
+ ERR_FAIL_COND_V(pa_ctx == NULL, ERR_CANT_OPEN);
+
+ pa_ready = 0;
+ pa_context_set_state_callback(pa_ctx, pa_state_cb, (void *)this);
+
+ int ret = pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFLAGS, NULL);
+ if (ret < 0) {
+ if (pa_ctx) {
+ pa_context_unref(pa_ctx);
+ pa_ctx = NULL;
+ }
+
+ if (pa_ml) {
+ pa_mainloop_free(pa_ml);
+ pa_ml = NULL;
+ }
+
+ return ERR_CANT_OPEN;
+ }
+
+ while (pa_ready == 0) {
+ pa_mainloop_iterate(pa_ml, 1, NULL);
+ }
+
+ if (pa_ready < 0) {
+ if (pa_ctx) {
+ pa_context_disconnect(pa_ctx);
+ pa_context_unref(pa_ctx);
+ pa_ctx = NULL;
+ }
+
+ if (pa_ml) {
+ pa_mainloop_free(pa_ml);
+ pa_ml = NULL;
+ }
+
+ return ERR_CANT_OPEN;
+ }
+
+ Error err = init_device();
+ if (err == OK) {
+ mutex = Mutex::create();
+ thread = Thread::create(AudioDriverPulseAudio::thread_func, this);
+ }
return OK;
}
@@ -233,9 +257,24 @@ Error AudioDriverPulseAudio::init() {
float AudioDriverPulseAudio::get_latency() {
if (latency == 0) { //only do this once since it's approximate anyway
- int error_code;
- pa_usec_t palat = pa_simple_get_latency(pulse, &error_code);
- latency = double(palat) / 1000000.0;
+ lock();
+
+ pa_usec_t palat = 0;
+ if (pa_stream_get_state(pa_str) == PA_STREAM_READY) {
+ int negative = 0;
+
+ if (pa_stream_get_latency(pa_str, &palat, &negative) >= 0) {
+ if (negative) {
+ palat = 0;
+ }
+ }
+ }
+
+ if (palat > 0) {
+ latency = double(palat) / 1000000.0;
+ }
+
+ unlock();
}
return latency;
@@ -258,7 +297,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
ad->unlock();
- if (ad->channels == ad->pa_channels) {
+ if (ad->channels == ad->pa_map.channels) {
for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {
ad->samples_out[i] = ad->samples_in[i] >> 16;
}
@@ -268,7 +307,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
unsigned int out_idx = 0;
for (unsigned int i = 0; i < ad->buffer_frames; i++) {
- for (unsigned int j = 0; j < ad->pa_channels - 1; j++) {
+ for (unsigned int j = 0; j < ad->pa_map.channels - 1; j++) {
ad->samples_out[out_idx++] = ad->samples_in[in_idx++] >> 16;
}
uint32_t l = ad->samples_in[in_idx++];
@@ -278,17 +317,57 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
}
}
- // pa_simple_write always consumes the entire buffer
-
int error_code;
int byte_size = ad->pa_buffer_size * sizeof(int16_t);
- if (pa_simple_write(ad->pulse, ad->samples_out.ptr(), byte_size, &error_code) < 0) {
- // can't recover here
- fprintf(stderr, "PulseAudio failed and can't recover: %s\n", pa_strerror(error_code));
- ad->active = false;
- ad->exit_thread = true;
- break;
+
+ ad->lock();
+
+ int ret;
+ do {
+ ret = pa_mainloop_iterate(ad->pa_ml, 0, NULL);
+ } while (ret > 0);
+
+ if (pa_stream_get_state(ad->pa_str) == PA_STREAM_READY) {
+ const void *ptr = ad->samples_out.ptr();
+ while (byte_size > 0) {
+ size_t bytes = pa_stream_writable_size(ad->pa_str);
+ if (bytes > 0) {
+ if (bytes > byte_size) {
+ bytes = byte_size;
+ }
+
+ int ret = pa_stream_write(ad->pa_str, ptr, bytes, NULL, 0LL, PA_SEEK_RELATIVE);
+ if (ret >= 0) {
+ byte_size -= bytes;
+ ptr = (const char *)ptr + bytes;
+ }
+ } else {
+ pa_mainloop_iterate(ad->pa_ml, 1, NULL);
+ }
+ }
}
+
+ // User selected a new device, finish the current one so we'll init the new device
+ if (ad->device_name != ad->new_device) {
+ ad->device_name = ad->new_device;
+ ad->finish_device();
+
+ Error err = ad->init_device();
+ if (err != OK) {
+ ERR_PRINT("PulseAudio: init_device error");
+ ad->device_name = "Default";
+ ad->new_device = "Default";
+
+ err = ad->init_device();
+ if (err != OK) {
+ ad->active = false;
+ ad->exit_thread = true;
+ break;
+ }
+ }
+ }
+
+ ad->unlock();
}
ad->thread_exited = true;
@@ -309,6 +388,61 @@ AudioDriver::SpeakerMode AudioDriverPulseAudio::get_speaker_mode() const {
return get_speaker_mode_by_total_channels(channels);
}
+void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {
+ AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata;
+ int ctr = 0;
+
+ // If eol is set to a positive number, you're at the end of the list
+ if (eol > 0) {
+ return;
+ }
+
+ ad->pa_devices.push_back(l->name);
+ ad->pa_status++;
+}
+
+Array AudioDriverPulseAudio::get_device_list() {
+
+ pa_devices.clear();
+ pa_devices.push_back("Default");
+
+ if (pa_ctx == NULL) {
+ return pa_devices;
+ }
+
+ lock();
+
+ // Get the device list
+ pa_status = 0;
+ pa_operation *pa_op = pa_context_get_sink_info_list(pa_ctx, pa_sinklist_cb, (void *)this);
+ if (pa_op) {
+ while (pa_status == 0) {
+ int ret = pa_mainloop_iterate(pa_ml, 1, NULL);
+ if (ret < 0) {
+ ERR_PRINT("pa_mainloop_iterate error");
+ }
+ }
+
+ pa_operation_unref(pa_op);
+ } else {
+ ERR_PRINT("pa_context_get_server_info error");
+ }
+
+ unlock();
+
+ return pa_devices;
+}
+
+String AudioDriverPulseAudio::get_device() {
+
+ return device_name;
+}
+
+void AudioDriverPulseAudio::set_device(String device) {
+
+ new_device = device;
+}
+
void AudioDriverPulseAudio::lock() {
if (!thread || !mutex)
@@ -323,6 +457,15 @@ void AudioDriverPulseAudio::unlock() {
mutex->unlock();
}
+void AudioDriverPulseAudio::finish_device() {
+
+ if (pa_str) {
+ pa_stream_disconnect(pa_str);
+ pa_stream_unref(pa_str);
+ pa_str = NULL;
+ }
+}
+
void AudioDriverPulseAudio::finish() {
if (!thread)
@@ -331,9 +474,17 @@ void AudioDriverPulseAudio::finish() {
exit_thread = true;
Thread::wait_to_finish(thread);
- if (pulse) {
- pa_simple_free(pulse);
- pulse = NULL;
+ finish_device();
+
+ if (pa_ctx) {
+ pa_context_disconnect(pa_ctx);
+ pa_context_unref(pa_ctx);
+ pa_ctx = NULL;
+ }
+
+ if (pa_ml) {
+ pa_mainloop_free(pa_ml);
+ pa_ml = NULL;
}
memdelete(thread);
@@ -347,9 +498,16 @@ void AudioDriverPulseAudio::finish() {
AudioDriverPulseAudio::AudioDriverPulseAudio() {
+ pa_ml = NULL;
+ pa_ctx = NULL;
+ pa_str = NULL;
+
mutex = NULL;
thread = NULL;
- pulse = NULL;
+
+ device_name = "Default";
+ new_device = "Default";
+ default_device = "";
samples_in.clear();
samples_out.clear();
@@ -358,7 +516,8 @@ AudioDriverPulseAudio::AudioDriverPulseAudio() {
buffer_frames = 0;
pa_buffer_size = 0;
channels = 0;
- pa_channels = 0;
+ pa_ready = 0;
+ pa_status = 0;
active = false;
thread_exited = false;
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h
index 5737f24314..b471f5f9d5 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.h
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.h
@@ -37,14 +37,21 @@
#include "core/os/thread.h"
#include "servers/audio_server.h"
-#include <pulse/simple.h>
+#include <pulse/pulseaudio.h>
class AudioDriverPulseAudio : public AudioDriver {
Thread *thread;
Mutex *mutex;
- pa_simple *pulse;
+ pa_mainloop *pa_ml;
+ pa_context *pa_ctx;
+ pa_stream *pa_str;
+ pa_channel_map pa_map;
+
+ String device_name;
+ String new_device;
+ String default_device;
Vector<int32_t> samples_in;
Vector<int16_t> samples_out;
@@ -53,7 +60,9 @@ class AudioDriverPulseAudio : public AudioDriver {
unsigned int buffer_frames;
unsigned int pa_buffer_size;
int channels;
- int pa_channels;
+ int pa_ready;
+ int pa_status;
+ Array pa_devices;
bool active;
bool thread_exited;
@@ -61,6 +70,16 @@ class AudioDriverPulseAudio : public AudioDriver {
float latency;
+ static void pa_state_cb(pa_context *c, void *userdata);
+ static void pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata);
+ static void pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata);
+ static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata);
+
+ Error init_device();
+ void finish_device();
+
+ void detect_channels();
+
static void thread_func(void *p_udata);
public:
@@ -72,6 +91,9 @@ public:
virtual void start();
virtual int get_mix_rate() const;
virtual SpeakerMode get_speaker_mode() const;
+ virtual Array get_device_list();
+ virtual String get_device();
+ virtual void set_device(String device);
virtual void lock();
virtual void unlock();
virtual void finish();
diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp
index aae6c0d308..e1680601ad 100644
--- a/drivers/wasapi/audio_driver_wasapi.cpp
+++ b/drivers/wasapi/audio_driver_wasapi.cpp
@@ -35,6 +35,19 @@
#include "os/os.h"
#include "project_settings.h"
+#include <functiondiscoverykeys.h>
+
+#ifndef PKEY_Device_FriendlyName
+
+#undef DEFINE_PROPERTYKEY
+/* clang-format off */
+#define DEFINE_PROPERTYKEY(id, a, b, c, d, e, f, g, h, i, j, k, l) \
+ const PROPERTYKEY id = { { a, b, c, { d, e, f, g, h, i, j, k, } }, l };
+/* clang-format on */
+
+DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);
+#endif
+
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
@@ -121,7 +134,61 @@ Error AudioDriverWASAPI::init_device(bool reinit) {
HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator);
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
- hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device);
+ if (device_name == "Default") {
+ hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device);
+ } else {
+ IMMDeviceCollection *devices = NULL;
+
+ hr = enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices);
+ ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
+
+ LPWSTR strId = NULL;
+ bool found = false;
+
+ UINT count = 0;
+ hr = devices->GetCount(&count);
+ ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
+
+ for (ULONG i = 0; i < count && !found; i++) {
+ IMMDevice *device = NULL;
+
+ hr = devices->Item(i, &device);
+ ERR_BREAK(hr != S_OK);
+
+ IPropertyStore *props = NULL;
+ hr = device->OpenPropertyStore(STGM_READ, &props);
+ ERR_BREAK(hr != S_OK);
+
+ PROPVARIANT propvar;
+ PropVariantInit(&propvar);
+
+ hr = props->GetValue(PKEY_Device_FriendlyName, &propvar);
+ ERR_BREAK(hr != S_OK);
+
+ if (device_name == String(propvar.pwszVal)) {
+ hr = device->GetId(&strId);
+ ERR_BREAK(hr != S_OK);
+
+ found = true;
+ }
+
+ PropVariantClear(&propvar);
+ props->Release();
+ device->Release();
+ }
+
+ if (found) {
+ hr = enumerator->GetDevice(strId, &device);
+ }
+
+ if (strId) {
+ CoTaskMemFree(strId);
+ }
+
+ if (device == NULL) {
+ hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device);
+ }
+ }
if (reinit) {
// In case we're trying to re-initialize the device prevent throwing this error on the console,
// otherwise if there is currently no device available this will spam the console.
@@ -294,6 +361,64 @@ AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const {
return get_speaker_mode_by_total_channels(channels);
}
+Array AudioDriverWASAPI::get_device_list() {
+
+ Array list;
+ IMMDeviceCollection *devices = NULL;
+ IMMDeviceEnumerator *enumerator = NULL;
+
+ list.push_back(String("Default"));
+
+ CoInitialize(NULL);
+
+ HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator);
+ ERR_FAIL_COND_V(hr != S_OK, Array());
+
+ hr = enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices);
+ ERR_FAIL_COND_V(hr != S_OK, Array());
+
+ UINT count = 0;
+ hr = devices->GetCount(&count);
+ ERR_FAIL_COND_V(hr != S_OK, Array());
+
+ for (ULONG i = 0; i < count; i++) {
+ IMMDevice *device = NULL;
+
+ hr = devices->Item(i, &device);
+ ERR_BREAK(hr != S_OK);
+
+ IPropertyStore *props = NULL;
+ hr = device->OpenPropertyStore(STGM_READ, &props);
+ ERR_BREAK(hr != S_OK);
+
+ PROPVARIANT propvar;
+ PropVariantInit(&propvar);
+
+ hr = props->GetValue(PKEY_Device_FriendlyName, &propvar);
+ ERR_BREAK(hr != S_OK);
+
+ list.push_back(String(propvar.pwszVal));
+
+ PropVariantClear(&propvar);
+ props->Release();
+ device->Release();
+ }
+
+ devices->Release();
+ enumerator->Release();
+ return list;
+}
+
+String AudioDriverWASAPI::get_device() {
+
+ return device_name;
+}
+
+void AudioDriverWASAPI::set_device(String device) {
+
+ new_device = device;
+}
+
void AudioDriverWASAPI::write_sample(AudioDriverWASAPI *ad, BYTE *buffer, int i, int32_t sample) {
if (ad->format_tag == WAVE_FORMAT_PCM) {
switch (ad->bits_per_sample) {
@@ -409,7 +534,8 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
}
}
- if (default_device_changed) {
+ // If we're using the Default device and it changed finish it so we'll re-init the device
+ if (ad->device_name == "Default" && default_device_changed) {
Error err = ad->finish_device();
if (err != OK) {
ERR_PRINT("WASAPI: finish_device error");
@@ -418,6 +544,15 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
default_device_changed = false;
}
+ // User selected a new device, finish the current one so we'll init the new device
+ if (ad->device_name != ad->new_device) {
+ ad->device_name = ad->new_device;
+ Error err = ad->finish_device();
+ if (err != OK) {
+ ERR_PRINT("WASAPI: finish_device error");
+ }
+ }
+
if (!ad->audio_client) {
Error err = ad->init_device(true);
if (err == OK) {
@@ -492,6 +627,9 @@ AudioDriverWASAPI::AudioDriverWASAPI() {
thread_exited = false;
exit_thread = false;
active = false;
+
+ device_name = "Default";
+ new_device = "Default";
}
#endif
diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h
index 2b19f0cca1..c97f4c288c 100644
--- a/drivers/wasapi/audio_driver_wasapi.h
+++ b/drivers/wasapi/audio_driver_wasapi.h
@@ -49,6 +49,9 @@ class AudioDriverWASAPI : public AudioDriver {
Mutex *mutex;
Thread *thread;
+ String device_name;
+ String new_device;
+
WORD format_tag;
WORD bits_per_sample;
@@ -80,6 +83,9 @@ public:
virtual void start();
virtual int get_mix_rate() const;
virtual SpeakerMode get_speaker_mode() const;
+ virtual Array get_device_list();
+ virtual String get_device();
+ virtual void set_device(String device);
virtual void lock();
virtual void unlock();
virtual void finish();
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 9ae9ab1501..4c7f2f53cc 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -212,35 +212,39 @@ void FindReplaceBar::_replace_all() {
text_edit->begin_complex_operation();
- while (search_next()) {
-
- // replace area
- Point2i match_from(result_line, result_col);
- Point2i match_to(result_line, result_col + search_text_len);
-
- if (match_from < prev_match)
- break; // done
+ if (search_current()) {
+ do {
+ // replace area
+ Point2i match_from(result_line, result_col);
+ Point2i match_to(result_line, result_col + search_text_len);
+
+ if (match_from < prev_match) {
+ break; // done
+ }
- prev_match = Point2i(result_line, result_col + replace_text.length());
+ prev_match = Point2i(result_line, result_col + replace_text.length());
- text_edit->unfold_line(result_line);
- text_edit->select(result_line, result_col, result_line, match_to.y);
+ text_edit->unfold_line(result_line);
+ text_edit->select(result_line, result_col, result_line, match_to.y);
- if (selection_enabled && is_selection_only()) {
+ if (selection_enabled && is_selection_only()) {
+ if (match_from < selection_begin || match_to > selection_end) {
+ continue;
+ }
- if (match_from < selection_begin || match_to > selection_end)
- continue;
+ // replace but adjust selection bounds
+ text_edit->insert_text_at_cursor(replace_text);
+ if (match_to.x == selection_end.x) {
+ selection_end.y += replace_text.length() - search_text_len;
+ }
- // replace but adjust selection bounds
- text_edit->insert_text_at_cursor(replace_text);
- if (match_to.x == selection_end.x)
- selection_end.y += replace_text.length() - search_text_len;
- } else {
- // just replace
- text_edit->insert_text_at_cursor(replace_text);
- }
+ } else {
+ // just replace
+ text_edit->insert_text_at_cursor(replace_text);
+ }
- rc++;
+ rc++;
+ } while (search_next());
}
text_edit->end_complex_operation();
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 13ef6e9e0e..78fb35e354 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -433,26 +433,7 @@ Object *CreateDialog::instance_selected() {
custom = md;
if (custom != String()) {
- if (EditorNode::get_editor_data().get_custom_types().has(custom)) {
-
- for (int i = 0; i < EditorNode::get_editor_data().get_custom_types()[custom].size(); i++) {
- if (EditorNode::get_editor_data().get_custom_types()[custom][i].name == selected->get_text(0)) {
- Ref<Texture> icon = EditorNode::get_editor_data().get_custom_types()[custom][i].icon;
- Ref<Script> script = EditorNode::get_editor_data().get_custom_types()[custom][i].script;
- String name = selected->get_text(0);
-
- Object *ob = ClassDB::instance(custom);
- ERR_FAIL_COND_V(!ob, NULL);
- if (ob->is_class("Node")) {
- ob->call("set_name", name);
- }
- ob->set_script(script.get_ref_ptr());
- if (icon.is_valid())
- ob->set_meta("_editor_icon", icon);
- return ob;
- }
- }
- }
+ return EditorNode::get_editor_data().instance_custom_type(selected->get_text(0), custom);
} else {
return ClassDB::instance(selected->get_text(0));
}
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index 95ed40d889..ef9265ecd2 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -452,6 +452,31 @@ void EditorData::add_custom_type(const String &p_type, const String &p_inherits,
custom_types[p_inherits].push_back(ct);
}
+Object *EditorData::instance_custom_type(const String &p_type, const String &p_inherits) {
+
+ if (get_custom_types().has(p_inherits)) {
+
+ for (int i = 0; i < get_custom_types()[p_inherits].size(); i++) {
+ if (get_custom_types()[p_inherits][i].name == p_type) {
+ Ref<Texture> icon = get_custom_types()[p_inherits][i].icon;
+ Ref<Script> script = get_custom_types()[p_inherits][i].script;
+
+ Object *ob = ClassDB::instance(p_inherits);
+ ERR_FAIL_COND_V(!ob, NULL);
+ if (ob->is_class("Node")) {
+ ob->call("set_name", p_type);
+ }
+ ob->set_script(script.get_ref_ptr());
+ if (icon.is_valid())
+ ob->set_meta("_editor_icon", icon);
+ return ob;
+ }
+ }
+ }
+
+ return NULL;
+}
+
void EditorData::remove_custom_type(const String &p_type) {
for (Map<String, Vector<CustomType> >::Element *E = custom_types.front(); E; E = E->next()) {
diff --git a/editor/editor_data.h b/editor/editor_data.h
index 844145853d..1a498a6150 100644
--- a/editor/editor_data.h
+++ b/editor/editor_data.h
@@ -171,6 +171,7 @@ public:
void restore_editor_global_states();
void add_custom_type(const String &p_type, const String &p_inherits, const Ref<Script> &p_script, const Ref<Texture> &p_icon);
+ Object *instance_custom_type(const String &p_type, const String &p_inherits);
void remove_custom_type(const String &p_type);
const Map<String, Vector<CustomType> > &get_custom_types() const { return custom_types; }
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 7f76cf1af2..f3be02a8c7 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -1172,7 +1172,12 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) {
class_desc->push_indent(1);
Vector<DocData::ConstantDoc> enum_list = E->get();
+ Map<String, int> enumValuesContainer;
+ int enumStartingLine = enum_line[E->key()];
+
for (int i = 0; i < enum_list.size(); i++) {
+ if (cd.name == "@GlobalScope")
+ enumValuesContainer[enum_list[i].name] = enumStartingLine;
class_desc->push_font(doc_code_font);
class_desc->push_color(headline_color);
@@ -1200,6 +1205,9 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) {
class_desc->add_newline();
}
+ if (cd.name == "@GlobalScope")
+ enum_values_line[E->key()] = enumValuesContainer;
+
class_desc->pop();
class_desc->add_newline();
@@ -1485,21 +1493,32 @@ void EditorHelp::_help_callback(const String &p_topic) {
if (method_line.has(name))
line = method_line[name];
} else if (what == "class_property") {
-
if (property_line.has(name))
line = property_line[name];
} else if (what == "class_enum") {
-
if (enum_line.has(name))
line = enum_line[name];
} else if (what == "class_theme_item") {
-
if (theme_property_line.has(name))
line = theme_property_line[name];
} else if (what == "class_constant") {
-
if (constant_line.has(name))
line = constant_line[name];
+ } else if (what == "class_global") {
+ if (constant_line.has(name))
+ line = constant_line[name];
+ else {
+ Map<String, Map<String, int> >::Element *iter = enum_values_line.front();
+ while (true) {
+ if (iter->value().has(name)) {
+ line = iter->value()[name];
+ break;
+ } else if (iter == enum_values_line.back())
+ break;
+ else
+ iter = iter->next();
+ }
+ }
}
class_desc->call_deferred("scroll_to_line", line);
diff --git a/editor/editor_help.h b/editor/editor_help.h
index aa84aa611f..0f93e1b55b 100644
--- a/editor/editor_help.h
+++ b/editor/editor_help.h
@@ -152,6 +152,7 @@ class EditorHelp : public VBoxContainer {
Map<String, int> theme_property_line;
Map<String, int> constant_line;
Map<String, int> enum_line;
+ Map<String, Map<String, int> > enum_values_line;
int description_line;
RichTextLabel *class_desc;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 7936cf446f..c31bec9a1b 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -470,24 +470,28 @@ void EditorNode::_fs_changed() {
}
if (preset.is_null()) {
String err = "Unknown export preset: " + export_defer.preset;
- ERR_PRINT(err.utf8().get_data());
+ ERR_PRINTS(err);
} else {
Ref<EditorExportPlatform> platform = preset->get_platform();
if (platform.is_null()) {
String err = "Preset \"" + export_defer.preset + "\" doesn't have a platform.";
- ERR_PRINT(err.utf8().get_data());
+ ERR_PRINTS(err);
} else {
// ensures export_project does not loop infinitely, because notifications may
// come during the export
export_defer.preset = "";
+ Error err;
if (!preset->is_runnable() && (export_defer.path.ends_with(".pck") || export_defer.path.ends_with(".zip"))) {
if (export_defer.path.ends_with(".zip")) {
- platform->save_zip(preset, export_defer.path);
+ err = platform->save_zip(preset, export_defer.path);
} else if (export_defer.path.ends_with(".pck")) {
- platform->save_pack(preset, export_defer.path);
+ err = platform->save_pack(preset, export_defer.path);
}
} else {
- platform->export_project(preset, export_defer.debug, export_defer.path, /*p_flags*/ 0);
+ err = platform->export_project(preset, export_defer.debug, export_defer.path, /*p_flags*/ 0);
+ }
+ if (err != OK) {
+ ERR_PRINTS(vformat(TTR("Project export failed with error code %d."), (int)err));
}
}
}
@@ -1230,29 +1234,25 @@ void EditorNode::_dialog_action(String p_file) {
} break;
case FILE_EXPORT_TILESET: {
- Ref<TileSet> ml;
- if (FileAccess::exists(p_file)) {
- ml = ResourceLoader::load(p_file, "TileSet");
+ Ref<TileSet> tileset;
+ if (FileAccess::exists(p_file) && file_export_lib_merge->is_pressed()) {
+ tileset = ResourceLoader::load(p_file, "TileSet");
- if (ml.is_null()) {
- if (file_export_lib_merge->is_pressed()) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
- accept->set_text(TTR("Can't load TileSet for merging!"));
- accept->popup_centered_minsize();
- return;
- }
- } else if (!file_export_lib_merge->is_pressed()) {
- ml->clear();
+ if (tileset.is_null()) {
+ current_option = -1;
+ accept->get_ok()->set_text(TTR("I see.."));
+ accept->set_text(TTR("Can't load TileSet for merging!"));
+ accept->popup_centered_minsize();
+ return;
}
} else {
- ml = Ref<TileSet>(memnew(TileSet));
+ tileset = Ref<TileSet>(memnew(TileSet));
}
- TileSetEditor::update_library_file(editor_data.get_edited_scene_root(), ml, true);
+ TileSetEditor::update_library_file(editor_data.get_edited_scene_root(), tileset, true);
- Error err = ResourceSaver::save(p_file, ml);
+ Error err = ResourceSaver::save(p_file, tileset);
if (err) {
current_option = -1;
@@ -3892,6 +3892,53 @@ void EditorNode::_update_dock_slots_visibility() {
}
}
+void EditorNode::_dock_tab_changed(int p_tab) {
+
+ // update visibility but dont set current tab
+ VSplitContainer *splits[DOCK_SLOT_MAX / 2] = {
+ left_l_vsplit,
+ left_r_vsplit,
+ right_l_vsplit,
+ right_r_vsplit,
+ };
+
+ if (!docks_visible) {
+
+ for (int i = 0; i < DOCK_SLOT_MAX; i++) {
+ dock_slot[i]->hide();
+ }
+
+ for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) {
+ splits[i]->hide();
+ }
+
+ right_hsplit->hide();
+ bottom_panel->hide();
+ } else {
+ for (int i = 0; i < DOCK_SLOT_MAX; i++) {
+
+ if (dock_slot[i]->get_tab_count())
+ dock_slot[i]->show();
+ else
+ dock_slot[i]->hide();
+ }
+
+ for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) {
+ bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count();
+ if (in_use)
+ splits[i]->show();
+ else
+ splits[i]->hide();
+ }
+ bottom_panel->show();
+
+ if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible())
+ right_hsplit->show();
+ else
+ right_hsplit->hide();
+ }
+}
+
void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section) {
for (int i = 0; i < DOCK_SLOT_MAX; i++) {
@@ -4762,6 +4809,7 @@ void EditorNode::_bind_methods() {
ClassDB::bind_method("_dock_popup_exit", &EditorNode::_dock_popup_exit);
ClassDB::bind_method("_dock_move_left", &EditorNode::_dock_move_left);
ClassDB::bind_method("_dock_move_right", &EditorNode::_dock_move_right);
+ ClassDB::bind_method("_dock_tab_changed", &EditorNode::_dock_tab_changed);
ClassDB::bind_method("_layout_menu_option", &EditorNode::_layout_menu_option);
@@ -5121,6 +5169,9 @@ EditorNode::EditorNode() {
dock_slot[i]->set_popup(dock_select_popup);
dock_slot[i]->connect("pre_popup_pressed", this, "_dock_pre_popup", varray(i));
dock_slot[i]->set_tab_align(TabContainer::ALIGN_LEFT);
+ dock_slot[i]->set_drag_to_rearrange_enabled(true);
+ dock_slot[i]->set_tabs_rearrange_group(1);
+ dock_slot[i]->connect("tab_changed", this, "_dock_tab_changed");
}
dock_drag_timer = memnew(Timer);
@@ -5158,6 +5209,7 @@ EditorNode::EditorNode() {
scene_tabs->set_tab_align(Tabs::ALIGN_LEFT);
scene_tabs->set_tab_close_display_policy((bool(EDITOR_DEF("interface/scene_tabs/always_show_close_button", false)) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY));
scene_tabs->set_min_width(int(EDITOR_DEF("interface/scene_tabs/minimum_width", 50)) * EDSCALE);
+ scene_tabs->set_drag_to_rearrange_enabled(true);
scene_tabs->connect("tab_changed", this, "_scene_tab_changed");
scene_tabs->connect("right_button_pressed", this, "_scene_tab_script_edited");
scene_tabs->connect("tab_close", this, "_scene_tab_closed");
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 90bebffca6..f774fa0a2e 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -569,6 +569,7 @@ private:
void _save_docks_to_config(Ref<ConfigFile> p_layout, const String &p_section);
void _load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section);
void _update_dock_slots_visibility();
+ void _dock_tab_changed(int p_tab);
bool restoring_scenes;
void _save_open_scenes_to_config(Ref<ConfigFile> p_layout, const String &p_section);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index b10f785a28..3582379e34 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -626,8 +626,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_color_disabled", "PopupMenu", font_color_disabled);
theme->set_icon("checked", "PopupMenu", theme->get_icon("GuiChecked", "EditorIcons"));
theme->set_icon("unchecked", "PopupMenu", theme->get_icon("GuiUnchecked", "EditorIcons"));
- theme->set_icon("radio_checked", "PopupMenu", theme->get_icon("GuiChecked", "EditorIcons"));
- theme->set_icon("radio_unchecked", "PopupMenu", theme->get_icon("GuiUnchecked", "EditorIcons"));
+ theme->set_icon("radio_checked", "PopupMenu", theme->get_icon("GuiRadioChecked", "EditorIcons"));
+ theme->set_icon("radio_unchecked", "PopupMenu", theme->get_icon("GuiRadioUnchecked", "EditorIcons"));
theme->set_icon("submenu", "PopupMenu", theme->get_icon("ArrowRight", "EditorIcons"));
theme->set_icon("visibility_hidden", "PopupMenu", theme->get_icon("GuiVisibilityHidden", "EditorIcons"));
theme->set_icon("visibility_visible", "PopupMenu", theme->get_icon("GuiVisibilityVisible", "EditorIcons"));
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 9bfa50148f..d4c7d7483e 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -533,6 +533,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) {
filelist.push_back(fi);
}
+ filelist.sort();
}
String oi = "Object";
@@ -719,12 +720,13 @@ void FileSystemDock::_push_to_history() {
button_hist_next->set_disabled(history_pos == history.size() - 1);
}
-void FileSystemDock::_get_all_files_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files) const {
+void FileSystemDock::_get_all_items_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files, Vector<String> &folders) const {
if (efsd == NULL)
return;
for (int i = 0; i < efsd->get_subdir_count(); i++) {
- _get_all_files_in_dir(efsd->get_subdir(i), files);
+ folders.push_back(efsd->get_subdir(i)->get_path());
+ _get_all_items_in_dir(efsd->get_subdir(i), files, folders);
}
for (int i = 0; i < efsd->get_file_count(); i++) {
files.push_back(efsd->get_file_path(i));
@@ -746,7 +748,8 @@ void FileSystemDock::_find_remaps(EditorFileSystemDirectory *efsd, const Map<Str
}
}
-void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_new_path, Map<String, String> &p_renames) const {
+void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_new_path,
+ Map<String, String> &p_file_renames, Map<String, String> &p_folder_renames) const {
//Ensure folder paths end with "/"
String old_path = (p_item.is_file || p_item.path.ends_with("/")) ? p_item.path : (p_item.path + "/");
String new_path = (p_item.is_file || p_new_path.ends_with("/")) ? p_new_path : (p_new_path + "/");
@@ -763,11 +766,13 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_
}
//Build a list of files which will have new paths as a result of this operation
- Vector<String> changed_paths;
+ Vector<String> file_changed_paths;
+ Vector<String> folder_changed_paths;
if (p_item.is_file) {
- changed_paths.push_back(old_path);
+ file_changed_paths.push_back(old_path);
} else {
- _get_all_files_in_dir(EditorFileSystem::get_singleton()->get_filesystem_path(old_path), changed_paths);
+ folder_changed_paths.push_back(old_path);
+ _get_all_items_in_dir(EditorFileSystem::get_singleton()->get_filesystem_path(old_path), file_changed_paths, folder_changed_paths);
}
DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
@@ -783,12 +788,12 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_
}
// update scene if it is open
- for (int i = 0; i < changed_paths.size(); ++i) {
- String new_item_path = p_item.is_file ? new_path : changed_paths[i].replace_first(old_path, new_path);
- if (ResourceLoader::get_resource_type(new_item_path) == "PackedScene" && editor->is_scene_open(changed_paths[i])) {
+ for (int i = 0; i < file_changed_paths.size(); ++i) {
+ String new_item_path = p_item.is_file ? new_path : file_changed_paths[i].replace_first(old_path, new_path);
+ if (ResourceLoader::get_resource_type(new_item_path) == "PackedScene" && editor->is_scene_open(file_changed_paths[i])) {
EditorData *ed = &editor->get_editor_data();
for (int j = 0; j < ed->get_edited_scene_count(); j++) {
- if (ed->get_scene_path(j) == changed_paths[i]) {
+ if (ed->get_scene_path(j) == file_changed_paths[i]) {
ed->get_edited_scene_root(j)->set_filename(new_item_path);
break;
}
@@ -797,9 +802,12 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_
}
//Only treat as a changed dependency if it was successfully moved
- for (int i = 0; i < changed_paths.size(); ++i) {
- p_renames[changed_paths[i]] = changed_paths[i].replace_first(old_path, new_path);
- print_line(" Remap: " + changed_paths[i] + " -> " + p_renames[changed_paths[i]]);
+ for (int i = 0; i < file_changed_paths.size(); ++i) {
+ p_file_renames[file_changed_paths[i]] = file_changed_paths[i].replace_first(old_path, new_path);
+ print_line(" Remap: " + file_changed_paths[i] + " -> " + p_file_renames[file_changed_paths[i]]);
+ }
+ for (int i = 0; i < folder_changed_paths.size(); ++i) {
+ p_folder_renames[folder_changed_paths[i]] = folder_changed_paths[i].replace_first(old_path, new_path);
}
} else {
EditorNode::get_singleton()->add_io_error(TTR("Error moving:") + "\n" + old_path + "\n");
@@ -912,6 +920,25 @@ void FileSystemDock::_update_dependencies_after_move(const Map<String, String> &
}
}
+void FileSystemDock::_update_favorite_dirs_list_after_move(const Map<String, String> &p_renames) const {
+
+ Vector<String> favorite_dirs = EditorSettings::get_singleton()->get_favorite_dirs();
+ Vector<String> new_favorite_dirs;
+
+ for (int i = 0; i < favorite_dirs.size(); i++) {
+ String old_path = favorite_dirs[i] + "/";
+
+ if (p_renames.has(old_path)) {
+ String new_path = p_renames[old_path];
+ new_favorite_dirs.push_back(new_path.substr(0, new_path.length() - 1));
+ } else {
+ new_favorite_dirs.push_back(favorite_dirs[i]);
+ }
+ }
+
+ EditorSettings::get_singleton()->set_favorite_dirs(new_favorite_dirs);
+}
+
void FileSystemDock::_make_dir_confirm() {
String dir_name = make_dir_dialog_text->get_text().strip_edges();
@@ -970,10 +997,12 @@ void FileSystemDock::_rename_operation_confirm() {
}
memdelete(da);
- Map<String, String> renames;
- _try_move_item(to_rename, new_path, renames);
- _update_dependencies_after_move(renames);
- _update_resource_paths_after_move(renames);
+ Map<String, String> file_renames;
+ Map<String, String> folder_renames;
+ _try_move_item(to_rename, new_path, file_renames, folder_renames);
+ _update_dependencies_after_move(file_renames);
+ _update_resource_paths_after_move(file_renames);
+ _update_favorite_dirs_list_after_move(folder_renames);
//Rescan everything
print_line("call rescan!");
@@ -1017,15 +1046,17 @@ void FileSystemDock::_duplicate_operation_confirm() {
void FileSystemDock::_move_operation_confirm(const String &p_to_path) {
- Map<String, String> renames;
+ Map<String, String> file_renames;
+ Map<String, String> folder_renames;
for (int i = 0; i < to_move.size(); i++) {
String old_path = to_move[i].path.ends_with("/") ? to_move[i].path.substr(0, to_move[i].path.length() - 1) : to_move[i].path;
String new_path = p_to_path.plus_file(old_path.get_file());
- _try_move_item(to_move[i], new_path, renames);
+ _try_move_item(to_move[i], new_path, file_renames, folder_renames);
}
- _update_dependencies_after_move(renames);
- _update_resource_paths_after_move(renames);
+ _update_dependencies_after_move(file_renames);
+ _update_resource_paths_after_move(file_renames);
+ _update_favorite_dirs_list_after_move(folder_renames);
print_line("call rescan!");
_rescan();
@@ -1034,8 +1065,19 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path) {
void FileSystemDock::_file_option(int p_option) {
switch (p_option) {
case FILE_SHOW_IN_EXPLORER: {
- String dir = ProjectSettings::get_singleton()->globalize_path(this->path);
- OS::get_singleton()->shell_open(String("file://") + dir);
+
+ String path = this->path;
+
+ // first try to grab directory from selected file, so that it works for searched files
+ int idx = files->get_current();
+
+ if (idx >= 0 && idx < files->get_item_count()) {
+ path = files->get_item_metadata(idx);
+ path = path.get_base_dir();
+ }
+
+ path = ProjectSettings::get_singleton()->globalize_path(path);
+ OS::get_singleton()->shell_open(String("file://") + path);
} break;
case FILE_OPEN: {
for (int i = 0; i < files->get_item_count(); i++) {
@@ -1660,6 +1702,8 @@ void FileSystemDock::_files_gui_input(Ref<InputEvent> p_event) {
_file_option(FILE_COPY_PATH);
} else if (ED_IS_SHORTCUT("filesystem_dock/delete", p_event)) {
_file_option(FILE_REMOVE);
+ } else if (ED_IS_SHORTCUT("filesystem_dock/rename", p_event)) {
+ _file_option(FILE_RENAME);
}
}
}
@@ -1770,6 +1814,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
ED_SHORTCUT("filesystem_dock/copy_path", TTR("Copy Path"), KEY_MASK_CMD | KEY_C);
ED_SHORTCUT("filesystem_dock/duplicate", TTR("Duplicate..."), KEY_MASK_CMD | KEY_D);
ED_SHORTCUT("filesystem_dock/delete", TTR("Delete"), KEY_DELETE);
+ ED_SHORTCUT("filesystem_dock/rename", TTR("Rename"));
HBoxContainer *toolbar_hbc = memnew(HBoxContainer);
add_child(toolbar_hbc);
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 377316d1ba..c8448a1022 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -176,12 +176,13 @@ private:
void _file_selected();
void _dir_selected();
- void _get_all_files_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files) const;
+ void _get_all_items_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files, Vector<String> &folders) const;
void _find_remaps(EditorFileSystemDirectory *efsd, const Map<String, String> &renames, Vector<String> &to_remaps) const;
- void _try_move_item(const FileOrFolder &p_item, const String &p_new_path, Map<String, String> &p_renames) const;
+ void _try_move_item(const FileOrFolder &p_item, const String &p_new_path, Map<String, String> &p_file_renames, Map<String, String> &p_folder_renames) const;
void _try_duplicate_item(const FileOrFolder &p_item, const String &p_new_path) const;
void _update_dependencies_after_move(const Map<String, String> &p_renames) const;
void _update_resource_paths_after_move(const Map<String, String> &p_renames) const;
+ void _update_favorite_dirs_list_after_move(const Map<String, String> &p_renames) const;
void _make_dir_confirm();
void _rename_operation_confirm();
diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp
new file mode 100644
index 0000000000..9442bbc0e8
--- /dev/null
+++ b/editor/find_in_files.cpp
@@ -0,0 +1,829 @@
+/*************************************************************************/
+/* find_in_files.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 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 "find_in_files.h"
+#include "core/os/dir_access.h"
+#include "core/os/os.h"
+#include "editor_scale.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/check_box.h"
+#include "scene/gui/file_dialog.h"
+#include "scene/gui/grid_container.h"
+#include "scene/gui/item_list.h"
+#include "scene/gui/label.h"
+#include "scene/gui/line_edit.h"
+#include "scene/gui/progress_bar.h"
+
+#define ROOT_PREFIX "res://"
+
+const char *FindInFiles::SIGNAL_RESULT_FOUND = "result_found";
+const char *FindInFiles::SIGNAL_FINISHED = "finished";
+
+// TODO Would be nice in Vector and PoolVectors
+template <typename T>
+inline void pop_back(T &container) {
+ container.resize(container.size() - 1);
+}
+
+// TODO Copied from TextEdit private, would be nice to extract it in a single place
+static bool is_text_char(CharType c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+}
+
+FindInFiles::FindInFiles() {
+ _root_prefix = ROOT_PREFIX;
+ _extension_filter.insert("gd");
+ _extension_filter.insert("cs");
+ _searching = false;
+ _whole_words = true;
+ _match_case = true;
+}
+
+void FindInFiles::set_search_text(String p_pattern) {
+ _pattern = p_pattern;
+}
+
+void FindInFiles::set_whole_words(bool p_whole_word) {
+ _whole_words = p_whole_word;
+}
+
+void FindInFiles::set_match_case(bool p_match_case) {
+ _match_case = p_match_case;
+}
+
+void FindInFiles::set_folder(String folder) {
+ _root_dir = folder;
+}
+
+void FindInFiles::set_filter(const Set<String> &exts) {
+ _extension_filter = exts;
+}
+
+void FindInFiles::_notification(int p_notification) {
+ if (p_notification == NOTIFICATION_PROCESS) {
+ _process();
+ }
+}
+
+void FindInFiles::start() {
+ if (_pattern == "") {
+ print_line("Nothing to search, pattern is empty");
+ emit_signal(SIGNAL_FINISHED);
+ return;
+ }
+ if (_extension_filter.size() == 0) {
+ print_line("Nothing to search, filter matches no files");
+ emit_signal(SIGNAL_FINISHED);
+ return;
+ }
+
+ // Init search
+ _current_dir = "";
+ PoolStringArray init_folder;
+ init_folder.append(_root_dir);
+ _folders_stack.push_back(init_folder);
+
+ _initial_files_count = 0;
+
+ _searching = true;
+ set_process(true);
+}
+
+void FindInFiles::stop() {
+ _searching = false;
+ _current_dir = "";
+ set_process(false);
+}
+
+void FindInFiles::_process() {
+ // This part can be moved to a thread if needed
+
+ OS &os = *OS::get_singleton();
+ float duration = 0.0;
+ while (duration < 1.0 / 120.0) {
+ float time_before = os.get_ticks_msec();
+ _iterate();
+ duration += (os.get_ticks_msec() - time_before);
+ }
+}
+
+void FindInFiles::_iterate() {
+
+ if (_folders_stack.size() != 0) {
+
+ // Scan folders first so we can build a list of files and have progress info later
+
+ PoolStringArray &folders_to_scan = _folders_stack[_folders_stack.size() - 1];
+
+ if (folders_to_scan.size() != 0) {
+ // Scan one folder below
+
+ String folder_name = folders_to_scan[folders_to_scan.size() - 1];
+ pop_back(folders_to_scan);
+
+ _current_dir = _current_dir.plus_file(folder_name);
+
+ PoolStringArray sub_dirs;
+ _scan_dir(_root_prefix + _current_dir, sub_dirs);
+
+ if (sub_dirs.size() != 0) {
+ _folders_stack.push_back(sub_dirs);
+ }
+
+ } else {
+ // Go back one level
+
+ pop_back(_folders_stack);
+ _current_dir = _current_dir.get_base_dir();
+
+ if (_folders_stack.size() == 0) {
+ // All folders scanned
+ _initial_files_count = _files_to_scan.size();
+ }
+ }
+
+ } else if (_files_to_scan.size() != 0) {
+
+ // Then scan files
+
+ String fpath = _files_to_scan[_files_to_scan.size() - 1];
+ pop_back(_files_to_scan);
+ _scan_file(_root_prefix + fpath);
+
+ } else {
+ print_line("Search complete");
+ set_process(false);
+ _current_dir = "";
+ _searching = false;
+ emit_signal(SIGNAL_FINISHED);
+ }
+}
+
+float FindInFiles::get_progress() const {
+ if (_initial_files_count != 0) {
+ return static_cast<float>(_initial_files_count - _files_to_scan.size()) / static_cast<float>(_initial_files_count);
+ }
+ return 0;
+}
+
+void FindInFiles::_scan_dir(String path, PoolStringArray &out_folders) {
+
+ DirAccess *dir = DirAccess::open(path);
+ if (dir == NULL) {
+ print_line("Cannot open directory! " + path);
+ return;
+ }
+
+ //print_line(String("Scanning ") + path);
+
+ dir->list_dir_begin();
+
+ for (int i = 0; i < 1000; ++i) {
+ String file = dir->get_next();
+
+ if (file == "")
+ break;
+
+ // Ignore special dirs and hidden dirs (such as .git and .import)
+ if (file == "." || file == ".." || file.begins_with("."))
+ continue;
+
+ if (dir->current_is_dir())
+ out_folders.append(file);
+
+ else {
+ String file_ext = file.get_extension();
+ if (_extension_filter.has(file_ext)) {
+ _files_to_scan.push_back(file);
+ }
+ }
+ }
+}
+
+void FindInFiles::_scan_file(String fpath) {
+
+ FileAccess *f = FileAccess::open(fpath, FileAccess::READ);
+ if (f == NULL) {
+ f->close();
+ print_line(String("Cannot open file ") + fpath);
+ return;
+ }
+
+ int line_number = 0;
+
+ while (!f->eof_reached()) {
+
+ // line number starts at 1
+ ++line_number;
+
+ int begin = 0;
+ int end = 0;
+
+ String line = f->get_line();
+
+ // Find all occurrences in the current line
+ while (true) {
+ begin = _match_case ? line.find(_pattern, end) : line.findn(_pattern, end);
+
+ if (begin == -1)
+ break;
+
+ end = begin + _pattern.length();
+
+ if (_whole_words) {
+ if (begin > 0 && is_text_char(line[begin - 1])) {
+ continue;
+ }
+ if (end < line.size() && is_text_char(line[end])) {
+ continue;
+ }
+ }
+
+ emit_signal(SIGNAL_RESULT_FOUND, fpath, line_number, begin, end, line);
+ }
+ }
+
+ f->close();
+}
+
+void FindInFiles::_bind_methods() {
+
+ ADD_SIGNAL(MethodInfo(SIGNAL_RESULT_FOUND,
+ PropertyInfo(Variant::STRING, "path"),
+ PropertyInfo(Variant::INT, "line_number"),
+ PropertyInfo(Variant::INT, "begin"),
+ PropertyInfo(Variant::INT, "end"),
+ PropertyInfo(Variant::STRING, "text")));
+
+ ADD_SIGNAL(MethodInfo(SIGNAL_FINISHED));
+}
+
+//-----------------------------------------------------------------------------
+const char *FindInFilesDialog::SIGNAL_FIND_REQUESTED = "find_requested";
+const char *FindInFilesDialog::SIGNAL_REPLACE_REQUESTED = "replace_requested";
+
+FindInFilesDialog::FindInFilesDialog() {
+
+ set_custom_minimum_size(Size2(400, 190));
+ set_resizable(true);
+ set_title(TTR("Find in files"));
+
+ VBoxContainer *vbc = memnew(VBoxContainer);
+ vbc->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 8 * EDSCALE);
+ vbc->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 8 * EDSCALE);
+ vbc->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -8 * EDSCALE);
+ vbc->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -8 * EDSCALE);
+ add_child(vbc);
+
+ GridContainer *gc = memnew(GridContainer);
+ gc->set_columns(2);
+ vbc->add_child(gc);
+
+ Label *find_label = memnew(Label);
+ find_label->set_text(TTR("Find: "));
+ gc->add_child(find_label);
+
+ _search_text_line_edit = memnew(LineEdit);
+ _search_text_line_edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ _search_text_line_edit->connect("text_changed", this, "_on_search_text_modified");
+ _search_text_line_edit->connect("text_entered", this, "_on_search_text_entered");
+ gc->add_child(_search_text_line_edit);
+
+ {
+ Control *placeholder = memnew(Control);
+ gc->add_child(placeholder);
+ }
+
+ {
+ HBoxContainer *hbc = memnew(HBoxContainer);
+
+ _whole_words_checkbox = memnew(CheckBox);
+ _whole_words_checkbox->set_text(TTR("Whole words"));
+ _whole_words_checkbox->set_pressed(true);
+ hbc->add_child(_whole_words_checkbox);
+
+ _match_case_checkbox = memnew(CheckBox);
+ _match_case_checkbox->set_text(TTR("Match case"));
+ _match_case_checkbox->set_pressed(true);
+ hbc->add_child(_match_case_checkbox);
+
+ gc->add_child(hbc);
+ }
+
+ Label *folder_label = memnew(Label);
+ folder_label->set_text(TTR("Folder: "));
+ gc->add_child(folder_label);
+
+ {
+ HBoxContainer *hbc = memnew(HBoxContainer);
+
+ Label *prefix_label = memnew(Label);
+ prefix_label->set_text(ROOT_PREFIX);
+ hbc->add_child(prefix_label);
+
+ _folder_line_edit = memnew(LineEdit);
+ _folder_line_edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ hbc->add_child(_folder_line_edit);
+
+ Button *folder_button = memnew(Button);
+ folder_button->set_text("...");
+ folder_button->connect("pressed", this, "_on_folder_button_pressed");
+ hbc->add_child(folder_button);
+
+ _folder_dialog = memnew(FileDialog);
+ _folder_dialog->set_mode(FileDialog::MODE_OPEN_DIR);
+ _folder_dialog->connect("dir_selected", this, "_on_folder_selected");
+ add_child(_folder_dialog);
+
+ gc->add_child(hbc);
+ }
+
+ Label *filter_label = memnew(Label);
+ filter_label->set_text(TTR("Filter: "));
+ gc->add_child(filter_label);
+
+ {
+ HBoxContainer *hbc = memnew(HBoxContainer);
+
+ Vector<String> exts;
+ exts.push_back("gd");
+ exts.push_back("cs");
+
+ for (int i = 0; i < exts.size(); ++i) {
+ CheckBox *cb = memnew(CheckBox);
+ cb->set_text(exts[i]);
+ cb->set_pressed(true);
+ hbc->add_child(cb);
+ _filters.push_back(cb);
+ }
+
+ gc->add_child(hbc);
+ }
+
+ {
+ Control *placeholder = memnew(Control);
+ placeholder->set_custom_minimum_size(Size2(0, EDSCALE * 16));
+ vbc->add_child(placeholder);
+ }
+
+ {
+ HBoxContainer *hbc = memnew(HBoxContainer);
+ hbc->set_alignment(HBoxContainer::ALIGN_CENTER);
+
+ _find_button = memnew(Button);
+ _find_button->set_text(TTR("Find..."));
+ _find_button->connect("pressed", this, "_on_find_button_pressed");
+ _find_button->set_disabled(true);
+ hbc->add_child(_find_button);
+
+ {
+ Control *placeholder = memnew(Control);
+ placeholder->set_custom_minimum_size(Size2(EDSCALE * 16, 0));
+ hbc->add_child(placeholder);
+ }
+
+ _replace_button = memnew(Button);
+ _replace_button->set_text(TTR("Replace..."));
+ _replace_button->connect("pressed", this, "_on_replace_button_pressed");
+ _replace_button->set_disabled(true);
+ hbc->add_child(_replace_button);
+
+ {
+ Control *placeholder = memnew(Control);
+ placeholder->set_custom_minimum_size(Size2(EDSCALE * 16, 0));
+ hbc->add_child(placeholder);
+ }
+
+ Button *cancel_button = memnew(Button);
+ cancel_button->set_text(TTR("Cancel"));
+ cancel_button->connect("pressed", this, "hide");
+ hbc->add_child(cancel_button);
+
+ vbc->add_child(hbc);
+ }
+}
+
+void FindInFilesDialog::set_search_text(String text) {
+ _search_text_line_edit->set_text(text);
+}
+
+String FindInFilesDialog::get_search_text() const {
+ String text = _search_text_line_edit->get_text();
+ return text.strip_edges();
+}
+
+bool FindInFilesDialog::is_match_case() const {
+ return _match_case_checkbox->is_pressed();
+}
+
+bool FindInFilesDialog::is_whole_words() const {
+ return _whole_words_checkbox->is_pressed();
+}
+
+String FindInFilesDialog::get_folder() const {
+ String text = _folder_line_edit->get_text();
+ return text.strip_edges();
+}
+
+Set<String> FindInFilesDialog::get_filter() const {
+ Set<String> filters;
+ for (int i = 0; i < _filters.size(); ++i) {
+ CheckBox *cb = _filters[i];
+ if (cb->is_pressed()) {
+ filters.insert(_filters[i]->get_text());
+ }
+ }
+ return filters;
+}
+
+void FindInFilesDialog::_notification(int p_what) {
+ if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+ if (is_visible()) {
+ // Doesn't work more than once if not deferred...
+ _search_text_line_edit->call_deferred("grab_focus");
+ _search_text_line_edit->select_all();
+ }
+ }
+}
+
+void FindInFilesDialog::_on_folder_button_pressed() {
+ _folder_dialog->popup_centered_ratio();
+}
+
+void FindInFilesDialog::_on_find_button_pressed() {
+ emit_signal(SIGNAL_FIND_REQUESTED);
+ hide();
+}
+
+void FindInFilesDialog::_on_replace_button_pressed() {
+ emit_signal(SIGNAL_REPLACE_REQUESTED);
+ hide();
+}
+
+void FindInFilesDialog::_on_search_text_modified(String text) {
+
+ ERR_FAIL_COND(!_find_button);
+ ERR_FAIL_COND(!_replace_button);
+
+ _find_button->set_disabled(get_search_text().empty());
+ _replace_button->set_disabled(get_search_text().empty());
+}
+
+void FindInFilesDialog::_on_search_text_entered(String text) {
+ // This allows to trigger a global search without leaving the keyboard
+ if (!_find_button->is_disabled())
+ _on_find_button_pressed();
+}
+
+void FindInFilesDialog::_on_folder_selected(String path) {
+ int i = path.find("://");
+ if (i != -1)
+ path = path.right(i + 3);
+ _folder_line_edit->set_text(path);
+}
+
+void FindInFilesDialog::_bind_methods() {
+
+ ClassDB::bind_method("_on_folder_button_pressed", &FindInFilesDialog::_on_folder_button_pressed);
+ ClassDB::bind_method("_on_find_button_pressed", &FindInFilesDialog::_on_find_button_pressed);
+ ClassDB::bind_method("_on_replace_button_pressed", &FindInFilesDialog::_on_replace_button_pressed);
+ ClassDB::bind_method("_on_folder_selected", &FindInFilesDialog::_on_folder_selected);
+ ClassDB::bind_method("_on_search_text_modified", &FindInFilesDialog::_on_search_text_modified);
+ ClassDB::bind_method("_on_search_text_entered", &FindInFilesDialog::_on_search_text_entered);
+
+ ADD_SIGNAL(MethodInfo(SIGNAL_FIND_REQUESTED));
+ ADD_SIGNAL(MethodInfo(SIGNAL_REPLACE_REQUESTED));
+}
+
+//-----------------------------------------------------------------------------
+const char *FindInFilesPanel::SIGNAL_RESULT_SELECTED = "result_selected";
+const char *FindInFilesPanel::SIGNAL_FILES_MODIFIED = "files_modified";
+
+FindInFilesPanel::FindInFilesPanel() {
+
+ _finder = memnew(FindInFiles);
+ _finder->connect(FindInFiles::SIGNAL_RESULT_FOUND, this, "_on_result_found");
+ _finder->connect(FindInFiles::SIGNAL_FINISHED, this, "_on_finished");
+ add_child(_finder);
+
+ VBoxContainer *vbc = memnew(VBoxContainer);
+ vbc->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 0);
+ vbc->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 0);
+ vbc->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0);
+ vbc->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0);
+ add_child(vbc);
+
+ {
+ HBoxContainer *hbc = memnew(HBoxContainer);
+
+ Label *find_label = memnew(Label);
+ find_label->set_text(TTR("Find: "));
+ hbc->add_child(find_label);
+
+ _search_text_label = memnew(Label);
+ _search_text_label->add_font_override("font", get_font("source", "EditorFonts"));
+ hbc->add_child(_search_text_label);
+
+ _progress_bar = memnew(ProgressBar);
+ _progress_bar->set_h_size_flags(SIZE_EXPAND_FILL);
+ hbc->add_child(_progress_bar);
+ set_progress_visible(false);
+
+ _status_label = memnew(Label);
+ hbc->add_child(_status_label);
+
+ _cancel_button = memnew(Button);
+ _cancel_button->set_text(TTR("Cancel"));
+ _cancel_button->connect("pressed", this, "_on_cancel_button_clicked");
+ _cancel_button->set_disabled(true);
+ hbc->add_child(_cancel_button);
+
+ vbc->add_child(hbc);
+ }
+
+ // In the future, this should be replaced by a more specific list container,
+ // which can highlight text regions and change opacity for enabled/disabled states
+ _results_display = memnew(ItemList);
+ _results_display->add_font_override("font", get_font("source", "EditorFonts"));
+ _results_display->set_v_size_flags(SIZE_EXPAND_FILL);
+ _results_display->connect("item_selected", this, "_on_result_selected");
+ vbc->add_child(_results_display);
+
+ {
+ _replace_container = memnew(HBoxContainer);
+
+ Label *replace_label = memnew(Label);
+ replace_label->set_text(TTR("Replace: "));
+ _replace_container->add_child(replace_label);
+
+ _replace_line_edit = memnew(LineEdit);
+ _replace_line_edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ _replace_line_edit->connect("text_changed", this, "_on_replace_text_changed");
+ _replace_container->add_child(_replace_line_edit);
+
+ _replace_all_button = memnew(Button);
+ _replace_all_button->set_text(TTR("Replace all (no undo)"));
+ _replace_all_button->connect("pressed", this, "_on_replace_all_clicked");
+ _replace_container->add_child(_replace_all_button);
+
+ _replace_container->hide();
+
+ vbc->add_child(_replace_container);
+ }
+}
+
+void FindInFilesPanel::set_with_replace(bool with_replace) {
+
+ _replace_container->set_visible(with_replace);
+}
+
+void FindInFilesPanel::start_search() {
+
+ _results_display->clear();
+ _status_label->set_text(TTR("Searching..."));
+ _search_text_label->set_text(_finder->get_search_text());
+
+ set_process(true);
+ set_progress_visible(true);
+
+ _finder->start();
+
+ update_replace_buttons();
+ _cancel_button->set_disabled(false);
+}
+
+void FindInFilesPanel::stop_search() {
+
+ _finder->stop();
+
+ _status_label->set_text("");
+ update_replace_buttons();
+ set_progress_visible(false);
+ _cancel_button->set_disabled(true);
+}
+
+void FindInFilesPanel::_notification(int p_what) {
+ if (p_what == NOTIFICATION_PROCESS) {
+ _progress_bar->set_as_ratio(_finder->get_progress());
+ }
+}
+
+void FindInFilesPanel::_on_result_found(String fpath, int line_number, int begin, int end, String text) {
+
+ int i = _results_display->get_item_count();
+ _results_display->add_item(fpath + ": " + String::num(line_number) + ": " + text.replace("\t", " "));
+ _results_display->set_item_metadata(i, varray(fpath, line_number, begin, end));
+}
+
+void FindInFilesPanel::_on_finished() {
+
+ _status_label->set_text(TTR("Search complete"));
+ update_replace_buttons();
+ set_progress_visible(false);
+ _cancel_button->set_disabled(true);
+}
+
+void FindInFilesPanel::_on_cancel_button_clicked() {
+ stop_search();
+}
+
+void FindInFilesPanel::_on_result_selected(int i) {
+
+ Array meta = _results_display->get_item_metadata(i);
+ emit_signal(SIGNAL_RESULT_SELECTED, meta[0], meta[1], meta[2], meta[3]);
+}
+
+void FindInFilesPanel::_on_replace_text_changed(String text) {
+ update_replace_buttons();
+}
+
+void FindInFilesPanel::_on_replace_all_clicked() {
+
+ String replace_text = get_replace_text();
+ ERR_FAIL_COND(replace_text.empty());
+
+ String last_fpath;
+ PoolIntArray locations;
+ PoolStringArray modified_files;
+
+ for (int i = 0; i < _results_display->get_item_count(); ++i) {
+
+ Array meta = _results_display->get_item_metadata(i);
+
+ String fpath = meta[0];
+
+ // Results are sorted by file, so we can batch replaces
+ if (fpath != last_fpath) {
+ if (locations.size() != 0) {
+ apply_replaces_in_file(last_fpath, locations, replace_text);
+ modified_files.append(last_fpath);
+ locations.resize(0);
+ }
+ }
+
+ locations.append(meta[1]); // line_number
+ locations.append(meta[2]); // begin
+ locations.append(meta[3]); // end
+
+ last_fpath = fpath;
+ }
+
+ if (locations.size() != 0) {
+ apply_replaces_in_file(last_fpath, locations, replace_text);
+ modified_files.append(last_fpath);
+ }
+
+ // Hide replace bar so we can't trigger the action twice without doing a new search
+ set_with_replace(false);
+
+ emit_signal(SIGNAL_FILES_MODIFIED, modified_files);
+}
+
+// Same as get_line, but preserves line ending characters
+class ConservativeGetLine {
+public:
+ String get_line(FileAccess *f) {
+
+ _line_buffer.clear();
+
+ CharType c = f->get_8();
+
+ while (!f->eof_reached()) {
+
+ if (c == '\n') {
+ _line_buffer.push_back(c);
+ _line_buffer.push_back(0);
+ return String::utf8(_line_buffer.ptr());
+
+ } else if (c == '\0') {
+ _line_buffer.push_back(c);
+ return String::utf8(_line_buffer.ptr());
+
+ } else if (c != '\r') {
+ _line_buffer.push_back(c);
+ }
+
+ c = f->get_8();
+ }
+
+ _line_buffer.push_back(0);
+ return String::utf8(_line_buffer.ptr());
+ }
+
+private:
+ Vector<char> _line_buffer;
+};
+
+void FindInFilesPanel::apply_replaces_in_file(String fpath, PoolIntArray locations, String text) {
+
+ ERR_FAIL_COND(locations.size() % 3 != 0);
+
+ //print_line(String("Replacing {0} occurrences in {1}").format(varray(fpath, locations.size() / 3)));
+
+ // If the file is already open, I assume the editor will reload it.
+ // If there are unsaved changes, the user will be asked on focus,
+ // however that means either loosing changes or loosing replaces.
+
+ FileAccess *f = FileAccess::open(fpath, FileAccess::READ);
+ ERR_FAIL_COND(f == NULL);
+
+ String buffer;
+ int current_line = 1;
+
+ ConservativeGetLine conservative;
+
+ String line = conservative.get_line(f);
+
+ PoolIntArray::Read locations_read = locations.read();
+ for (int i = 0; i < locations.size(); i += 3) {
+
+ int repl_line_number = locations_read[i];
+ int repl_begin = locations_read[i + 1];
+ int repl_end = locations_read[i + 2];
+
+ while (current_line < repl_line_number) {
+ buffer += line;
+ line = conservative.get_line(f);
+ ++current_line;
+ }
+
+ line = line.left(repl_begin) + text + line.right(repl_end);
+ }
+
+ buffer += line;
+
+ while (!f->eof_reached()) {
+ buffer += conservative.get_line(f);
+ }
+
+ // Now the modified contents are in the buffer, rewrite the file with our changes
+
+ Error err = f->reopen(fpath, FileAccess::WRITE);
+ ERR_FAIL_COND(err != OK);
+
+ f->store_string(buffer);
+
+ f->close();
+}
+
+String FindInFilesPanel::get_replace_text() {
+ return _replace_line_edit->get_text().strip_edges();
+}
+
+void FindInFilesPanel::update_replace_buttons() {
+
+ String text = get_replace_text();
+ bool disabled = text.empty() || _finder->is_searching();
+
+ _replace_all_button->set_disabled(disabled);
+}
+
+void FindInFilesPanel::set_progress_visible(bool visible) {
+ _progress_bar->set_self_modulate(Color(1, 1, 1, visible ? 1 : 0));
+}
+
+void FindInFilesPanel::_bind_methods() {
+
+ ClassDB::bind_method("_on_result_found", &FindInFilesPanel::_on_result_found);
+ ClassDB::bind_method("_on_finished", &FindInFilesPanel::_on_finished);
+ ClassDB::bind_method("_on_cancel_button_clicked", &FindInFilesPanel::_on_cancel_button_clicked);
+ ClassDB::bind_method("_on_result_selected", &FindInFilesPanel::_on_result_selected);
+ ClassDB::bind_method("_on_replace_text_changed", &FindInFilesPanel::_on_replace_text_changed);
+ ClassDB::bind_method("_on_replace_all_clicked", &FindInFilesPanel::_on_replace_all_clicked);
+
+ ADD_SIGNAL(MethodInfo(SIGNAL_RESULT_SELECTED,
+ PropertyInfo(Variant::STRING, "path"),
+ PropertyInfo(Variant::INT, "line_number"),
+ PropertyInfo(Variant::INT, "begin"),
+ PropertyInfo(Variant::INT, "end")));
+
+ ADD_SIGNAL(MethodInfo(SIGNAL_FILES_MODIFIED, PropertyInfo(Variant::STRING, "paths")));
+}
diff --git a/editor/find_in_files.h b/editor/find_in_files.h
new file mode 100644
index 0000000000..d57184960b
--- /dev/null
+++ b/editor/find_in_files.h
@@ -0,0 +1,184 @@
+/*************************************************************************/
+/* find_in_files.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 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 FIND_IN_FILES_H
+#define FIND_IN_FILES_H
+
+#include "scene/gui/dialogs.h"
+
+// Performs the actual search
+class FindInFiles : public Node {
+ GDCLASS(FindInFiles, Node)
+public:
+ static const char *SIGNAL_RESULT_FOUND;
+ static const char *SIGNAL_FINISHED;
+
+ FindInFiles();
+
+ void set_search_text(String p_pattern);
+ void set_whole_words(bool p_whole_word);
+ void set_match_case(bool p_match_case);
+ void set_folder(String folder);
+ void set_filter(const Set<String> &exts);
+
+ String get_search_text() const { return _pattern; }
+
+ bool is_whole_words() const { return _whole_words; }
+ bool is_match_case() const { return _match_case; }
+
+ void start();
+ void stop();
+
+ bool is_searching() const { return _searching; }
+ float get_progress() const;
+
+protected:
+ void _notification(int p_notification);
+
+ static void _bind_methods();
+
+private:
+ void _process();
+ void _iterate();
+ void _scan_dir(String path, PoolStringArray &out_folders);
+ void _scan_file(String fpath);
+
+ // Config
+ String _pattern;
+ Set<String> _extension_filter;
+ String _root_prefix;
+ String _root_dir;
+ bool _whole_words;
+ bool _match_case;
+
+ // State
+ bool _searching;
+ String _current_dir;
+ Vector<PoolStringArray> _folders_stack;
+ Vector<String> _files_to_scan;
+ int _initial_files_count;
+};
+
+class LineEdit;
+class CheckBox;
+class FileDialog;
+
+// Prompts search parameters
+class FindInFilesDialog : public WindowDialog {
+ GDCLASS(FindInFilesDialog, WindowDialog)
+public:
+ static const char *SIGNAL_FIND_REQUESTED;
+ static const char *SIGNAL_REPLACE_REQUESTED;
+
+ FindInFilesDialog();
+
+ void set_search_text(String text);
+
+ String get_search_text() const;
+ bool is_match_case() const;
+ bool is_whole_words() const;
+ String get_folder() const;
+ Set<String> get_filter() const;
+
+protected:
+ static void _bind_methods();
+
+ void _notification(int p_what);
+
+private:
+ void _on_folder_button_pressed();
+ void _on_find_button_pressed();
+ void _on_replace_button_pressed();
+ void _on_folder_selected(String path);
+ void _on_search_text_modified(String text);
+ void _on_search_text_entered(String text);
+
+ LineEdit *_search_text_line_edit;
+ LineEdit *_folder_line_edit;
+ Vector<CheckBox *> _filters;
+ CheckBox *_match_case_checkbox;
+ CheckBox *_whole_words_checkbox;
+ Button *_find_button;
+ Button *_replace_button;
+ FileDialog *_folder_dialog;
+};
+
+class Button;
+class ItemList;
+class ProgressBar;
+
+// Display search results
+class FindInFilesPanel : public Control {
+ GDCLASS(FindInFilesPanel, Control)
+public:
+ static const char *SIGNAL_RESULT_SELECTED;
+ static const char *SIGNAL_FILES_MODIFIED;
+
+ FindInFilesPanel();
+
+ FindInFiles *get_finder() const { return _finder; }
+
+ void set_with_replace(bool with_replace);
+
+ void start_search();
+ void stop_search();
+
+protected:
+ static void _bind_methods();
+
+ void _notification(int p_what);
+
+private:
+ void _on_result_found(String fpath, int line_number, int begin, int end, String text);
+ void _on_finished();
+ void _on_cancel_button_clicked();
+ void _on_result_selected(int i);
+ void _on_replace_text_changed(String text);
+ void _on_replace_all_clicked();
+
+ void apply_replaces_in_file(String fpath, PoolIntArray locations, String text);
+
+ void update_replace_buttons();
+ String get_replace_text();
+ void set_progress_visible(bool visible);
+
+ FindInFiles *_finder;
+ Label *_search_text_label;
+ ItemList *_results_display;
+ Label *_status_label;
+ Button *_cancel_button;
+ ProgressBar *_progress_bar;
+
+ HBoxContainer *_replace_container;
+ LineEdit *_replace_line_edit;
+ Button *_replace_all_button;
+};
+
+#endif // FIND_IN_FILES_H
diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
index 863b13cbd7..c1e897a04c 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -480,7 +480,7 @@ Error ColladaImport::_create_material(const String &p_target) {
}
}
- float roughness = Math::sqrt(1.0 - ((Math::log(effect.shininess) / Math::log(2.0)) / 8.0)); //not very right..
+ float roughness = (effect.shininess - 1.0) / 510;
material->set_roughness(roughness);
if (effect.double_sided) {
diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp
index 321d29f2f6..af79f9946a 100644
--- a/editor/import/editor_scene_importer_gltf.cpp
+++ b/editor/import/editor_scene_importer_gltf.cpp
@@ -1732,14 +1732,19 @@ void EditorSceneImporterGLTF::_generate_bone(GLTFState &state, int p_node, Vecto
for (int i = 0; i < n->joints.size(); i++) {
ERR_FAIL_COND(n->joints[i].skin < 0);
- int bone_index = skeletons[n->joints[i].skin]->get_bone_count();
- skeletons[n->joints[i].skin]->add_bone(n->name);
+ int bone_index = n->joints[i].bone;
+
+ Skeleton *s = skeletons[n->joints[i].skin];
+ while (s->get_bone_count() <= bone_index) {
+ s->add_bone("Bone " + itos(s->get_bone_count()));
+ }
+
if (p_parent_bones.size()) {
- skeletons[n->joints[i].skin]->set_bone_parent(bone_index, p_parent_bones[i]);
+ s->set_bone_parent(bone_index, p_parent_bones[i]);
}
- skeletons[n->joints[i].skin]->set_bone_rest(bone_index, state.skins[n->joints[i].skin].bones[n->joints[i].bone].inverse_bind.affine_inverse());
+ s->set_bone_rest(bone_index, state.skins[n->joints[i].skin].bones[n->joints[i].bone].inverse_bind.affine_inverse());
- n->godot_nodes.push_back(skeletons[n->joints[i].skin]);
+ n->godot_nodes.push_back(s);
n->joints[i].godot_bone_index = bone_index;
parent_bones.push_back(bone_index);
}
diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp
index 03155b3a48..debdeb1c4a 100644
--- a/editor/import/resource_importer_wav.cpp
+++ b/editor/import/resource_importer_wav.cpp
@@ -304,17 +304,23 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
int limit_rate_hz = p_options["force/max_rate_hz"];
if (limit_rate && rate > limit_rate_hz && rate > 0 && frames > 0) {
//resampleeee!!!
- int new_data_frames = frames * limit_rate_hz / rate;
+ int new_data_frames = (int)(frames * (float)limit_rate_hz / (float)rate);
+
+ print_line("\tresampling ratio: " + rtos((float)limit_rate_hz / (float)rate));
+ print_line("\tnew frames: " + itos(new_data_frames));
+
Vector<float> new_data;
new_data.resize(new_data_frames * format_channels);
for (int c = 0; c < format_channels; c++) {
+ float frac = .0f;
+ int ipos = 0;
+
for (int i = 0; i < new_data_frames; i++) {
//simple cubic interpolation should be enough.
- float pos = float(i) * frames / new_data_frames;
- float mu = pos - Math::floor(pos);
- int ipos = int(Math::floor(pos));
+
+ float mu = frac;
float y0 = data[MAX(0, ipos - 1) * format_channels + c];
float y1 = data[ipos * format_channels + c];
@@ -330,14 +336,22 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
float res = (a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3);
new_data[i * format_channels + c] = res;
+
+ // update position and always keep fractional part within ]0...1]
+ // in order to avoid 32bit floating point precision errors
+
+ frac += (float)rate / (float)limit_rate_hz;
+ int tpos = (int)Math::floor(frac);
+ ipos += tpos;
+ frac -= tpos;
}
}
if (loop) {
-
- loop_begin = loop_begin * new_data_frames / frames;
- loop_end = loop_end * new_data_frames / frames;
+ loop_begin = (int)(loop_begin * (float)new_data_frames / (float)frames);
+ loop_end = (int)(loop_end * (float)new_data_frames / (float)frames);
}
+
data = new_data;
rate = limit_rate_hz;
frames = new_data_frames;
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 04c9246aed..7612f18aff 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -1679,10 +1679,10 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
onion_skinning->get_popup()->add_separator();
onion_skinning->get_popup()->add_item(TTR("Depth"), -1);
onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true);
- onion_skinning->get_popup()->add_check_item(TTR("1 step"), ONION_SKINNING_1_STEP);
+ onion_skinning->get_popup()->add_radio_check_item(TTR("1 step"), ONION_SKINNING_1_STEP);
onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true);
- onion_skinning->get_popup()->add_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS);
- onion_skinning->get_popup()->add_check_item(TTR("3 steps"), ONION_SKINNING_3_STEPS);
+ onion_skinning->get_popup()->add_radio_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS);
+ onion_skinning->get_popup()->add_radio_check_item(TTR("3 steps"), ONION_SKINNING_3_STEPS);
onion_skinning->get_popup()->add_separator();
onion_skinning->get_popup()->add_check_item(TTR("Differences Only"), ONION_SKINNING_DIFFERENCES_ONLY);
onion_skinning->get_popup()->add_check_item(TTR("Force White Modulate"), ONION_SKINNING_FORCE_WHITE_MODULATE);
diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp
index f0e186e4b0..37213c1866 100644
--- a/editor/plugins/animation_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_tree_editor_plugin.cpp
@@ -756,6 +756,7 @@ void AnimationTreeEditor::_gui_input(Ref<InputEvent> p_event) {
if (rclick_type == CLICK_INPUT_SLOT || rclick_type == CLICK_OUTPUT_SLOT) {
node_popup->clear();
+ node_popup->set_size(Size2(1, 1));
node_popup->add_item(TTR("Disconnect"), NODE_DISCONNECT);
if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) {
node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT);
@@ -774,6 +775,7 @@ void AnimationTreeEditor::_gui_input(Ref<InputEvent> p_event) {
if (rclick_type == CLICK_NODE) {
node_popup->clear();
+ node_popup->set_size(Size2(1, 1));
node_popup->add_item(TTR("Rename"), NODE_RENAME);
node_popup->add_item(TTR("Remove"), NODE_ERASE);
if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION)
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index c203b4b81e..b72c9b25be 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -902,7 +902,7 @@ void EditorAssetLibrary::_search(int p_page) {
}
if (filter->get_text() != String()) {
- args += "&filter=" + filter->get_text().http_escape();
+ args += "&filter=" + filter->get_text().percent_encode();
}
if (p_page > 0) {
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index e49ce06100..bb94aee462 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -1720,8 +1720,6 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
selection_menu_additive_selection = b->get_shift();
selection_menu->set_global_position(b->get_global_position());
selection_menu->popup();
- selection_menu->call_deferred("grab_click_focus");
- selection_menu->set_invalidate_click_until_motion();
return true;
}
}
diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp
index 5020f5b851..8b44f672b0 100644
--- a/editor/plugins/item_list_editor_plugin.cpp
+++ b/editor/plugins/item_list_editor_plugin.cpp
@@ -42,9 +42,18 @@ bool ItemListPlugin::_set(const StringName &p_name, const Variant &p_value) {
set_item_text(idx, p_value);
else if (what == "icon")
set_item_icon(idx, p_value);
- else if (what == "checkable")
- set_item_checkable(idx, p_value);
- else if (what == "checked")
+ else if (what == "checkable") {
+ // This keeps compatibility to/from versions where this property was a boolean, before radio buttons
+ switch ((int)p_value) {
+ case 0:
+ case 1:
+ set_item_checkable(idx, p_value);
+ break;
+ case 2:
+ set_item_radio_checkable(idx, true);
+ break;
+ }
+ } else if (what == "checked")
set_item_checked(idx, p_value);
else if (what == "id")
set_item_id(idx, p_value);
@@ -68,9 +77,14 @@ bool ItemListPlugin::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = get_item_text(idx);
else if (what == "icon")
r_ret = get_item_icon(idx);
- else if (what == "checkable")
- r_ret = is_item_checkable(idx);
- else if (what == "checked")
+ else if (what == "checkable") {
+ // This keeps compatibility to/from versions where this property was a boolean, before radio buttons
+ if (!is_item_checkable(idx)) {
+ r_ret = 0;
+ } else {
+ r_ret = is_item_radio_checkable(idx) ? 2 : 1;
+ }
+ } else if (what == "checked")
r_ret = is_item_checked(idx);
else if (what == "id")
r_ret = get_item_id(idx);
@@ -95,7 +109,7 @@ void ItemListPlugin::_get_property_list(List<PropertyInfo> *p_list) const {
int flags = get_flags();
if (flags & FLAG_CHECKABLE) {
- p_list->push_back(PropertyInfo(Variant::BOOL, base + "checkable"));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base + "checkable", PROPERTY_HINT_ENUM, "No,As checkbox,As radio button"));
p_list->push_back(PropertyInfo(Variant::BOOL, base + "checked"));
}
diff --git a/editor/plugins/item_list_editor_plugin.h b/editor/plugins/item_list_editor_plugin.h
index bd7d3db10d..d6a071b9b9 100644
--- a/editor/plugins/item_list_editor_plugin.h
+++ b/editor/plugins/item_list_editor_plugin.h
@@ -74,7 +74,9 @@ public:
virtual Ref<Texture> get_item_icon(int p_idx) const { return Ref<Texture>(); };
virtual void set_item_checkable(int p_idx, bool p_check) {}
+ virtual void set_item_radio_checkable(int p_idx, bool p_check) {}
virtual bool is_item_checkable(int p_idx) const { return false; };
+ virtual bool is_item_radio_checkable(int p_idx) const { return false; };
virtual void set_item_checked(int p_idx, bool p_checked) {}
virtual bool is_item_checked(int p_idx) const { return false; };
@@ -145,7 +147,9 @@ public:
virtual Ref<Texture> get_item_icon(int p_idx) const { return pp->get_item_icon(p_idx); }
virtual void set_item_checkable(int p_idx, bool p_check) { pp->set_item_as_checkable(p_idx, p_check); }
+ virtual void set_item_radio_checkable(int p_idx, bool p_check) { pp->set_item_as_radio_checkable(p_idx, p_check); }
virtual bool is_item_checkable(int p_idx) const { return pp->is_item_checkable(p_idx); }
+ virtual bool is_item_radio_checkable(int p_idx) const { return pp->is_item_radio_checkable(p_idx); }
virtual void set_item_checked(int p_idx, bool p_checked) { pp->set_item_checked(p_idx, p_checked); }
virtual bool is_item_checked(int p_idx) const { return pp->is_item_checked(p_idx); }
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 09388870f1..9193b3fbbf 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -39,9 +39,11 @@
#include "core/project_settings.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "editor/find_in_files.h"
#include "editor/node_dock.h"
#include "editor/script_editor_debugger.h"
#include "scene/main/viewport.h"
+#include "script_text_editor.h"
/*** SCRIPT EDITOR ****/
@@ -54,6 +56,8 @@ void ScriptEditorBase::_bind_methods() {
ADD_SIGNAL(MethodInfo("request_open_script_at_line", PropertyInfo(Variant::OBJECT, "script"), PropertyInfo(Variant::INT, "line")));
ADD_SIGNAL(MethodInfo("request_save_history"));
ADD_SIGNAL(MethodInfo("go_to_help", PropertyInfo(Variant::STRING, "what")));
+ // TODO This signal is no use for VisualScript...
+ ADD_SIGNAL(MethodInfo("search_in_files_requested", PropertyInfo(Variant::STRING, "text")));
}
static bool _can_open_in_editor(Script *p_script) {
@@ -298,15 +302,9 @@ void ScriptEditor::_script_created(Ref<Script> p_script) {
void ScriptEditor::_goto_script_line2(int p_line) {
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return;
-
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
- if (!current)
- return;
-
- current->goto_line(p_line);
+ ScriptEditorBase *current = _get_current_editor();
+ if (current)
+ current->goto_line(p_line);
}
void ScriptEditor::_goto_script_line(REF p_script, int p_line) {
@@ -316,19 +314,22 @@ void ScriptEditor::_goto_script_line(REF p_script, int p_line) {
if (edit(p_script, p_line, 0)) {
editor->push_item(p_script.ptr());
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return;
-
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
- if (!current)
- return;
-
- current->goto_line(p_line, true);
+ ScriptEditorBase *current = _get_current_editor();
+ if (current)
+ current->goto_line(p_line, true);
}
}
}
+ScriptEditorBase *ScriptEditor::_get_current_editor() const {
+
+ int selected = tab_container->get_current_tab();
+ if (selected < 0 || selected >= tab_container->get_child_count())
+ return NULL;
+
+ return Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
+}
+
void ScriptEditor::_update_history_arrows() {
script_back->set_disabled(history_pos <= 0);
@@ -587,7 +588,7 @@ void ScriptEditor::_close_docs_tab() {
}
void ScriptEditor::_copy_script_path() {
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(tab_container->get_current_tab()));
+ ScriptEditorBase *se = _get_current_editor();
Ref<Script> script = se->get_edited_script();
OS::get_singleton()->set_clipboard(script->get_path());
}
@@ -819,11 +820,8 @@ void ScriptEditor::_file_dialog_action(String p_file) {
Ref<Script> ScriptEditor::_get_current_script() {
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return NULL;
+ ScriptEditorBase *current = _get_current_editor();
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
if (current) {
return current->get_edited_script();
} else {
@@ -939,11 +937,7 @@ void ScriptEditor::_menu_option(int p_option) {
}
}
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return;
-
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
+ ScriptEditorBase *current = _get_current_editor();
if (current) {
switch (p_option) {
@@ -1033,7 +1027,7 @@ void ScriptEditor::_menu_option(int p_option) {
_copy_script_path();
} break;
case SHOW_IN_FILE_SYSTEM: {
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(tab_container->get_current_tab()));
+ ScriptEditorBase *se = _get_current_editor();
Ref<Script> script = se->get_edited_script();
FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock();
file_system_dock->navigate_to_path(script->get_path());
@@ -1223,6 +1217,17 @@ void ScriptEditor::_notification(int p_what) {
recent_scripts->set_as_minsize();
} break;
+ case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: {
+
+ if (is_visible()) {
+ find_in_files_button->show();
+ } else {
+ find_in_files->hide();
+ find_in_files_button->hide();
+ }
+
+ } break;
+
default:
break;
}
@@ -1230,15 +1235,11 @@ void ScriptEditor::_notification(int p_what) {
bool ScriptEditor::can_take_away_focus() const {
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return true;
-
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
- if (!current)
+ ScriptEditorBase *current = _get_current_editor();
+ if (current)
+ return current->can_lose_focus_on_node_selection();
+ else
return true;
-
- return current->can_lose_focus_on_node_selection();
}
void ScriptEditor::close_builtin_scripts_from_scene(const String &p_scene) {
@@ -1315,20 +1316,13 @@ void ScriptEditor::ensure_focus_current() {
if (!is_inside_tree())
return;
- int cidx = tab_container->get_current_tab();
- if (cidx < 0 || cidx >= tab_container->get_tab_count())
- return;
-
- Control *c = Object::cast_to<Control>(tab_container->get_child(cidx));
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(c);
- if (!se)
- return;
- se->ensure_focus();
+ ScriptEditorBase *current = _get_current_editor();
+ if (current)
+ current->ensure_focus();
}
void ScriptEditor::_members_overview_selected(int p_idx) {
- Node *current = tab_container->get_child(tab_container->get_current_tab());
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current);
+ ScriptEditorBase *se = _get_current_editor();
if (!se) {
return;
}
@@ -1362,18 +1356,12 @@ void ScriptEditor::ensure_select_current() {
if (tab_container->get_child_count() && tab_container->get_current_tab() >= 0) {
- Node *current = tab_container->get_child(tab_container->get_current_tab());
-
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current);
+ ScriptEditorBase *se = _get_current_editor();
if (se) {
- Ref<Script> script = se->get_edited_script();
-
if (!grab_focus_block && is_visible_in_tree())
se->ensure_focus();
}
-
- EditorHelp *eh = Object::cast_to<EditorHelp>(current);
}
_update_selected_editor_menu();
@@ -1413,12 +1401,7 @@ struct _ScriptEditorItemData {
void ScriptEditor::_update_members_overview_visibility() {
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return;
-
- Node *current = tab_container->get_child(tab_container->get_current_tab());
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current);
+ ScriptEditorBase *se = _get_current_editor();
if (!se) {
members_overview->set_visible(false);
return;
@@ -1434,12 +1417,7 @@ void ScriptEditor::_update_members_overview_visibility() {
void ScriptEditor::_update_members_overview() {
members_overview->clear();
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return;
-
- Node *current = tab_container->get_child(tab_container->get_current_tab());
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current);
+ ScriptEditorBase *se = _get_current_editor();
if (!se) {
return;
}
@@ -1778,6 +1756,20 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool
}
ERR_FAIL_COND_V(!se, false);
+ bool highlighter_set = false;
+ for (int i = 0; i < syntax_highlighters_func_count; i++) {
+ SyntaxHighlighter *highlighter = syntax_highlighters_funcs[i]();
+ se->add_syntax_highlighter(highlighter);
+
+ if (!highlighter_set) {
+ List<String> languages = highlighter->get_supported_languages();
+ if (languages.find(p_script->get_language()->get_name())) {
+ se->set_syntax_highlighter(highlighter);
+ highlighter_set = true;
+ }
+ }
+ }
+
tab_container->add_child(se);
se->set_edited_script(p_script);
se->set_tooltip_request_func("_get_debug_tooltip", this);
@@ -1799,6 +1791,7 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool
se->connect("request_open_script_at_line", this, "_goto_script_line");
se->connect("go_to_help", this, "_help_class_goto");
se->connect("request_save_history", this, "_save_history");
+ se->connect("search_in_files_requested", this, "_on_find_in_files_requested");
//test for modification, maybe the script was not edited but was loaded
@@ -2494,6 +2487,14 @@ void ScriptEditor::_open_script_request(const String &p_path) {
}
}
+int ScriptEditor::syntax_highlighters_func_count = 0;
+CreateSyntaxHighlighterFunc ScriptEditor::syntax_highlighters_funcs[ScriptEditor::SYNTAX_HIGHLIGHTER_FUNC_MAX];
+
+void ScriptEditor::register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func) {
+ ERR_FAIL_COND(syntax_highlighters_func_count == SYNTAX_HIGHLIGHTER_FUNC_MAX);
+ syntax_highlighters_funcs[syntax_highlighters_func_count++] = p_func;
+}
+
int ScriptEditor::script_editor_func_count = 0;
CreateScriptEditorFunc ScriptEditor::script_editor_funcs[ScriptEditor::SCRIPT_EDITOR_FUNC_MAX];
@@ -2508,6 +2509,48 @@ void ScriptEditor::_script_changed() {
NodeDock::singleton->update_lists();
}
+void ScriptEditor::_on_find_in_files_requested(String text) {
+
+ find_in_files_dialog->set_search_text(text);
+ find_in_files_dialog->popup_centered_minsize();
+}
+
+void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_number, int begin, int end) {
+
+ Ref<Resource> res = ResourceLoader::load(fpath);
+ edit(res);
+
+ ScriptEditorBase *seb = _get_current_editor();
+
+ ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(seb);
+ if (ste) {
+ ste->goto_line_selection(line_number - 1, begin, end);
+ }
+}
+
+void ScriptEditor::_start_find_in_files(bool with_replace) {
+
+ FindInFiles *f = find_in_files->get_finder();
+
+ f->set_search_text(find_in_files_dialog->get_search_text());
+ f->set_match_case(find_in_files_dialog->is_match_case());
+ f->set_whole_words(find_in_files_dialog->is_match_case());
+ f->set_folder(find_in_files_dialog->get_folder());
+ f->set_filter(find_in_files_dialog->get_filter());
+
+ find_in_files->set_with_replace(with_replace);
+ find_in_files->start_search();
+
+ find_in_files_button->set_pressed(true);
+ find_in_files->show();
+}
+
+void ScriptEditor::_on_find_in_files_modified_files(PoolStringArray paths) {
+
+ _test_script_times_on_disk();
+ _update_modified_scripts_for_external_editor();
+}
+
void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_file_dialog_action", &ScriptEditor::_file_dialog_action);
@@ -2555,6 +2598,10 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_script_list_gui_input", &ScriptEditor::_script_list_gui_input);
ClassDB::bind_method("_script_changed", &ScriptEditor::_script_changed);
ClassDB::bind_method("_update_recent_scripts", &ScriptEditor::_update_recent_scripts);
+ ClassDB::bind_method("_on_find_in_files_requested", &ScriptEditor::_on_find_in_files_requested);
+ ClassDB::bind_method("_start_find_in_files", &ScriptEditor::_start_find_in_files);
+ ClassDB::bind_method("_on_find_in_files_result_selected", &ScriptEditor::_on_find_in_files_result_selected);
+ ClassDB::bind_method("_on_find_in_files_modified_files", &ScriptEditor::_on_find_in_files_modified_files);
ClassDB::bind_method(D_METHOD("get_drag_data_fw", "point", "from"), &ScriptEditor::get_drag_data_fw);
ClassDB::bind_method(D_METHOD("can_drop_data_fw", "point", "data", "from"), &ScriptEditor::can_drop_data_fw);
@@ -2816,6 +2863,19 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
add_child(help_index);
help_index->connect("open_class", this, "_help_class_open");
+ find_in_files_dialog = memnew(FindInFilesDialog);
+ find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_FIND_REQUESTED, this, "_start_find_in_files", varray(false));
+ find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_REPLACE_REQUESTED, this, "_start_find_in_files", varray(true));
+ add_child(find_in_files_dialog);
+ find_in_files = memnew(FindInFilesPanel);
+ find_in_files_button = editor->add_bottom_panel_item(TTR("Search results"), find_in_files);
+ find_in_files_button->set_tooltip(TTR("Search in files"));
+ find_in_files->set_custom_minimum_size(Size2(0, 200));
+ find_in_files->connect(FindInFilesPanel::SIGNAL_RESULT_SELECTED, this, "_on_find_in_files_result_selected");
+ find_in_files->connect(FindInFilesPanel::SIGNAL_FILES_MODIFIED, this, "_on_find_in_files_modified_files");
+ find_in_files->hide();
+ find_in_files_button->hide();
+
history_pos = -1;
//debugger_gui->hide();
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index bcc604d990..9f37b18d7d 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -80,6 +80,9 @@ protected:
static void _bind_methods();
public:
+ virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter) = 0;
+ virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter) = 0;
+
virtual void apply_code() = 0;
virtual Ref<Script> get_edited_script() const = 0;
virtual Vector<String> get_functions() = 0;
@@ -112,9 +115,12 @@ public:
ScriptEditorBase() {}
};
+typedef SyntaxHighlighter *(*CreateSyntaxHighlighterFunc)();
typedef ScriptEditorBase *(*CreateScriptEditorFunc)(const Ref<Script> &p_script);
class EditorScriptCodeCompletionCache;
+class FindInFilesDialog;
+class FindInFilesPanel;
class ScriptEditor : public PanelContainer {
@@ -213,13 +219,21 @@ class ScriptEditor : public PanelContainer {
ToolButton *script_back;
ToolButton *script_forward;
+ FindInFilesDialog *find_in_files_dialog;
+ FindInFilesPanel *find_in_files;
+ Button *find_in_files_button;
+
enum {
- SCRIPT_EDITOR_FUNC_MAX = 32
+ SCRIPT_EDITOR_FUNC_MAX = 32,
+ SYNTAX_HIGHLIGHTER_FUNC_MAX = 32
};
static int script_editor_func_count;
static CreateScriptEditorFunc script_editor_funcs[SCRIPT_EDITOR_FUNC_MAX];
+ static int syntax_highlighters_func_count;
+ static CreateSyntaxHighlighterFunc syntax_highlighters_funcs[SYNTAX_HIGHLIGHTER_FUNC_MAX];
+
struct ScriptHistory {
Control *control;
@@ -296,6 +310,8 @@ class ScriptEditor : public PanelContainer {
void _update_window_menu();
void _script_created(Ref<Script> p_script);
+ ScriptEditorBase *_get_current_editor() const;
+
void _save_layout();
void _editor_settings_changed();
void _autosave_scripts();
@@ -351,6 +367,11 @@ class ScriptEditor : public PanelContainer {
Ref<Script> _get_current_script();
Array _get_open_scripts() const;
+ void _on_find_in_files_requested(String text);
+ void _on_find_in_files_result_selected(String fpath, int line_number, int begin, int end);
+ void _start_find_in_files(bool with_replace);
+ void _on_find_in_files_modified_files(PoolStringArray paths);
+
static void _open_script_request(const String &p_path);
static ScriptEditor *script_editor;
@@ -399,7 +420,9 @@ public:
ScriptEditorDebugger *get_debugger() { return debugger; }
void set_live_auto_reload_running_scripts(bool p_enabled);
+ static void register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func);
static void register_create_script_editor_function(CreateScriptEditorFunc p_func);
+
ScriptEditor(EditorNode *p_editor);
~ScriptEditor();
};
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index c8ea2f79fe..bcc575a7ac 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -529,6 +529,14 @@ void ScriptTextEditor::goto_line(int p_line, bool p_with_error) {
tx->call_deferred("cursor_set_line", p_line);
}
+void ScriptTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
+ TextEdit *tx = code_editor->get_text_edit();
+ tx->unfold_line(p_line);
+ tx->call_deferred("cursor_set_line", p_line);
+ tx->call_deferred("cursor_set_column", p_begin);
+ tx->select(p_line, p_begin, p_line, p_end);
+}
+
void ScriptTextEditor::ensure_focus() {
code_editor->get_text_edit()->grab_focus();
@@ -573,6 +581,7 @@ void ScriptTextEditor::set_edited_script(const Ref<Script> &p_script) {
ERR_FAIL_COND(!script.is_null());
script = p_script;
+ _set_theme_for_script();
code_editor->get_text_edit()->set_text(script->get_source_code());
code_editor->get_text_edit()->clear_undo_history();
@@ -580,8 +589,6 @@ void ScriptTextEditor::set_edited_script(const Ref<Script> &p_script) {
emit_signal("name_changed");
code_editor->update_line_and_column();
-
- _set_theme_for_script();
}
void ScriptTextEditor::_validate_script() {
@@ -789,6 +796,26 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
emit_signal("go_to_help", "class_method:" + result.class_name + ":" + result.class_member);
} break;
+ case ScriptLanguage::LookupResult::RESULT_CLASS_ENUM: {
+
+ StringName cname = result.class_name;
+ StringName success;
+ while (true) {
+ success = ClassDB::get_integer_constant_enum(cname, result.class_member, true);
+ if (success != StringName()) {
+ result.class_name = cname;
+ cname = ClassDB::get_parent_class(cname);
+ } else {
+ break;
+ }
+ }
+
+ emit_signal("go_to_help", "class_enum:" + result.class_name + ":" + result.class_member);
+
+ } break;
+ case ScriptLanguage::LookupResult::RESULT_CLASS_TBD_GLOBALSCOPE: {
+ emit_signal("go_to_help", "class_global:" + result.class_name + ":" + result.class_member);
+ } break;
}
}
}
@@ -1154,6 +1181,15 @@ void ScriptTextEditor::_edit_option(int p_op) {
code_editor->get_find_replace_bar()->popup_replace();
} break;
+ case SEARCH_IN_FILES: {
+
+ String selected_text = code_editor->get_text_edit()->get_selection_text();
+
+ // Yep, because it doesn't make sense to instance this dialog for every single script open...
+ // So this will be delegated to the ScriptEditor
+ emit_signal("search_in_files_requested", selected_text);
+
+ } break;
case SEARCH_LOCATE_FUNCTION: {
quick_open->popup(get_functions());
@@ -1245,11 +1281,26 @@ void ScriptTextEditor::_edit_option(int p_op) {
}
}
+void ScriptTextEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
+ highlighters[p_highlighter->get_name()] = p_highlighter;
+ highlighter_menu->get_popup()->add_item(p_highlighter->get_name());
+}
+
+void ScriptTextEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
+ TextEdit *te = code_editor->get_text_edit();
+ te->_set_syntax_highlighting(p_highlighter);
+}
+
+void ScriptTextEditor::_change_syntax_highlighter(int p_idx) {
+ set_syntax_highlighter(highlighters[highlighter_menu->get_popup()->get_item_text(p_idx)]);
+}
+
void ScriptTextEditor::_bind_methods() {
ClassDB::bind_method("_validate_script", &ScriptTextEditor::_validate_script);
ClassDB::bind_method("_load_theme_settings", &ScriptTextEditor::_load_theme_settings);
ClassDB::bind_method("_breakpoint_toggled", &ScriptTextEditor::_breakpoint_toggled);
+ ClassDB::bind_method("_change_syntax_highlighter", &ScriptTextEditor::_change_syntax_highlighter);
ClassDB::bind_method("_edit_option", &ScriptTextEditor::_edit_option);
ClassDB::bind_method("_goto_line", &ScriptTextEditor::_goto_line);
ClassDB::bind_method("_lookup_symbol", &ScriptTextEditor::_lookup_symbol);
@@ -1626,6 +1677,8 @@ ScriptTextEditor::ScriptTextEditor() {
search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV);
search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE);
search_menu->get_popup()->add_separator();
+ search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_in_files"), SEARCH_IN_FILES);
+ search_menu->get_popup()->add_separator();
search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_function"), SEARCH_LOCATE_FUNCTION);
search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE);
search_menu->get_popup()->add_separator();
@@ -1635,6 +1688,14 @@ ScriptTextEditor::ScriptTextEditor() {
edit_hb->add_child(edit_menu);
+ highlighters["Standard"] = NULL;
+
+ highlighter_menu = memnew(MenuButton);
+ highlighter_menu->set_text(TTR("Syntax Highlighter"));
+ highlighter_menu->get_popup()->add_item("Standard");
+ highlighter_menu->get_popup()->connect("id_pressed", this, "_change_syntax_highlighter");
+ edit_hb->add_child(highlighter_menu);
+
quick_open = memnew(ScriptEditorQuickOpen);
add_child(quick_open);
quick_open->connect("goto_line", this, "_goto_line");
@@ -1697,7 +1758,9 @@ void ScriptTextEditor::register_editor() {
ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3);
ED_SHORTCUT("script_text_editor/replace", TTR("Replace.."), KEY_MASK_CMD | KEY_R);
- ED_SHORTCUT("script_text_editor/goto_function", TTR("Goto Function.."), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_F);
+ ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F);
+
+ ED_SHORTCUT("script_text_editor/goto_function", TTR("Goto Function.."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F);
ED_SHORTCUT("script_text_editor/goto_line", TTR("Goto Line.."), KEY_MASK_CMD | KEY_L);
ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_SHIFT | KEY_F1);
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index 22e8fbce25..a93e1a6fa8 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -49,6 +49,7 @@ class ScriptTextEditor : public ScriptEditorBase {
HBoxContainer *edit_hb;
MenuButton *edit_menu;
+ MenuButton *highlighter_menu;
MenuButton *search_menu;
PopupMenu *context_menu;
@@ -105,6 +106,7 @@ class ScriptTextEditor : public ScriptEditorBase {
SEARCH_REPLACE,
SEARCH_LOCATE_FUNCTION,
SEARCH_GOTO_LINE,
+ SEARCH_IN_FILES,
DEBUG_TOGGLE_BREAKPOINT,
DEBUG_REMOVE_ALL_BREAKPOINTS,
DEBUG_GOTO_NEXT_BREAKPOINT,
@@ -125,6 +127,9 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+ Map<String, SyntaxHighlighter *> highlighters;
+ void _change_syntax_highlighter(int p_idx);
+
void _edit_option(int p_op);
void _make_context_menu(bool p_selection, bool p_color, bool p_can_fold, bool p_is_folded);
void _text_edit_gui_input(const Ref<InputEvent> &ev);
@@ -145,6 +150,9 @@ protected:
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
public:
+ virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter);
+ virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter);
+
virtual void apply_code();
virtual Ref<Script> get_edited_script() const;
virtual Vector<String> get_functions();
@@ -163,6 +171,7 @@ public:
virtual void tag_saved_version();
virtual void goto_line(int p_line, bool p_with_error = false);
+ void goto_line_selection(int p_line, int p_begin, int p_end);
virtual void reload(bool p_soft);
virtual void get_breakpoints(List<int> *p_breakpoints);
diff --git a/editor/plugins/shader_graph_editor_plugin.cpp b/editor/plugins/shader_graph_editor_plugin.cpp
index 59085c203f..e1d28cc215 100644
--- a/editor/plugins/shader_graph_editor_plugin.cpp
+++ b/editor/plugins/shader_graph_editor_plugin.cpp
@@ -2769,8 +2769,6 @@ void ShaderGraphEditor::_popup_requested(const Vector2 &p_position)
popup->set_global_position(p_position);
popup->set_size( Size2( 200, 0) );
popup->popup();
- popup->call_deferred("grab_click_focus");
- popup->set_invalidate_click_until_motion();
}
void ShaderGraphEditor::_notification(int p_what) {
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index e484140527..9d7c582e0e 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -886,8 +886,6 @@ void SpatialEditorViewport::_list_select(Ref<InputEventMouseButton> b) {
selection_menu->set_global_position(b->get_global_position());
selection_menu->popup();
- selection_menu->call_deferred("grab_click_focus");
- selection_menu->set_invalidate_click_until_motion();
}
}
@@ -3353,14 +3351,14 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/front_view"), VIEW_FRONT);
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/rear_view"), VIEW_REAR);
view_menu->get_popup()->add_separator();
- view_menu->get_popup()->add_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE);
- view_menu->get_popup()->add_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL);
+ view_menu->get_popup()->add_radio_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE);
+ view_menu->get_popup()->add_radio_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true);
view_menu->get_popup()->add_separator();
- view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_normal", TTR("Display Normal")), VIEW_DISPLAY_NORMAL);
- view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME);
- view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW);
- view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS);
+ view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_normal", TTR("Display Normal")), VIEW_DISPLAY_NORMAL);
+ view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME);
+ view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW);
+ view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), true);
view_menu->get_popup()->add_separator();
view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_environment", TTR("View Environment")), VIEW_ENVIRONMENT);
@@ -5111,12 +5109,12 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
accept = memnew(AcceptDialog);
editor->get_gui_base()->add_child(accept);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"), KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"), KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS);
p->add_separator();
p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN);
diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp
index 4367fe8976..5ba3931689 100644
--- a/editor/plugins/texture_region_editor_plugin.cpp
+++ b/editor/plugins/texture_region_editor_plugin.cpp
@@ -805,12 +805,10 @@ TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) {
snap_mode_button->set_text(TTR("<None>"));
PopupMenu *p = snap_mode_button->get_popup();
p->set_hide_on_checkable_item_selection(false);
- p->add_item(TTR("<None>"), 0);
- p->add_item(TTR("Pixel Snap"), 1);
- p->add_item(TTR("Grid Snap"), 2);
- p->add_item(TTR("Auto Slice"), 3);
- for (int i = 0; i < 4; i++)
- p->set_item_as_checkable(i, true);
+ p->add_radio_check_item(TTR("<None>"), 0);
+ p->add_radio_check_item(TTR("Pixel Snap"), 1);
+ p->add_radio_check_item(TTR("Grid Snap"), 2);
+ p->add_radio_check_item(TTR("Auto Slice"), 3);
p->set_item_checked(0, true);
p->connect("id_pressed", this, "_set_snap_mode");
hb_grid = memnew(HBoxContainer);
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 295d9439ad..550dfb3ae1 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -692,6 +692,10 @@ ThemeEditor::ThemeEditor() {
test_menu_button->get_popup()->add_check_item(TTR("Check Item"));
test_menu_button->get_popup()->add_check_item(TTR("Checked Item"));
test_menu_button->get_popup()->set_item_checked(2, true);
+ test_menu_button->get_popup()->add_separator();
+ test_menu_button->get_popup()->add_check_item(TTR("Radio Item"));
+ test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item"));
+ test_menu_button->get_popup()->set_item_checked(5, true);
first_vb->add_child(test_menu_button);
OptionButton *test_option_button = memnew(OptionButton);
diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp
index c5c7272ed2..14c584fa35 100644
--- a/editor/plugins/tile_map_editor_plugin.cpp
+++ b/editor/plugins/tile_map_editor_plugin.cpp
@@ -78,6 +78,7 @@ void TileMapEditor::_notification(int p_what) {
p->set_item_icon(p->get_item_index(OPTION_PAINTING), get_icon("Edit", "EditorIcons"));
p->set_item_icon(p->get_item_index(OPTION_PICK_TILE), get_icon("ColorPick", "EditorIcons"));
p->set_item_icon(p->get_item_index(OPTION_SELECT), get_icon("ToolSelect", "EditorIcons"));
+ p->set_item_icon(p->get_item_index(OPTION_MOVE), get_icon("ToolMove", "EditorIcons"));
p->set_item_icon(p->get_item_index(OPTION_DUPLICATE), get_icon("Duplicate", "EditorIcons"));
p->set_item_icon(p->get_item_index(OPTION_ERASE_SELECTION), get_icon("Remove", "EditorIcons"));
@@ -156,6 +157,14 @@ void TileMapEditor::_menu_option(int p_option) {
undo_redo->commit_action();
} break;
+ case OPTION_MOVE: {
+
+ if (selection_active) {
+ _update_copydata();
+ tool = TOOL_MOVING;
+ canvas_item_editor->update();
+ }
+ } break;
}
}
@@ -829,6 +838,29 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
copydata.clear();
canvas_item_editor->update();
+ } else if (tool == TOOL_MOVING) {
+
+ Point2 ofs = over_tile - rectangle.position;
+
+ undo_redo->create_action(TTR("Move"));
+ undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
+ for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
+ for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
+
+ _set_cell(Point2i(j, i), TileMap::INVALID_CELL, false, false, false);
+ }
+ }
+ for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) {
+
+ _set_cell(E->get().pos + ofs, E->get().cell, E->get().flip_h, E->get().flip_v, E->get().transpose);
+ }
+ undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
+ undo_redo->commit_action();
+
+ copydata.clear();
+ selection_active = false;
+
+ canvas_item_editor->update();
} else if (tool == TOOL_SELECTING) {
@@ -888,6 +920,16 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
return true;
}
+ if (tool == TOOL_MOVING) {
+
+ tool = TOOL_NONE;
+ copydata.clear();
+
+ canvas_item_editor->update();
+
+ return true;
+ }
+
if (tool == TOOL_NONE) {
paint_undo.clear();
@@ -1095,7 +1137,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (k->get_scancode() == KEY_ESCAPE) {
- if (tool == TOOL_DUPLICATING)
+ if (tool == TOOL_DUPLICATING || tool == TOOL_MOVING)
copydata.clear();
else if (tool == TOOL_SELECTING || selection_active)
selection_active = false;
@@ -1150,6 +1192,14 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
return true;
}
}
+ if (ED_IS_SHORTCUT("tile_map_editor/move_selection", p_event)) {
+ if (selection_active) {
+ _update_copydata();
+ tool = TOOL_MOVING;
+ canvas_item_editor->update();
+ return true;
+ }
+ }
if (ED_IS_SHORTCUT("tile_map_editor/find_tile", p_event)) {
search_box->select_all();
search_box->grab_focus();
@@ -1159,18 +1209,21 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (ED_IS_SHORTCUT("tile_map_editor/mirror_x", p_event)) {
flip_h = !flip_h;
mirror_x->set_pressed(flip_h);
+ _update_transform_buttons();
canvas_item_editor->update();
return true;
}
if (ED_IS_SHORTCUT("tile_map_editor/mirror_y", p_event)) {
flip_v = !flip_v;
mirror_y->set_pressed(flip_v);
+ _update_transform_buttons();
canvas_item_editor->update();
return true;
}
if (ED_IS_SHORTCUT("tile_map_editor/transpose", p_event)) {
transpose = !transpose;
transp->set_pressed(transpose);
+ _update_transform_buttons();
canvas_item_editor->update();
return true;
}
@@ -1343,7 +1396,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) {
_draw_cell(id, Point2i(j, i), flip_h, flip_v, transpose, xform);
}
}
- } else if (tool == TOOL_DUPLICATING) {
+ } else if (tool == TOOL_DUPLICATING || tool == TOOL_MOVING) {
if (copydata.empty())
return;
@@ -1590,6 +1643,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
p->add_item(TTR("Pick Tile"), OPTION_PICK_TILE, KEY_CONTROL);
p->add_separator();
p->add_shortcut(ED_SHORTCUT("tile_map_editor/select", TTR("Select"), KEY_MASK_CMD + KEY_B), OPTION_SELECT);
+ p->add_shortcut(ED_SHORTCUT("tile_map_editor/move_selection", TTR("Move Selection"), KEY_MASK_CMD + KEY_M), OPTION_MOVE);
p->add_shortcut(ED_SHORTCUT("tile_map_editor/duplicate_selection", TTR("Duplicate Selection"), KEY_MASK_CMD + KEY_D), OPTION_DUPLICATE);
p->add_shortcut(ED_GET_SHORTCUT("tile_map_editor/erase_selection"), OPTION_ERASE_SELECTION);
p->add_separator();
diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h
index 2d582d030b..3257901c88 100644
--- a/editor/plugins/tile_map_editor_plugin.h
+++ b/editor/plugins/tile_map_editor_plugin.h
@@ -61,6 +61,7 @@ class TileMapEditor : public VBoxContainer {
TOOL_BUCKET,
TOOL_PICKING,
TOOL_DUPLICATING,
+ TOOL_MOVING
};
enum Options {
@@ -72,6 +73,7 @@ class TileMapEditor : public VBoxContainer {
OPTION_ERASE_SELECTION,
OPTION_PAINTING,
OPTION_FIX_INVALID,
+ OPTION_MOVE
};
TileMap *node;
diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp
index 2311439728..41692e805f 100644
--- a/editor/plugins/tile_set_editor_plugin.cpp
+++ b/editor/plugins/tile_set_editor_plugin.cpp
@@ -278,10 +278,11 @@ void TileSetEditor::_changed_callback(Object *p_changed, const char *p_prop) {
preview->set_region_rect(tileset->tile_get_region(get_current_tile()));
} else if (p_prop == StringName("name")) {
update_tile_list_icon();
- } else if (p_prop == StringName("texture") || p_prop == StringName("tile_mode")) {
+ } else if (p_prop == StringName("texture") || p_prop == StringName("modulate") || p_prop == StringName("tile_mode")) {
_on_tile_list_selected(get_current_tile());
workspace->update();
preview->set_texture(tileset->tile_get_texture(get_current_tile()));
+ preview->set_modulate(tileset->tile_get_modulate(get_current_tile()));
preview->set_region_rect(tileset->tile_get_region(get_current_tile()));
if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE)
property_editor->show();
@@ -578,6 +579,7 @@ void TileSetEditor::_on_tile_list_selected(int p_index) {
if (get_current_tile() >= 0) {
current_item_index = p_index;
preview->set_texture(tileset->tile_get_texture(get_current_tile()));
+ preview->set_modulate(tileset->tile_get_modulate(get_current_tile()));
preview->set_region_rect(tileset->tile_get_region(get_current_tile()));
workspace->set_custom_minimum_size(tileset->tile_get_region(get_current_tile()).size);
update_workspace_tile_mode();
@@ -1736,6 +1738,7 @@ void TileSetEditor::update_tile_list() {
region.position += pos;
}
tile_list->set_item_icon_region(tile_list->get_item_count() - 1, region);
+ tile_list->set_item_icon_modulate(tile_list->get_item_count() - 1, tileset->tile_get_modulate(E->get()));
}
if (tile_list->get_item_count() > 0 && selected_tile < tile_list->get_item_count()) {
tile_list->select(selected_tile);
@@ -1763,6 +1766,7 @@ void TileSetEditor::update_tile_list_icon() {
tile_list->set_item_metadata(current_idx, E->get());
tile_list->set_item_icon(current_idx, tileset->tile_get_texture(E->get()));
tile_list->set_item_icon_region(current_idx, region);
+ tile_list->set_item_icon_modulate(current_idx, tileset->tile_get_modulate(E->get()));
tile_list->set_item_text(current_idx, tileset->tile_get_name(E->get()));
current_idx += 1;
}
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index 75523cd843..6e3be343ba 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -31,6 +31,7 @@
#include "project_settings_editor.h"
#include "core/global_constants.h"
+#include "core/input_map.h"
#include "core/os/keyboard.h"
#include "core/project_settings.h"
#include "core/translation.h"
@@ -212,7 +213,7 @@ void ProjectSettingsEditor::_device_input_add() {
Ref<InputEventMouseButton> mb;
mb.instance();
mb->set_button_index(device_index->get_selected() + 1);
- mb->set_device(device_id->get_value());
+ mb->set_device(_get_current_device());
for (int i = 0; i < arr.size(); i++) {
@@ -233,7 +234,7 @@ void ProjectSettingsEditor::_device_input_add() {
jm.instance();
jm->set_axis(device_index->get_selected() >> 1);
jm->set_axis_value(device_index->get_selected() & 1 ? 1 : -1);
- jm->set_device(device_id->get_value());
+ jm->set_device(_get_current_device());
for (int i = 0; i < arr.size(); i++) {
@@ -254,7 +255,7 @@ void ProjectSettingsEditor::_device_input_add() {
jb.instance();
jb->set_button_index(device_index->get_selected());
- jb->set_device(device_id->get_value());
+ jb->set_device(_get_current_device());
for (int i = 0; i < arr.size(); i++) {
@@ -289,6 +290,20 @@ void ProjectSettingsEditor::_device_input_add() {
_show_last_added(ie, name);
}
+void ProjectSettingsEditor::_set_current_device(int i_device) {
+ device_id->select(i_device + 1);
+}
+
+int ProjectSettingsEditor::_get_current_device() {
+ return device_id->get_selected() - 1;
+}
+
+String ProjectSettingsEditor::_get_device_string(int i_device) {
+ if (i_device == InputMap::ALL_DEVICES)
+ return TTR("All Devices");
+ return TTR("Device") + " " + itos(i_device);
+}
+
void ProjectSettingsEditor::_press_a_key_confirm() {
if (last_wait_for_key.is_null())
@@ -422,10 +437,10 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even
Ref<InputEventMouseButton> mb = p_exiting_event;
if (mb.is_valid()) {
device_index->select(mb->get_button_index() - 1);
- device_id->set_value(mb->get_device());
+ _set_current_device(mb->get_device());
device_input->get_ok()->set_text(TTR("Change"));
} else {
- device_id->set_value(0);
+ _set_current_device(0);
device_input->get_ok()->set_text(TTR("Add"));
}
} break;
@@ -443,10 +458,10 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even
Ref<InputEventJoypadMotion> jm = p_exiting_event;
if (jm.is_valid()) {
device_index->select(jm->get_axis() * 2 + (jm->get_axis_value() > 0 ? 1 : 0));
- device_id->set_value(jm->get_device());
+ _set_current_device(jm->get_device());
device_input->get_ok()->set_text(TTR("Change"));
} else {
- device_id->set_value(0);
+ _set_current_device(0);
device_input->get_ok()->set_text(TTR("Add"));
}
} break;
@@ -464,10 +479,10 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even
Ref<InputEventJoypadButton> jb = p_exiting_event;
if (jb.is_valid()) {
device_index->select(jb->get_button_index());
- device_id->set_value(jb->get_device());
+ _set_current_device(jb->get_device());
device_input->get_ok()->set_text(TTR("Change"));
} else {
- device_id->set_value(0);
+ _set_current_device(0);
device_input->get_ok()->set_text(TTR("Add"));
}
@@ -676,7 +691,7 @@ void ProjectSettingsEditor::_update_actions() {
if (jb.is_valid()) {
- String str = TTR("Device") + " " + itos(jb->get_device()) + ", " + TTR("Button") + " " + itos(jb->get_button_index());
+ String str = _get_device_string(jb->get_device()) + ", " + TTR("Button") + " " + itos(jb->get_button_index());
if (jb->get_button_index() >= 0 && jb->get_button_index() < JOY_BUTTON_MAX)
str += String() + " (" + _button_names[jb->get_button_index()] + ").";
else
@@ -689,7 +704,7 @@ void ProjectSettingsEditor::_update_actions() {
Ref<InputEventMouseButton> mb = ie;
if (mb.is_valid()) {
- String str = TTR("Device") + " " + itos(mb->get_device()) + ", ";
+ String str = _get_device_string(mb->get_device()) + ", ";
switch (mb->get_button_index()) {
case BUTTON_LEFT: str += TTR("Left Button."); break;
case BUTTON_RIGHT: str += TTR("Right Button."); break;
@@ -710,7 +725,7 @@ void ProjectSettingsEditor::_update_actions() {
int ax = jm->get_axis();
int n = 2 * ax + (jm->get_axis_value() < 0 ? 0 : 1);
String desc = _axis_names[n];
- String str = TTR("Device") + " " + itos(jm->get_device()) + ", " + TTR("Axis") + " " + itos(ax) + " " + (jm->get_axis_value() < 0 ? "-" : "+") + desc + ".";
+ String str = _get_device_string(jm->get_device()) + ", " + TTR("Axis") + " " + itos(ax) + " " + (jm->get_axis_value() < 0 ? "-" : "+") + desc + ".";
action->set_text(0, str);
action->set_icon(0, get_icon("JoyAxis", "EditorIcons"));
}
@@ -1781,8 +1796,10 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
l->set_text(TTR("Device:"));
vbc_left->add_child(l);
- device_id = memnew(SpinBox);
- device_id->set_value(0);
+ device_id = memnew(OptionButton);
+ for (int i = -1; i < 8; i++)
+ device_id->add_item(_get_device_string(i));
+ _set_current_device(0);
vbc_left->add_child(device_id);
VBoxContainer *vbc_right = memnew(VBoxContainer);
diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h
index 9364b0ff2c..0ced88d7f6 100644
--- a/editor/project_settings_editor.h
+++ b/editor/project_settings_editor.h
@@ -80,7 +80,7 @@ class ProjectSettingsEditor : public AcceptDialog {
ConfirmationDialog *press_a_key;
Label *press_a_key_label;
ConfirmationDialog *device_input;
- SpinBox *device_id;
+ OptionButton *device_id;
OptionButton *device_index;
Label *device_index_label;
MenuButton *popup_copy_to_feature;
@@ -170,6 +170,10 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+ int _get_current_device();
+ void _set_current_device(int i_device);
+ String _get_device_string(int i_device);
+
public:
void add_translation(const String &p_translation);
static ProjectSettingsEditor *get_singleton() { return singleton; }
diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp
index 043add046e..4bd70d0c29 100644
--- a/editor/property_editor.cpp
+++ b/editor/property_editor.cpp
@@ -285,6 +285,11 @@ void CustomPropertyEditor::_menu_option(int p_which) {
}
Object *obj = ClassDB::instance(intype);
+
+ if (!obj) {
+ obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
+ }
+
ERR_BREAK(!obj);
Resource *res = Object::cast_to<Resource>(obj);
ERR_BREAK(!res);
@@ -877,6 +882,12 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
} else if (hint_text != "") {
int idx = 0;
+ Vector<EditorData::CustomType> custom_resources;
+
+ if (EditorNode::get_editor_data().get_custom_types().has("Resource")) {
+ custom_resources = EditorNode::get_editor_data().get_custom_types()["Resource"];
+ }
+
for (int i = 0; i < hint_text.get_slice_count(","); i++) {
String base = hint_text.get_slice(",", i);
@@ -885,6 +896,11 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
valid_inheritors.insert(base);
List<StringName> inheritors;
ClassDB::get_inheriters_from_class(base.strip_edges(), &inheritors);
+
+ for (int i = 0; i < custom_resources.size(); i++) {
+ inheritors.push_back(custom_resources[i].name);
+ }
+
List<StringName>::Element *E = inheritors.front();
while (E) {
valid_inheritors.insert(E->get());
@@ -893,14 +909,34 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
for (Set<String>::Element *E = valid_inheritors.front(); E; E = E->next()) {
String t = E->get();
- if (!ClassDB::can_instance(t))
+
+ bool is_custom_resource = false;
+ Ref<Texture> icon;
+ if (!custom_resources.empty()) {
+ for (int i = 0; i < custom_resources.size(); i++) {
+ if (custom_resources[i].name == t) {
+ is_custom_resource = true;
+ if (custom_resources[i].icon.is_valid())
+ icon = custom_resources[i].icon;
+ break;
+ }
+ }
+ }
+
+ if (!is_custom_resource && !ClassDB::can_instance(t))
continue;
+
inheritors_array.push_back(t);
int id = TYPE_BASE_ID + idx;
- if (has_icon(t, "EditorIcons")) {
- menu->add_icon_item(get_icon(t, "EditorIcons"), vformat(TTR("New %s"), t), id);
+ if (!icon.is_valid() && has_icon(t, "EditorIcons")) {
+ icon = get_icon(t, "EditorIcons");
+ }
+
+ if (icon.is_valid()) {
+
+ menu->add_icon_item(icon, vformat(TTR("New %s"), t), id);
} else {
menu->add_item(vformat(TTR("New %s"), t), id);
@@ -1094,6 +1130,10 @@ void CustomPropertyEditor::_type_create_selected(int p_idx) {
Object *obj = ClassDB::instance(intype);
+ if (!obj) {
+ obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
+ }
+
ERR_FAIL_COND(!obj);
Resource *res = Object::cast_to<Resource>(obj);
@@ -1291,6 +1331,11 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
if (hint == PROPERTY_HINT_RESOURCE_TYPE) {
Object *obj = ClassDB::instance(intype);
+
+ if (!obj) {
+ obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
+ }
+
ERR_BREAK(!obj);
Resource *res = Object::cast_to<Resource>(obj);
ERR_BREAK(!res);
@@ -3939,7 +3984,7 @@ void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) {
if (_might_be_in_instance() && _get_instanced_node_original_property(prop, vorig)) {
- _edit_set(prop, vorig);
+ _edit_set(prop, vorig.duplicate(true)); // Set, making sure to duplicate arrays properly
return;
}
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index d5ec858c37..b13b238fd7 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -916,9 +916,6 @@ void SceneTreeDock::perform_node_renames(Node *p_base, List<Pair<NodePath, NodeP
if (!r_rem_anims)
r_rem_anims = &rem_anims;
- if (!bool(EDITOR_DEF("editors/animation/autorename_animation_tracks", true)))
- return;
-
if (!p_base) {
p_base = edited_scene;
@@ -927,7 +924,54 @@ void SceneTreeDock::perform_node_renames(Node *p_base, List<Pair<NodePath, NodeP
if (!p_base)
return;
- if (Object::cast_to<AnimationPlayer>(p_base)) {
+ // Renaming node paths used in script instances
+ if (p_base->get_script_instance()) {
+
+ ScriptInstance *si = p_base->get_script_instance();
+
+ if (si) {
+
+ List<PropertyInfo> properties;
+ si->get_property_list(&properties);
+
+ for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
+
+ String propertyname = E->get().name;
+ Variant p = p_base->get(propertyname);
+ if (p.get_type() == Variant::NODE_PATH) {
+
+ // Goes through all paths to check if its matching
+ for (List<Pair<NodePath, NodePath> >::Element *E = p_renames->front(); E; E = E->next()) {
+
+ NodePath root_path = p_base->get_path();
+
+ NodePath rel_path_old = root_path.rel_path_to(E->get().first);
+
+ NodePath rel_path_new = E->get().second;
+
+ // if not empty, get new relative path
+ if (E->get().second != NodePath()) {
+ rel_path_new = root_path.rel_path_to(E->get().second);
+ }
+
+ // if old path detected, then it needs to be replaced with the new one
+ if (p == rel_path_old) {
+
+ editor_data->get_undo_redo().add_do_property(p_base, propertyname, rel_path_new);
+ editor_data->get_undo_redo().add_undo_property(p_base, propertyname, rel_path_old);
+
+ p_base->set(propertyname, rel_path_new);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ bool autorename_animation_tracks = bool(EDITOR_DEF("editors/animation/autorename_animation_tracks", true));
+
+ if (autorename_animation_tracks && Object::cast_to<AnimationPlayer>(p_base)) {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_base);
List<StringName> anims;
@@ -1158,7 +1202,30 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
editor_data->get_undo_redo().add_do_method(new_parent, "move_child", node, p_position_in_parent + inc);
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
+ String old_name = former_names[ni];
String new_name = new_parent->validate_child_name(node);
+
+ // name was modified, fix the path renames
+ if (old_name.casecmp_to(new_name) != 0) {
+
+ // Fix the to name to have the new name
+ NodePath old_new_name = path_renames[ni].second;
+ NodePath new_path;
+
+ Vector<StringName> unfixed_new_names = old_new_name.get_names();
+ Vector<StringName> fixed_new_names;
+
+ // Get last name and replace with fixed new name
+ for (int a = 0; a < (unfixed_new_names.size() - 1); a++) {
+ fixed_new_names.push_back(unfixed_new_names[a]);
+ }
+ fixed_new_names.push_back(new_name);
+
+ NodePath fixed_node_path = NodePath(fixed_new_names, true);
+
+ path_renames[ni].second = fixed_node_path;
+ }
+
editor_data->get_undo_redo().add_do_method(sed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, -1);
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)) + "/" + new_name), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index());
@@ -1794,6 +1861,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
subresources.clear();
menu_subresources->clear();
+ menu_subresources->set_size(Size2(1, 1));
_add_children_to_popup(selection.front()->get(), 0);
if (menu->get_item_count() > 0)
menu->add_separator();
diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp
index d75ef5df8a..36d7a83930 100644
--- a/editor/settings_config_dialog.cpp
+++ b/editor/settings_config_dialog.cpp
@@ -199,6 +199,14 @@ void EditorSettingsDialog::_update_icons() {
void EditorSettingsDialog::_update_shortcuts() {
+ Map<String, bool> collapsed;
+
+ if (shortcuts->get_root() && shortcuts->get_root()->get_children()) {
+ for (TreeItem *item = shortcuts->get_root()->get_children(); item; item = item->get_next()) {
+ collapsed[item->get_text(0)] = item->is_collapsed();
+ }
+ }
+
shortcuts->clear();
List<String> slist;
@@ -223,7 +231,13 @@ void EditorSettingsDialog::_update_shortcuts() {
section = sections[section_name];
} else {
section = shortcuts->create_item(root);
- section->set_text(0, section_name.capitalize());
+
+ String item_name = section_name.capitalize();
+ section->set_text(0, item_name);
+
+ if (collapsed.has(item_name)) {
+ section->set_collapsed(collapsed[item_name]);
+ }
sections[section_name] = section;
section->set_custom_bg_color(0, get_color("prop_subsection", "Editor"));
diff --git a/main/SCsub b/main/SCsub
index 9eddc4f5b6..e2bf03234f 100644
--- a/main/SCsub
+++ b/main/SCsub
@@ -121,7 +121,7 @@ def make_default_controller_mappings(target, source, env):
g.write("\t\"{}\",\n".format(mapping))
g.write("#endif\n")
- g.write("};\n")
+ g.write("\tNULL\n};\n")
g.close()
env.main_sources = []
diff --git a/main/input_default.cpp b/main/input_default.cpp
index c3bc83b2de..1c73ecf2d2 100644
--- a/main/input_default.cpp
+++ b/main/input_default.cpp
@@ -413,10 +413,6 @@ void InputDefault::set_mouse_position(const Point2 &p_posf) {
mouse_speed_track.update(p_posf - mouse_pos);
mouse_pos = p_posf;
- if (custom_cursor.is_valid()) {
- //removed, please insist that we implement hardware cursors
- // VisualServer::get_singleton()->cursor_set_pos(get_mouse_position());
- }
}
Point2 InputDefault::get_mouse_position() const {
@@ -499,15 +495,19 @@ bool InputDefault::is_emulating_touchscreen() const {
return emulate_touch;
}
+Input::CursorShape InputDefault::get_default_cursor_shape() {
+ return default_shape;
+}
+
+void InputDefault::set_default_cursor_shape(CursorShape p_shape) {
+ default_shape = p_shape;
+ OS::get_singleton()->set_cursor_shape((OS::CursorShape)p_shape);
+}
+
void InputDefault::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
if (Engine::get_singleton()->is_editor_hint())
return;
- if (custom_cursor == p_cursor)
- return;
-
- custom_cursor = p_cursor;
-
OS::get_singleton()->set_custom_mouse_cursor(p_cursor, (OS::CursorShape)p_shape, p_hotspot);
}
diff --git a/main/input_default.h b/main/input_default.h
index 0479fdc0ff..384b04cf41 100644
--- a/main/input_default.h
+++ b/main/input_default.h
@@ -115,7 +115,7 @@ class InputDefault : public Input {
SpeedTrack mouse_speed_track;
Map<int, Joypad> joy_names;
int fallback_mapping;
- RES custom_cursor;
+ CursorShape default_shape = CURSOR_ARROW;
public:
enum HatMask {
@@ -226,6 +226,8 @@ public:
void set_emulate_touch(bool p_emulate);
virtual bool is_emulating_touchscreen() const;
+ virtual CursorShape get_default_cursor_shape();
+ virtual void set_default_cursor_shape(CursorShape p_shape);
virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2());
virtual void set_mouse_in_window(bool p_in_window);
diff --git a/main/tests/test_gui.cpp b/main/tests/test_gui.cpp
index b7b6c21692..305b749717 100644
--- a/main/tests/test_gui.cpp
+++ b/main/tests/test_gui.cpp
@@ -185,6 +185,10 @@ public:
popup->add_item("Popup");
popup->add_check_item("Check Popup");
popup->set_item_checked(4, true);
+ popup->add_separator();
+ popup->add_radio_check_item("Option A");
+ popup->set_item_checked(6, true);
+ popup->add_radio_check_item("Option B");
OptionButton *options = memnew(OptionButton);
diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
index ab15e35f63..a9a40c0508 100644
--- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
+++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
@@ -22,14 +22,6 @@
1FF4C1851F584E3F00A41E41 /* GameKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FF4C1841F584E3F00A41E41 /* GameKit.framework */; };
1FF4C1871F584E5600A41E41 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FF4C1861F584E5600A41E41 /* StoreKit.framework */; };
1FF4C1871F584E7600A41E41 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FF4C1881F584E7600A41E41 /* StoreKit.framework */; };
- D07CD43F1C5D573600B7FB28 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4331C5D573600B7FB28 /* Default-568h@2x.png */; };
- D07CD4411C5D573600B7FB28 /* Default-667h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4351C5D573600B7FB28 /* Default-667h@2x.png */; };
- D07CD4421C5D573600B7FB28 /* Default-Portrait-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4361C5D573600B7FB28 /* Default-Portrait-736h@3x.png */; };
- D07CD4441C5D573600B7FB28 /* Default-Landscape-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4381C5D573600B7FB28 /* Default-Landscape-736h@3x.png */; };
- D07CD4451C5D573600B7FB28 /* Default-Landscape@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4391C5D573600B7FB28 /* Default-Landscape@2x.png */; };
- D07CD4461C5D573600B7FB28 /* Default-Landscape-1366h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD43A1C5D573600B7FB28 /* Default-Landscape-1366h@2x.png */; };
- D07CD4471C5D573600B7FB28 /* Default-Portrait@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD43B1C5D573600B7FB28 /* Default-Portrait@2x.png */; };
- D07CD4481C5D573600B7FB28 /* Default-Portrait-1366h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD43C1C5D573600B7FB28 /* Default-Portrait-1366h@2x.png */; };
D07CD44E1C5D589C00B7FB28 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D07CD44D1C5D589C00B7FB28 /* Images.xcassets */; };
D0BCFE3818AEBDA2004A7AAE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCFE3718AEBDA2004A7AAE /* Foundation.framework */; };
D0BCFE3A18AEBDA2004A7AAE /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCFE3918AEBDA2004A7AAE /* CoreGraphics.framework */; };
@@ -57,14 +49,6 @@
1FF4C1881F584E7600A41E41 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
1FF4C1881F584E6300A41E41 /* $binary.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = $binary.entitlements; sourceTree = "<group>"; };
1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = dummy.cpp; sourceTree = "<group>"; };
- D07CD4331C5D573600B7FB28 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
- D07CD4351C5D573600B7FB28 /* Default-667h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h@2x.png"; sourceTree = "<group>"; };
- D07CD4361C5D573600B7FB28 /* Default-Portrait-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait-736h@3x.png"; sourceTree = "<group>"; };
- D07CD4381C5D573600B7FB28 /* Default-Landscape-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape-736h@3x.png"; sourceTree = "<group>"; };
- D07CD4391C5D573600B7FB28 /* Default-Landscape@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape@2x.png"; sourceTree = "<group>"; };
- D07CD43A1C5D573600B7FB28 /* Default-Landscape-1366h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape-1366h@2x.png"; sourceTree = "<group>"; };
- D07CD43B1C5D573600B7FB28 /* Default-Portrait@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait@2x.png"; sourceTree = "<group>"; };
- D07CD43C1C5D573600B7FB28 /* Default-Portrait-1366h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait-1366h@2x.png"; sourceTree = "<group>"; };
D07CD44D1C5D589C00B7FB28 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
D0BCFE3418AEBDA2004A7AAE /* $binary.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = $binary.app; sourceTree = BUILT_PRODUCTS_DIR; };
D0BCFE3718AEBDA2004A7AAE /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
@@ -159,14 +143,6 @@
isa = PBXGroup;
children = (
1FF4C1881F584E6300A41E41 /* $binary.entitlements */,
- D07CD4331C5D573600B7FB28 /* Default-568h@2x.png */,
- D07CD4351C5D573600B7FB28 /* Default-667h@2x.png */,
- D07CD4361C5D573600B7FB28 /* Default-Portrait-736h@3x.png */,
- D07CD4381C5D573600B7FB28 /* Default-Landscape-736h@3x.png */,
- D07CD4391C5D573600B7FB28 /* Default-Landscape@2x.png */,
- D07CD43A1C5D573600B7FB28 /* Default-Landscape-1366h@2x.png */,
- D07CD43B1C5D573600B7FB28 /* Default-Portrait@2x.png */,
- D07CD43C1C5D573600B7FB28 /* Default-Portrait-1366h@2x.png */,
D07CD44D1C5D589C00B7FB28 /* Images.xcassets */,
D0BCFE4218AEBDA2004A7AAE /* Supporting Files */,
1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */,
@@ -254,14 +230,6 @@
1F1575721F582BE20003B888 /* dylibs in Resources */,
D07CD44E1C5D589C00B7FB28 /* Images.xcassets in Resources */,
D0BCFE7818AEBFEB004A7AAE /* $binary.pck in Resources */,
- D07CD4471C5D573600B7FB28 /* Default-Portrait@2x.png in Resources */,
- D07CD4461C5D573600B7FB28 /* Default-Landscape-1366h@2x.png in Resources */,
- D07CD4411C5D573600B7FB28 /* Default-667h@2x.png in Resources */,
- D07CD43F1C5D573600B7FB28 /* Default-568h@2x.png in Resources */,
- D07CD4451C5D573600B7FB28 /* Default-Landscape@2x.png in Resources */,
- D07CD4441C5D573600B7FB28 /* Default-Landscape-736h@3x.png in Resources */,
- D07CD4421C5D573600B7FB28 /* Default-Portrait-736h@3x.png in Resources */,
- D07CD4481C5D573600B7FB28 /* Default-Portrait-1366h@2x.png in Resources */,
D0BCFE4618AEBDA2004A7AAE /* InfoPlist.strings in Resources */,
$additional_pbx_resources_build
);
@@ -377,6 +345,7 @@
buildSettings = {
ARCHS = "$godot_archs";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CODE_SIGN_ENTITLEMENTS = $binary/$binary.entitlements;
CODE_SIGN_IDENTITY = "$code_sign_identity_debug";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "$code_sign_identity_debug";
@@ -401,6 +370,7 @@
buildSettings = {
ARCHS = "$godot_archs";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CODE_SIGN_ENTITLEMENTS = $binary/$binary.entitlements;
CODE_SIGN_IDENTITY = "$code_sign_identity_release";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "$code_sign_identity_release";
diff --git a/misc/dist/ios_xcode/godot_ios/Default-568h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-568h@2x.png
deleted file mode 100644
index 1d5e472665..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-568h@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-667h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-667h@2x.png
deleted file mode 100644
index b51598fed0..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-667h@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Landscape-1366h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Landscape-1366h@2x.png
deleted file mode 100644
index ec5b4f7888..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Landscape-1366h@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Landscape-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Default-Landscape-736h@3x.png
deleted file mode 100644
index 2a025b745b..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Landscape-736h@3x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Landscape@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Landscape@2x.png
deleted file mode 100644
index 7099f3e18d..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Landscape@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Portrait-1366h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Portrait-1366h@2x.png
deleted file mode 100644
index a6d054ba2a..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Portrait-1366h@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Portrait-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Default-Portrait-736h@3x.png
deleted file mode 100644
index 8c44edbccd..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Portrait-736h@3x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Portrait@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Portrait@2x.png
deleted file mode 100644
index a6d054ba2a..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Portrait@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Contents.json b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 0000000000..ce81d76027
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,102 @@
+{
+ "images" : [
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "2436h",
+ "filename" : "Default-Portrait-X.png",
+ "minimum-system-version" : "11.0",
+ "orientation" : "portrait",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "2436h",
+ "filename" : "Default-Landscape-X.png",
+ "minimum-system-version" : "11.0",
+ "orientation" : "landscape",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "736h",
+ "filename" : "Default-Portrait-736h@3x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "portrait",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "736h",
+ "filename" : "Default-Landscape-736h@3x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "landscape",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "667h",
+ "filename" : "Default-667h@2x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "portrait",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "filename" : "Default-480h@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "retina4",
+ "filename" : "Default-568h@2x.png",
+ "minimum-system-version" : "7.0",
+ "orientation" : "portrait",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png
new file mode 100644
index 0000000000..6b9b10daae
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
new file mode 100644
index 0000000000..50497496b7
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png
new file mode 100644
index 0000000000..1c489de69f
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png
new file mode 100644
index 0000000000..d82dfce936
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png
new file mode 100644
index 0000000000..5120595df8
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png
new file mode 100644
index 0000000000..cf6cf1347a
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
new file mode 100644
index 0000000000..4eb167ae18
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png
new file mode 100644
index 0000000000..a9f951deac
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png
new file mode 100644
index 0000000000..06d16412e2
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png
new file mode 100644
index 0000000000..d70cab17be
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
new file mode 100644
index 0000000000..f934971074
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
Binary files differ
diff --git a/misc/travis/scons-local-osx.sh b/misc/travis/scons-local-osx.sh
index d9d7d98b38..fe7b43aadc 100755
--- a/misc/travis/scons-local-osx.sh
+++ b/misc/travis/scons-local-osx.sh
@@ -5,10 +5,10 @@ echo "Download and install Scons local package ..."
echo
echo "Downloading sources ..."
-curl -L -O http://prdownloads.sourceforge.net/scons/scons-local-3.0.0.zip # latest version available here: http://scons.org/pages/download.html
+curl -L -O https://downloads.sourceforge.net/scons/scons-local-3.0.1.zip # latest version available here: http://scons.org/pages/download.html
echo "Extracting to build directory ..."
-unzip -qq -n scons-local-3.0.0.zip -d $TRAVIS_BUILD_DIR/scons-local
+unzip -qq -n scons-local-3.0.1.zip -d $TRAVIS_BUILD_DIR/scons-local
echo "Installing symlinks ..."
ln -s $TRAVIS_BUILD_DIR/scons-local/scons.py /usr/local/bin/scons
diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp
index 648919e612..bfb452d109 100644
--- a/modules/bullet/area_bullet.cpp
+++ b/modules/bullet/area_bullet.cpp
@@ -68,7 +68,9 @@ AreaBullet::AreaBullet() :
}
AreaBullet::~AreaBullet() {
- remove_all_overlapping_instantly();
+ // signal are handled by godot, so just clear without notify
+ for (int i = overlappingObjects.size() - 1; 0 <= i; --i)
+ overlappingObjects[i].object->on_exit_area(this);
}
void AreaBullet::dispatch_callbacks() {
@@ -121,23 +123,21 @@ void AreaBullet::scratch() {
isScratched = true;
}
-void AreaBullet::remove_all_overlapping_instantly() {
- CollisionObjectBullet *supportObject;
+void AreaBullet::clear_overlaps(bool p_notify) {
for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
- supportObject = overlappingObjects[i].object;
- call_event(supportObject, PhysicsServer::AREA_BODY_REMOVED);
- supportObject->on_exit_area(this);
+ if (p_notify)
+ call_event(overlappingObjects[i].object, PhysicsServer::AREA_BODY_REMOVED);
+ overlappingObjects[i].object->on_exit_area(this);
}
overlappingObjects.clear();
}
-void AreaBullet::remove_overlapping_instantly(CollisionObjectBullet *p_object) {
- CollisionObjectBullet *supportObject;
+void AreaBullet::remove_overlap(CollisionObjectBullet *p_object, bool p_notify) {
for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
- supportObject = overlappingObjects[i].object;
- if (supportObject == p_object) {
- call_event(supportObject, PhysicsServer::AREA_BODY_REMOVED);
- supportObject->on_exit_area(this);
+ if (overlappingObjects[i].object == p_object) {
+ if (p_notify)
+ call_event(overlappingObjects[i].object, PhysicsServer::AREA_BODY_REMOVED);
+ overlappingObjects[i].object->on_exit_area(this);
overlappingObjects.remove(i);
break;
}
diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h
index 78136d574b..b2046c684e 100644
--- a/modules/bullet/area_bullet.h
+++ b/modules/bullet/area_bullet.h
@@ -150,9 +150,9 @@ public:
void set_on_state_change(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant());
void scratch();
- void remove_all_overlapping_instantly();
+ void clear_overlaps(bool p_notify);
// Dispatch the callbacks and removes from overlapping list
- void remove_overlapping_instantly(CollisionObjectBullet *p_object);
+ void remove_overlap(CollisionObjectBullet *p_object, bool p_notify);
virtual void on_collision_filters_change();
virtual void on_collision_checker_start() {}
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
index 34aff68a4a..05c0e653df 100644
--- a/modules/bullet/collision_object_bullet.cpp
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -68,12 +68,10 @@ CollisionObjectBullet::CollisionObjectBullet(Type p_type) :
force_shape_reset(false) {}
CollisionObjectBullet::~CollisionObjectBullet() {
- // Remove all overlapping
+ // Remove all overlapping, notify is not required since godot take care of it
for (int i = areasOverlapped.size() - 1; 0 <= i; --i) {
- areasOverlapped[i]->remove_overlapping_instantly(this);
+ areasOverlapped[i]->remove_overlap(this, /*Notify*/ false);
}
- // not required
- // areasOverlapped.clear();
destroyBulletCollisionObject();
}
diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp
index 72c982bb0b..caa3d677dd 100644
--- a/modules/bullet/godot_result_callbacks.cpp
+++ b/modules/bullet/godot_result_callbacks.cpp
@@ -63,6 +63,9 @@ bool GodotClosestRayResultCallback::needsCollision(btBroadphaseProxy *proxy0) co
}
bool GodotAllConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
+ if (count >= m_resultMax)
+ return false;
+
const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
if (needs) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
@@ -70,6 +73,7 @@ bool GodotAllConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) con
if (m_exclude->has(gObj->get_self())) {
return false;
}
+
return true;
} else {
return false;
@@ -87,7 +91,7 @@ btScalar GodotAllConvexResultCallback::addSingleResult(btCollisionWorld::LocalCo
result.collider = 0 == result.collider_id ? NULL : ObjectDB::get_instance(result.collider_id);
++count;
- return count < m_resultMax;
+ return 1; // not used by bullet
}
bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
@@ -181,6 +185,9 @@ btScalar GodotAllContactResultCallback::addSingleResult(btManifoldPoint &cp, con
}
bool GodotContactPairContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
+ if (m_count >= m_resultMax)
+ return false;
+
const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
if (needs) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
@@ -206,7 +213,7 @@ btScalar GodotContactPairContactResultCallback::addSingleResult(btManifoldPoint
++m_count;
- return m_count < m_resultMax;
+ return 1; // Not used by bullet
}
bool GodotRestInfoContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
@@ -252,20 +259,17 @@ btScalar GodotRestInfoContactResultCallback::addSingleResult(btManifoldPoint &cp
m_collided = true;
}
- return cp.getDistance();
+ return 1; // Not used by bullet
}
void GodotDeepPenetrationContactResultCallback::addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorldOnB, btScalar depth) {
- // Has penetration
- if (m_penetration_distance < ABS(depth)) {
+ if (m_penetration_distance > depth) { // Has penetration?
bool isSwapped = m_manifoldPtr->getBody0() != m_body0Wrap->getCollisionObject();
-
m_penetration_distance = depth;
- m_pointCollisionObject = (isSwapped ? m_body0Wrap : m_body1Wrap)->getCollisionObject();
- m_other_compound_shape_index = isSwapped ? m_index1 : m_index0;
+ m_other_compound_shape_index = isSwapped ? m_index0 : m_index1;
m_pointNormalWorld = isSwapped ? normalOnBInWorld * -1 : normalOnBInWorld;
- m_pointWorld = isSwapped ? (pointInWorldOnB + normalOnBInWorld * depth) : pointInWorldOnB;
+ m_pointWorld = isSwapped ? (pointInWorldOnB + (normalOnBInWorld * depth)) : pointInWorldOnB;
}
}
diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h
index 60493d4788..363051f24c 100644
--- a/modules/bullet/godot_result_callbacks.h
+++ b/modules/bullet/godot_result_callbacks.h
@@ -185,21 +185,18 @@ struct GodotDeepPenetrationContactResultCallback : public btManifoldResult {
btVector3 m_pointWorld;
btScalar m_penetration_distance;
int m_other_compound_shape_index;
- const btCollisionObject *m_pointCollisionObject;
GodotDeepPenetrationContactResultCallback(const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap) :
btManifoldResult(body0Wrap, body1Wrap),
- m_pointCollisionObject(NULL),
m_penetration_distance(0),
m_other_compound_shape_index(0) {}
void reset() {
- m_pointCollisionObject = NULL;
m_penetration_distance = 0;
}
bool hasHit() {
- return m_pointCollisionObject;
+ return m_penetration_distance < 0;
}
virtual void addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorldOnB, btScalar depth);
diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp
index 9cbf83689b..76d9614465 100644
--- a/modules/bullet/shape_bullet.cpp
+++ b/modules/bullet/shape_bullet.cpp
@@ -125,14 +125,13 @@ btScaledBvhTriangleMeshShape *ShapeBullet::create_shape_concave(btBvhTriangleMes
}
}
-btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size) {
+btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) {
const btScalar ignoredHeightScale(1);
- const btScalar fieldHeight(500); // Meters
const int YAxis = 1; // 0=X, 1=Y, 2=Z
const bool flipQuadEdges = false;
const void *heightsPtr = p_heights.read().ptr();
- return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, -fieldHeight, fieldHeight, YAxis, PHY_FLOAT, flipQuadEdges));
+ return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, p_min_height, p_max_height, YAxis, PHY_FLOAT, flipQuadEdges));
}
btRayShape *ShapeBullet::create_shape_ray(real_t p_length, bool p_slips_on_slope) {
@@ -387,19 +386,44 @@ void HeightMapShapeBullet::set_data(const Variant &p_data) {
Dictionary d = p_data;
ERR_FAIL_COND(!d.has("width"));
ERR_FAIL_COND(!d.has("depth"));
- ERR_FAIL_COND(!d.has("cell_size"));
ERR_FAIL_COND(!d.has("heights"));
+ real_t l_min_height = 0.0;
+ real_t l_max_height = 0.0;
+
+ // If specified, min and max height will be used as precomputed values
+ if (d.has("min_height"))
+ l_min_height = d["min_height"];
+ if (d.has("max_height"))
+ l_max_height = d["max_height"];
+
+ ERR_FAIL_COND(l_min_height > l_max_height);
+
int l_width = d["width"];
int l_depth = d["depth"];
- real_t l_cell_size = d["cell_size"];
PoolVector<real_t> l_heights = d["heights"];
ERR_FAIL_COND(l_width <= 0);
ERR_FAIL_COND(l_depth <= 0);
- ERR_FAIL_COND(l_cell_size <= CMP_EPSILON);
- ERR_FAIL_COND(l_heights.size() != (width * depth));
- setup(heights, width, depth, cell_size);
+ ERR_FAIL_COND(l_heights.size() != (l_width * l_depth));
+
+ // Compute min and max heights if not specified.
+ if (!d.has("min_height") && !d.has("max_height")) {
+
+ PoolVector<real_t>::Read r = heights.read();
+ int heights_size = heights.size();
+
+ for (int i = 0; i < heights_size; ++i) {
+ real_t h = r[i];
+
+ if (h < l_min_height)
+ l_min_height = h;
+ else if (h > l_max_height)
+ l_max_height = h;
+ }
+ }
+
+ setup(l_heights, l_width, l_depth, l_min_height, l_max_height);
}
Variant HeightMapShapeBullet::get_data() const {
@@ -410,8 +434,14 @@ PhysicsServer::ShapeType HeightMapShapeBullet::get_type() const {
return PhysicsServer::SHAPE_HEIGHTMAP;
}
-void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size) {
+void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) {
+ // TODO cell size must be tweaked using localScaling, which is a shared property for all Bullet shapes
+
{ // Copy
+
+ // TODO If Godot supported 16-bit integer image format, we could share the same memory block for heightfields
+ // without having to copy anything, optimizing memory and loading performance (Bullet only reads and doesn't take ownership of the data).
+
const int heights_size = p_heights.size();
heights.resize(heights_size);
PoolVector<real_t>::Read p_heights_r = p_heights.read();
@@ -420,14 +450,16 @@ void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int
heights_w[i] = p_heights_r[i];
}
}
+
width = p_width;
depth = p_depth;
- cell_size = p_cell_size;
+ min_height = p_min_height;
+ max_height = p_max_height;
notifyShapeChanged();
}
btCollisionShape *HeightMapShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
- btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, cell_size));
+ btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, min_height, max_height));
cs->setLocalScaling(p_implicit_scale);
prepare(cs);
cs->setMargin(p_margin);
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
index 2acba90e36..abeea0f9ce 100644
--- a/modules/bullet/shape_bullet.h
+++ b/modules/bullet/shape_bullet.h
@@ -85,7 +85,7 @@ public:
/// IMPORTANT: Remember to delete the shape interface by calling: delete my_shape->getMeshInterface();
static class btConvexPointCloudShape *create_shape_convex(btAlignedObjectArray<btVector3> &p_vertices, const btVector3 &p_local_scaling = btVector3(1, 1, 1));
static class btScaledBvhTriangleMeshShape *create_shape_concave(btBvhTriangleMeshShape *p_mesh_shape, const btVector3 &p_local_scaling = btVector3(1, 1, 1));
- static class btHeightfieldTerrainShape *create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size);
+ static class btHeightfieldTerrainShape *create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
static class btRayShape *create_shape_ray(real_t p_length, bool p_slips_on_slope);
};
@@ -199,7 +199,8 @@ public:
PoolVector<real_t> heights;
int width;
int depth;
- real_t cell_size;
+ real_t min_height;
+ real_t max_height;
HeightMapShapeBullet();
@@ -209,7 +210,7 @@ public:
virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
private:
- void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size);
+ void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
};
class RayShapeBullet : public ShapeBullet {
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index 8c15758e0f..8450a66f65 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -660,7 +660,10 @@ void SpaceBullet::check_ghost_overlaps() {
// For each overlapping
for (i = ghostOverlaps.size() - 1; 0 <= i; --i) {
- if (!(ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_RIGID_BODY || ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_AREA))
+ if (ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_AREA) {
+ if (!static_cast<AreaBullet *>(ghostOverlaps[i]->getUserPointer())->is_monitorable())
+ continue;
+ } else if (ghostOverlaps[i]->getUserIndex() != CollisionObjectBullet::TYPE_RIGID_BODY)
continue;
otherObject = static_cast<RigidCollisionObjectBullet *>(ghostOverlaps[i]->getUserPointer());
@@ -993,7 +996,7 @@ public:
}
void reset() {
- result_collision_objects.empty();
+ result_collision_objects.clear();
}
};
@@ -1028,7 +1031,10 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran
for (int i = recover_broad_result.result_collision_objects.size() - 1; 0 <= i; --i) {
btCollisionObject *otherObject = recover_broad_result.result_collision_objects[i];
- if (!p_body->get_bt_collision_object()->checkCollideWith(otherObject) || !otherObject->checkCollideWith(p_body->get_bt_collision_object()) || (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()))
+ if (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()) {
+ otherObject->activate(); // Force activation of hitten rigid, soft body
+ continue;
+ } else if (!p_body->get_bt_collision_object()->checkCollideWith(otherObject) || !otherObject->checkCollideWith(p_body->get_bt_collision_object()))
continue;
if (otherObject->getCollisionShape()->isCompound()) {
@@ -1117,7 +1123,7 @@ bool SpaceBullet::RFP_convex_world_test(const btConvexShape *p_shapeA, const btC
dispatcher->freeCollisionAlgorithm(algorithm);
if (contactPointResult.hasHit()) {
- r_delta_recover_movement += contactPointResult.m_pointNormalWorld * (contactPointResult.m_penetration_distance * p_recover_movement_scale);
+ r_delta_recover_movement += contactPointResult.m_pointNormalWorld * (contactPointResult.m_penetration_distance * -1 * p_recover_movement_scale);
if (r_recover_result) {
if (contactPointResult.m_penetration_distance < r_recover_result->penetration_distance) {
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
index f3f4acd768..28b19224fb 100644
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -115,9 +115,6 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address &p_ip, int p_port
#endif
address.port = p_port;
- //enet_address_set_host (& address, "localhost");
- //address.port = p_port;
-
unique_id = _gen_unique_id();
/* Initiate the connection, allocating the enough channels */
@@ -128,7 +125,7 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address &p_ip, int p_port
ERR_FAIL_COND_V(!peer, ERR_CANT_CREATE);
}
- //technically safe to ignore the peer or anything else.
+ // Technically safe to ignore the peer or anything else.
connection_status = CONNECTION_CONNECTING;
active = true;
@@ -148,13 +145,13 @@ void NetworkedMultiplayerENet::poll() {
/* Wait up to 1000 milliseconds for an event. */
while (true) {
- if (!host || !active) //might have been disconnected while emitting a notification
+ if (!host || !active) // Might have been disconnected while emitting a notification
return;
int ret = enet_host_service(host, &event, 1);
if (ret < 0) {
- //error, do something?
+ // Error, do something?
break;
} else if (ret == 0) {
break;
@@ -172,7 +169,7 @@ void NetworkedMultiplayerENet::poll() {
int *new_id = memnew(int);
*new_id = event.data;
- if (*new_id == 0) { //data zero is sent by server (enet won't let you configure this). Server is always 1
+ if (*new_id == 0) { // Data zero is sent by server (enet won't let you configure this). Server is always 1
*new_id = 1;
}
@@ -180,22 +177,22 @@ void NetworkedMultiplayerENet::poll() {
peer_map[*new_id] = event.peer;
- connection_status = CONNECTION_CONNECTED; //if connecting, this means it connected t something!
+ connection_status = CONNECTION_CONNECTED; // If connecting, this means it connected to something!
emit_signal("peer_connected", *new_id);
if (server) {
- //someone connected, let it know of all the peers available
+ // Someone connected, notify all the peers available
for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
if (E->key() == *new_id)
continue;
- //send existing peers to new peer
+ // Send existing peers to new peer
ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE);
encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]);
encode_uint32(E->key(), &packet->data[4]);
enet_peer_send(event.peer, SYSCH_CONFIG, packet);
- //send the new peer to existing peers
+ // Send the new peer to existing peers
packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE);
encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]);
encode_uint32(*new_id, &packet->data[4]);
@@ -220,12 +217,12 @@ void NetworkedMultiplayerENet::poll() {
} else {
if (server) {
- //someone disconnected, let it know to everyone else
+ // Someone disconnected, notify everyone else
for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
if (E->key() == *id)
continue;
- //send the new peer to existing peers
+
ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE);
encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]);
encode_uint32(*id, &packet->data[4]);
@@ -246,7 +243,7 @@ void NetworkedMultiplayerENet::poll() {
case ENET_EVENT_TYPE_RECEIVE: {
if (event.channelID == SYSCH_CONFIG) {
- //some config message
+ // Some config message
ERR_CONTINUE(event.packet->dataLength < 8);
// Only server can send config messages
@@ -292,13 +289,13 @@ void NetworkedMultiplayerENet::poll() {
packet.from = *id;
if (target == 0) {
- //re-send the everyone but sender :|
+ // Re-send to everyone but sender :|
incoming_packets.push_back(packet);
- //and make copies for sending
+ // And make copies for sending
for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
- if (uint32_t(E->key()) == source) //do not resend to self
+ if (uint32_t(E->key()) == source) // Do not resend to self
continue;
ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags);
@@ -307,12 +304,12 @@ void NetworkedMultiplayerENet::poll() {
}
} else if (target < 0) {
- //to all but one
+ // To all but one
- //and make copies for sending
+ // And make copies for sending
for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
- if (uint32_t(E->key()) == source || E->key() == -target) //do not resend to self, also do not send to excluded
+ if (uint32_t(E->key()) == source || E->key() == -target) // Do not resend to self, also do not send to excluded
continue;
ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags);
@@ -321,18 +318,18 @@ void NetworkedMultiplayerENet::poll() {
}
if (-target != 1) {
- //server is not excluded
+ // Server is not excluded
incoming_packets.push_back(packet);
} else {
- //server is excluded, erase packet
+ // Server is excluded, erase packet
enet_packet_destroy(packet.packet);
}
} else if (target == 1) {
- //to myself and only myself
+ // To myself and only myself
incoming_packets.push_back(packet);
} else {
- //to someone else, specifically
+ // To someone else, specifically
ERR_CONTINUE(!peer_map.has(target));
enet_peer_send(peer_map[target], event.channelID, packet.packet);
}
@@ -341,14 +338,14 @@ void NetworkedMultiplayerENet::poll() {
incoming_packets.push_back(packet);
}
- //destroy packet later..
+ // Destroy packet later..
} else {
ERR_CONTINUE(true);
}
} break;
case ENET_EVENT_TYPE_NONE: {
- //do nothing
+ // Do nothing
} break;
}
}
@@ -377,16 +374,46 @@ void NetworkedMultiplayerENet::close_connection() {
if (peers_disconnected) {
enet_host_flush(host);
- OS::get_singleton()->delay_usec(100); //wait 100ms for disconnection packets to send
+ OS::get_singleton()->delay_usec(100); // Wait 100ms for disconnection packets to send
}
enet_host_destroy(host);
active = false;
incoming_packets.clear();
- unique_id = 1; //server is 1
+ unique_id = 1; // Server is 1
connection_status = CONNECTION_DISCONNECTED;
}
+void NetworkedMultiplayerENet::disconnect_peer(int p_peer, bool now) {
+
+ ERR_FAIL_COND(!active);
+ ERR_FAIL_COND(!is_server());
+ ERR_FAIL_COND(!peer_map.has(p_peer))
+
+ if (now) {
+ enet_peer_disconnect_now(peer_map[p_peer], 0);
+
+ // enet_peer_disconnect_now doesn't generate ENET_EVENT_TYPE_DISCONNECT,
+ // notify everyone else, send disconnect signal & remove from peer_map like in poll()
+
+ for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
+
+ if (E->key() == p_peer)
+ continue;
+
+ ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE);
+ encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]);
+ encode_uint32(p_peer, &packet->data[4]);
+ enet_peer_send(E->get(), SYSCH_CONFIG, packet);
+ }
+
+ emit_signal("peer_disconnected", p_peer);
+ peer_map.erase(p_peer);
+ } else {
+ enet_peer_disconnect_later(peer_map[p_peer], 0);
+ }
+}
+
int NetworkedMultiplayerENet::get_available_packet_count() const {
return incoming_packets.size();
@@ -440,9 +467,9 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
}
ENetPacket *packet = enet_packet_create(NULL, p_buffer_size + 12, packet_flags);
- encode_uint32(unique_id, &packet->data[0]); //source ID
- encode_uint32(target_peer, &packet->data[4]); //dest ID
- encode_uint32(packet_flags, &packet->data[8]); //dest ID
+ encode_uint32(unique_id, &packet->data[0]); // Source ID
+ encode_uint32(target_peer, &packet->data[4]); // Dest ID
+ encode_uint32(packet_flags, &packet->data[8]); // Dest ID
copymem(&packet->data[12], p_buffer, p_buffer_size);
if (server) {
@@ -450,14 +477,14 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
if (target_peer == 0) {
enet_host_broadcast(host, channel, packet);
} else if (target_peer < 0) {
- //send to all but one
- //and make copies for sending
+ // Send to all but one
+ // and make copies for sending
int exclude = -target_peer;
for (Map<int, ENetPeer *>::Element *F = peer_map.front(); F; F = F->next()) {
- if (F->key() == exclude) // exclude packet
+ if (F->key() == exclude) // Exclude packet
continue;
ENetPacket *packet2 = enet_packet_create(packet->data, packet->dataLength, packet_flags);
@@ -465,14 +492,14 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
enet_peer_send(F->get(), channel, packet2);
}
- enet_packet_destroy(packet); //original packet no longer needed
+ enet_packet_destroy(packet); // Original packet no longer needed
} else {
enet_peer_send(E->get(), channel, packet);
}
} else {
ERR_FAIL_COND_V(!peer_map.has(1), ERR_BUG);
- enet_peer_send(peer_map[1], channel, packet); //send to server for broadcast..
+ enet_peer_send(peer_map[1], channel, packet); // Send to server for broadcast..
}
enet_host_flush(host);
@@ -482,7 +509,7 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
int NetworkedMultiplayerENet::get_max_packet_size() const {
- return 1 << 24; //anything is good
+ return 1 << 24; // Anything is good
}
void NetworkedMultiplayerENet::_pop_current_packet() {
@@ -511,16 +538,12 @@ uint32_t NetworkedMultiplayerENet::_gen_unique_id() const {
(uint32_t)OS::get_singleton()->get_unix_time(), hash);
hash = hash_djb2_one_32(
(uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash);
- /*
hash = hash_djb2_one_32(
- (uint32_t)OS::get_singleton()->get_unique_id().hash64(), hash );
- */
+ (uint32_t)((uint64_t)this), hash); // Rely on ASLR heap
hash = hash_djb2_one_32(
- (uint32_t)((uint64_t)this), hash); //rely on aslr heap
- hash = hash_djb2_one_32(
- (uint32_t)((uint64_t)&hash), hash); //rely on aslr stack
+ (uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack
- hash = hash & 0x7FFFFFFF; // make it compatible with unsigned, since negatie id is used for exclusion
+ hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion
}
return hash;
@@ -596,7 +619,7 @@ size_t NetworkedMultiplayerENet::enet_compress(void *context, const ENetBuffer *
return 0;
if (ret > int(outLimit))
- return 0; //do not bother
+ return 0; // Do not bother
copymem(outData, enet->dst_compressor_mem.ptr(), ret);
@@ -651,7 +674,35 @@ void NetworkedMultiplayerENet::_setup_compressor() {
void NetworkedMultiplayerENet::enet_compressor_destroy(void *context) {
- //do none
+ // Nothing to do
+}
+
+IP_Address NetworkedMultiplayerENet::get_peer_address(int p_peer_id) const {
+
+ ERR_FAIL_COND_V(!peer_map.has(p_peer_id), IP_Address());
+ ERR_FAIL_COND_V(!is_server() && p_peer_id != 1, IP_Address());
+ ERR_FAIL_COND_V(peer_map[p_peer_id] == NULL, IP_Address());
+
+ IP_Address out;
+#ifdef GODOT_ENET
+ out.set_ipv6((uint8_t *)&(peer_map[p_peer_id]->address.host));
+#else
+ out.set_ipv4((uint8_t *)&(peer_map[p_peer_id]->address.host));
+#endif
+
+ return out;
+}
+
+int NetworkedMultiplayerENet::get_peer_port(int p_peer_id) const {
+
+ ERR_FAIL_COND_V(!peer_map.has(p_peer_id), 0);
+ ERR_FAIL_COND_V(!is_server() && p_peer_id != 1, 0);
+ ERR_FAIL_COND_V(peer_map[p_peer_id] == NULL, 0);
+#ifdef GODOT_ENET
+ return peer_map[p_peer_id]->address.port;
+#else
+ return peer_map[p_peer_id]->address.port;
+#endif
}
void NetworkedMultiplayerENet::_bind_methods() {
@@ -659,9 +710,12 @@ void NetworkedMultiplayerENet::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_server", "port", "max_clients", "in_bandwidth", "out_bandwidth"), &NetworkedMultiplayerENet::create_server, DEFVAL(32), DEFVAL(0), DEFVAL(0));
ClassDB::bind_method(D_METHOD("create_client", "ip", "port", "in_bandwidth", "out_bandwidth"), &NetworkedMultiplayerENet::create_client, DEFVAL(0), DEFVAL(0));
ClassDB::bind_method(D_METHOD("close_connection"), &NetworkedMultiplayerENet::close_connection);
+ ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "now"), &NetworkedMultiplayerENet::disconnect_peer, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_compression_mode", "mode"), &NetworkedMultiplayerENet::set_compression_mode);
ClassDB::bind_method(D_METHOD("get_compression_mode"), &NetworkedMultiplayerENet::get_compression_mode);
ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &NetworkedMultiplayerENet::set_bind_ip);
+ ClassDB::bind_method(D_METHOD("get_peer_address"), &NetworkedMultiplayerENet::get_peer_address);
+ ClassDB::bind_method(D_METHOD("get_peer_port"), &NetworkedMultiplayerENet::get_peer_port);
ADD_PROPERTY(PropertyInfo(Variant::INT, "compression_mode", PROPERTY_HINT_ENUM, "None,Range Coder,FastLZ,ZLib,ZStd"), "set_compression_mode", "get_compression_mode");
@@ -696,7 +750,7 @@ NetworkedMultiplayerENet::~NetworkedMultiplayerENet() {
close_connection();
}
-// sets IP for ENet to bind when using create_server
+// Sets IP for ENet to bind when using create_server
// if no IP is set, then ENet bind to ENET_HOST_ANY
void NetworkedMultiplayerENet::set_bind_ip(const IP_Address &p_ip) {
ERR_FAIL_COND(!p_ip.is_valid() && !p_ip.is_wildcard());
diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h
index 440e9b5400..0e8dd67160 100644
--- a/modules/enet/networked_multiplayer_enet.h
+++ b/modules/enet/networked_multiplayer_enet.h
@@ -115,11 +115,16 @@ public:
virtual int get_packet_peer() const;
+ virtual IP_Address get_peer_address(int p_peer_id) const;
+ virtual int get_peer_port(int p_peer_id) const;
+
Error create_server(int p_port, int p_max_clients = 32, int p_in_bandwidth = 0, int p_out_bandwidth = 0);
Error create_client(const IP_Address &p_ip, int p_port, int p_in_bandwidth = 0, int p_out_bandwidth = 0);
void close_connection();
+ void disconnect_peer(int p_peer, bool now = false);
+
virtual void poll();
virtual bool is_server() const;
diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml
index 308d8defc3..754a6d2514 100644
--- a/modules/gdnative/doc_classes/GDNativeLibrary.xml
+++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml
@@ -9,12 +9,6 @@
<demos>
</demos>
<methods>
- <method name="get_config_file">
- <return type="ConfigFile">
- </return>
- <description>
- </description>
- </method>
<method name="get_current_dependencies" qualifiers="const">
<return type="PoolStringArray">
</return>
@@ -29,6 +23,8 @@
</method>
</methods>
<members>
+ <member name="config_file" type="ConfigFile" setter="set_config_file" getter="get_config_file">
+ </member>
<member name="load_once" type="bool" setter="set_load_once" getter="should_load_once">
</member>
<member name="reloadable" type="bool" setter="set_reloadable" getter="is_reloadable">
diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp
index 42c3028f2c..897588385a 100644
--- a/modules/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative.cpp
@@ -66,8 +66,169 @@ GDNativeLibrary::GDNativeLibrary() {
GDNativeLibrary::~GDNativeLibrary() {
}
+bool GDNativeLibrary::_set(const StringName &p_name, const Variant &p_property) {
+
+ String name = p_name;
+
+ if (name.begins_with("entry/")) {
+ String key = name.substr(6, name.length() - 6);
+
+ config_file->set_value("entry", key, p_property);
+
+ set_config_file(config_file);
+
+ return true;
+ }
+
+ if (name.begins_with("dependency/")) {
+ String key = name.substr(11, name.length() - 11);
+
+ config_file->set_value("dependencies", key, p_property);
+
+ set_config_file(config_file);
+
+ return true;
+ }
+
+ return false;
+}
+
+bool GDNativeLibrary::_get(const StringName &p_name, Variant &r_property) const {
+ String name = p_name;
+
+ if (name.begins_with("entry/")) {
+ String key = name.substr(6, name.length() - 6);
+
+ r_property = config_file->get_value("entry", key);
+
+ return true;
+ }
+
+ if (name.begins_with("dependency/")) {
+ String key = name.substr(11, name.length() - 11);
+
+ r_property = config_file->get_value("dependencies", key);
+
+ return true;
+ }
+
+ return false;
+}
+
+void GDNativeLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
+ // set entries
+ List<String> entry_key_list;
+
+ if (config_file->has_section("entry"))
+ config_file->get_section_keys("entry", &entry_key_list);
+
+ for (List<String>::Element *E = entry_key_list.front(); E; E = E->next()) {
+ String key = E->get();
+
+ PropertyInfo prop;
+
+ prop.type = Variant::STRING;
+ prop.name = "entry/" + key;
+
+ p_list->push_back(prop);
+ }
+
+ // set dependencies
+ List<String> dependency_key_list;
+
+ if (config_file->has_section("dependencies"))
+ config_file->get_section_keys("dependencies", &dependency_key_list);
+
+ for (List<String>::Element *E = dependency_key_list.front(); E; E = E->next()) {
+ String key = E->get();
+
+ PropertyInfo prop;
+
+ prop.type = Variant::STRING;
+ prop.name = "dependency/" + key;
+
+ p_list->push_back(prop);
+ }
+}
+
+void GDNativeLibrary::set_config_file(Ref<ConfigFile> p_config_file) {
+
+ set_singleton(p_config_file->get_value("general", "singleton", default_singleton));
+ set_load_once(p_config_file->get_value("general", "load_once", default_load_once));
+ set_symbol_prefix(p_config_file->get_value("general", "symbol_prefix", default_symbol_prefix));
+ set_reloadable(p_config_file->get_value("general", "reloadable", default_reloadable));
+
+ String entry_lib_path;
+ {
+
+ List<String> entry_keys;
+
+ if (p_config_file->has_section("entry"))
+ p_config_file->get_section_keys("entry", &entry_keys);
+
+ for (List<String>::Element *E = entry_keys.front(); E; E = E->next()) {
+ String key = E->get();
+
+ Vector<String> tags = key.split(".");
+
+ bool skip = false;
+ for (int i = 0; i < tags.size(); i++) {
+ bool has_feature = OS::get_singleton()->has_feature(tags[i]);
+
+ if (!has_feature) {
+ skip = true;
+ break;
+ }
+ }
+
+ if (skip) {
+ continue;
+ }
+
+ entry_lib_path = p_config_file->get_value("entry", key);
+ break;
+ }
+ }
+
+ Vector<String> dependency_paths;
+ {
+
+ List<String> dependency_keys;
+
+ if (p_config_file->has_section("dependencies"))
+ p_config_file->get_section_keys("dependencies", &dependency_keys);
+
+ for (List<String>::Element *E = dependency_keys.front(); E; E = E->next()) {
+ String key = E->get();
+
+ Vector<String> tags = key.split(".");
+
+ bool skip = false;
+ for (int i = 0; i < tags.size(); i++) {
+ bool has_feature = OS::get_singleton()->has_feature(tags[i]);
+
+ if (!has_feature) {
+ skip = true;
+ break;
+ }
+ }
+
+ if (skip) {
+ continue;
+ }
+
+ dependency_paths = p_config_file->get_value("dependencies", key);
+ break;
+ }
+ }
+
+ current_library_path = entry_lib_path;
+ current_dependencies = dependency_paths;
+}
+
void GDNativeLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_config_file"), &GDNativeLibrary::get_config_file);
+ ClassDB::bind_method(D_METHOD("set_config_file", "config_file"), &GDNativeLibrary::set_config_file);
ClassDB::bind_method(D_METHOD("get_current_library_path"), &GDNativeLibrary::get_current_library_path);
ClassDB::bind_method(D_METHOD("get_current_dependencies"), &GDNativeLibrary::get_current_dependencies);
@@ -82,6 +243,8 @@ void GDNativeLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_symbol_prefix", "symbol_prefix"), &GDNativeLibrary::set_symbol_prefix);
ClassDB::bind_method(D_METHOD("set_reloadable", "reloadable"), &GDNativeLibrary::set_reloadable);
+ ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"), "set_config_file", "get_config_file");
+
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once");
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton");
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "symbol_prefix"), "set_symbol_prefix", "get_symbol_prefix");
@@ -337,73 +500,7 @@ RES GDNativeLibraryResourceLoader::load(const String &p_path, const String &p_or
*r_error = err;
}
- lib->set_singleton(config->get_value("general", "singleton", default_singleton));
- lib->set_load_once(config->get_value("general", "load_once", default_load_once));
- lib->set_symbol_prefix(config->get_value("general", "symbol_prefix", default_symbol_prefix));
- lib->set_reloadable(config->get_value("general", "reloadable", default_reloadable));
-
- String entry_lib_path;
- {
-
- List<String> entry_keys;
- config->get_section_keys("entry", &entry_keys);
-
- for (List<String>::Element *E = entry_keys.front(); E; E = E->next()) {
- String key = E->get();
-
- Vector<String> tags = key.split(".");
-
- bool skip = false;
- for (int i = 0; i < tags.size(); i++) {
- bool has_feature = OS::get_singleton()->has_feature(tags[i]);
-
- if (!has_feature) {
- skip = true;
- break;
- }
- }
-
- if (skip) {
- continue;
- }
-
- entry_lib_path = config->get_value("entry", key);
- break;
- }
- }
-
- Vector<String> dependency_paths;
- {
-
- List<String> dependency_keys;
- config->get_section_keys("dependencies", &dependency_keys);
-
- for (List<String>::Element *E = dependency_keys.front(); E; E = E->next()) {
- String key = E->get();
-
- Vector<String> tags = key.split(".");
-
- bool skip = false;
- for (int i = 0; i < tags.size(); i++) {
- bool has_feature = OS::get_singleton()->has_feature(tags[i]);
-
- if (!has_feature) {
- skip = true;
- break;
- }
- }
-
- if (skip) {
- continue;
- }
-
- dependency_paths = config->get_value("dependencies", key);
- break;
- }
- }
-
- lib->current_library_path = entry_lib_path;
- lib->current_dependencies = dependency_paths;
+ lib->set_config_file(config);
return lib;
}
diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h
index 3298ea950f..b17bb94f1c 100644
--- a/modules/gdnative/gdnative.h
+++ b/modules/gdnative/gdnative.h
@@ -66,8 +66,14 @@ public:
GDNativeLibrary();
~GDNativeLibrary();
+ virtual bool _set(const StringName &p_name, const Variant &p_property);
+ virtual bool _get(const StringName &p_name, Variant &r_property) const;
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
_FORCE_INLINE_ Ref<ConfigFile> get_config_file() { return config_file; }
+ void set_config_file(Ref<ConfigFile> p_config_file);
+
// things that change per-platform
// so there are no setters for this
_FORCE_INLINE_ String get_current_library_path() const {
diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp
index 7f5dbc12be..ab0f0e0a50 100644
--- a/modules/gdnative/gdnative/string.cpp
+++ b/modules/gdnative/gdnative/string.cpp
@@ -1168,24 +1168,6 @@ godot_string GDAPI godot_string_c_unescape(const godot_string *p_self) {
return result;
}
-godot_string GDAPI godot_string_http_escape(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->http_escape();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_http_unescape(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->http_unescape();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
godot_string GDAPI godot_string_json_escape(const godot_string *p_self) {
const String *self = (const String *)p_self;
godot_string result;
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index a8919f7130..9da2a69360 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -5468,20 +5468,6 @@
]
},
{
- "name": "godot_string_http_escape",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_http_unescape",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
"name": "godot_string_json_escape",
"return_type": "godot_string",
"arguments": [
@@ -5822,6 +5808,23 @@
]
},
{
+ "name": "godot_nativescript_set_global_type_tag",
+ "return_type": "void",
+ "arguments": [
+ ["int", "p_idx"],
+ ["const char *", "p_name"],
+ ["const void *", "p_type_tag"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_get_global_type_tag",
+ "return_type": "const void *",
+ "arguments": [
+ ["int", "p_idx"],
+ ["const char *", "p_name"]
+ ]
+ },
+ {
"name": "godot_nativescript_set_type_tag",
"return_type": "void",
"arguments": [
diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h
index 73245160c1..8fc59e21da 100644
--- a/modules/gdnative/include/gdnative/string.h
+++ b/modules/gdnative/include/gdnative/string.h
@@ -228,17 +228,14 @@ godot_string GDAPI godot_string_simplify_path(const godot_string *p_self);
godot_string GDAPI godot_string_c_escape(const godot_string *p_self);
godot_string GDAPI godot_string_c_escape_multiline(const godot_string *p_self);
godot_string GDAPI godot_string_c_unescape(const godot_string *p_self);
-godot_string GDAPI godot_string_http_escape(const godot_string *p_self);
-godot_string GDAPI godot_string_http_unescape(const godot_string *p_self);
+godot_string GDAPI godot_string_percent_decode(const godot_string *p_self);
+godot_string GDAPI godot_string_percent_encode(const godot_string *p_self);
godot_string GDAPI godot_string_json_escape(const godot_string *p_self);
godot_string GDAPI godot_string_word_wrap(const godot_string *p_self, godot_int p_chars_per_line);
godot_string GDAPI godot_string_xml_escape(const godot_string *p_self);
godot_string GDAPI godot_string_xml_escape_with_quotes(const godot_string *p_self);
godot_string GDAPI godot_string_xml_unescape(const godot_string *p_self);
-godot_string GDAPI godot_string_percent_decode(const godot_string *p_self);
-godot_string GDAPI godot_string_percent_encode(const godot_string *p_self);
-
godot_bool GDAPI godot_string_is_valid_float(const godot_string *p_self);
godot_bool GDAPI godot_string_is_valid_hex_number(const godot_string *p_self, godot_bool p_with_prefix);
godot_bool GDAPI godot_string_is_valid_html_color(const godot_string *p_self);
diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h
index de47ec55cc..cfbe16fa7d 100644
--- a/modules/gdnative/include/nativescript/godot_nativescript.h
+++ b/modules/gdnative/include/nativescript/godot_nativescript.h
@@ -214,13 +214,16 @@ void GDAPI godot_nativescript_set_signal_documentation(void *p_gdnative_handle,
// type tag API
+void GDAPI godot_nativescript_set_global_type_tag(int p_idx, const char *p_name, const void *p_type_tag);
+const void GDAPI *godot_nativescript_get_global_type_tag(int p_idx, const char *p_name);
+
void GDAPI godot_nativescript_set_type_tag(void *p_gdnative_handle, const char *p_name, const void *p_type_tag);
const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object);
// instance binding API
typedef struct {
- GDCALLINGCONV void *(*alloc_instance_binding_data)(void *, godot_object *);
+ GDCALLINGCONV void *(*alloc_instance_binding_data)(void *, const void *, godot_object *);
GDCALLINGCONV void (*free_instance_binding_data)(void *, void *);
void *data;
GDCALLINGCONV void (*free_func)(void *);
diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp
index aea595d0f0..ace2ecac5c 100644
--- a/modules/gdnative/nativescript/godot_nativescript.cpp
+++ b/modules/gdnative/nativescript/godot_nativescript.cpp
@@ -313,6 +313,14 @@ void GDAPI godot_nativescript_set_signal_documentation(void *p_gdnative_handle,
signal->get().documentation = *(String *)&p_documentation;
}
+void GDAPI godot_nativescript_set_global_type_tag(int p_idx, const char *p_name, const void *p_type_tag) {
+ NativeScriptLanguage::get_singleton()->set_global_type_tag(p_idx, StringName(p_name), p_type_tag);
+}
+
+const void GDAPI *godot_nativescript_get_global_type_tag(int p_idx, const char *p_name) {
+ return NativeScriptLanguage::get_singleton()->get_global_type_tag(p_idx, StringName(p_name));
+}
+
void GDAPI godot_nativescript_set_type_tag(void *p_gdnative_handle, const char *p_name, const void *p_type_tag) {
String *s = (String *)p_gdnative_handle;
@@ -331,13 +339,11 @@ const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object)
const Object *o = (Object *)p_object;
if (!o->get_script_instance()) {
- ERR_EXPLAIN("Attempted to get type tag on an object without a script!");
- ERR_FAIL_V(NULL);
+ return NULL;
} else {
NativeScript *script = Object::cast_to<NativeScript>(o->get_script_instance()->get_script().ptr());
if (!script) {
- ERR_EXPLAIN("Attempted to get type tag on an object without a nativescript attached");
- ERR_FAIL_V(NULL);
+ return NULL;
}
if (script->get_script_desc())
@@ -347,10 +353,6 @@ const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object)
return NULL;
}
-#ifdef __cplusplus
-}
-#endif
-
int GDAPI godot_nativescript_register_instance_binding_data_functions(godot_instance_binding_functions p_binding_functions) {
return NativeScriptLanguage::get_singleton()->register_binding_functions(p_binding_functions);
}
@@ -362,3 +364,7 @@ void GDAPI godot_nativescript_unregister_instance_binding_data_functions(int p_i
void GDAPI *godot_nativescript_get_instance_binding_data(int p_idx, godot_object *p_object) {
return NativeScriptLanguage::get_singleton()->get_instance_binding_data(p_idx, (Object *)p_object);
}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index ff31035036..d255148e0f 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -55,12 +55,6 @@
#include "editor/editor_node.h"
#endif
-//
-//
-// Script stuff
-//
-//
-
void NativeScript::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_class_name", "class_name"), &NativeScript::set_class_name);
ClassDB::bind_method(D_METHOD("get_class_name"), &NativeScript::get_class_name);
@@ -528,12 +522,6 @@ NativeScript::~NativeScript() {
#endif
}
- //
- //
- // ScriptInstance stuff
- //
- //
-
#define GET_SCRIPT_DESC() script->get_script_desc()
void NativeScriptInstance::_ml_call_reversed(NativeScriptDesc *script_data, const StringName &p_method, const Variant **p_args, int p_argcount) {
@@ -872,12 +860,6 @@ NativeScriptInstance::~NativeScriptInstance() {
}
}
-//
-//
-// ScriptingLanguage stuff
-//
-//
-
NativeScriptLanguage *NativeScriptLanguage::singleton;
void NativeScriptLanguage::_unload_stuff(bool p_reload) {
@@ -1195,8 +1177,11 @@ void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_objec
}
if (!(*binding_data)[p_idx]) {
+
+ const void *global_type_tag = global_type_tags[p_idx].get(p_object->get_class_name());
+
// no binding data yet, soooooo alloc new one \o/
- (*binding_data)[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, (godot_object *)p_object);
+ (*binding_data)[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, global_type_tag, (godot_object *)p_object);
}
return (*binding_data)[p_idx];
@@ -1238,6 +1223,27 @@ void NativeScriptLanguage::free_instance_binding_data(void *p_data) {
delete &binding_data;
}
+void NativeScriptLanguage::set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag) {
+ if (!global_type_tags.has(p_idx)) {
+ global_type_tags.insert(p_idx, HashMap<StringName, const void *>());
+ }
+
+ HashMap<StringName, const void *> &tags = global_type_tags[p_idx];
+
+ tags.set(p_class_name, p_type_tag);
+}
+
+const void *NativeScriptLanguage::get_global_type_tag(int p_idx, StringName p_class_name) const {
+ if (!global_type_tags.has(p_idx))
+ return NULL;
+
+ const HashMap<StringName, const void *> &tags = global_type_tags[p_idx];
+
+ const void *tag = tags.get(p_class_name);
+
+ return tag;
+}
+
#ifndef NO_THREADS
void NativeScriptLanguage::defer_init_library(Ref<GDNativeLibrary> lib, NativeScript *script) {
MutexLock lock(mutex);
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index 17b6ddc747..68a8126a32 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -36,6 +36,7 @@
#include "core/self_list.h"
#include "io/resource_loader.h"
#include "io/resource_saver.h"
+#include "oa_hash_map.h"
#include "ordered_hash_map.h"
#include "os/thread_safe.h"
#include "scene/main/node.h"
@@ -240,6 +241,8 @@ private:
Vector<Pair<bool, godot_instance_binding_functions> > binding_functions;
Set<Vector<void *> *> binding_instances;
+ Map<int, HashMap<StringName, const void *> > global_type_tags;
+
public:
// These two maps must only be touched on the main thread
Map<String, Map<StringName, NativeScriptDesc> > library_classes;
@@ -323,6 +326,9 @@ public:
virtual void *alloc_instance_binding_data(Object *p_object);
virtual void free_instance_binding_data(void *p_data);
+
+ void set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag);
+ const void *get_global_type_tag(int p_idx, StringName p_class_name) const;
};
inline NativeScriptDesc *NativeScript::get_script_desc() const {
diff --git a/modules/gdnative/pluginscript/register_types.cpp b/modules/gdnative/pluginscript/register_types.cpp
index 8888f9e157..924abf29df 100644
--- a/modules/gdnative/pluginscript/register_types.cpp
+++ b/modules/gdnative/pluginscript/register_types.cpp
@@ -64,7 +64,7 @@ static Error _check_language_desc(const godot_pluginscript_language_desc *desc)
// desc->make_function is not mandatory
// desc->complete_code is not mandatory
// desc->auto_indent_code is not mandatory
- // desc->add_global_constant is not mandatory
+ ERR_FAIL_COND_V(!desc->add_global_constant, ERR_BUG);
// desc->debug_get_error is not mandatory
// desc->debug_get_stack_level_count is not mandatory
// desc->debug_get_stack_level_line is not mandatory
@@ -78,7 +78,7 @@ static Error _check_language_desc(const godot_pluginscript_language_desc *desc)
// desc->profiling_stop is not mandatory
// desc->profiling_get_accumulated_data is not mandatory
// desc->profiling_get_frame_data is not mandatory
- // desc->frame is not mandatory
+ // desc->profiling_frame is not mandatory
ERR_FAIL_COND_V(!desc->script_desc.init, ERR_BUG);
ERR_FAIL_COND_V(!desc->script_desc.finish, ERR_BUG);
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 5f72dca866..0d52f0a995 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -1333,13 +1333,23 @@ static void _find_identifiers_in_block(GDScriptCompletionContext &context, int p
for (int i = 0; i < context.block->statements.size(); i++) {
- if (context.block->statements[i]->line > p_line)
+ GDScriptParser::Node *statement = context.block->statements[i];
+ if (statement->line > p_line)
continue;
- if (context.block->statements[i]->type == GDScriptParser::BlockNode::TYPE_LOCAL_VAR) {
+ GDScriptParser::BlockNode::Type statementType = statement->type;
+ if (statementType == GDScriptParser::BlockNode::TYPE_LOCAL_VAR) {
- const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(context.block->statements[i]);
+ const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(statement);
result.insert(lv->name.operator String());
+ } else if (statementType == GDScriptParser::BlockNode::TYPE_CONTROL_FLOW) {
+
+ const GDScriptParser::ControlFlowNode *cf = static_cast<const GDScriptParser::ControlFlowNode *>(statement);
+ if (cf->cf_type == GDScriptParser::ControlFlowNode::CF_FOR) {
+
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(cf->arguments[0]);
+ result.insert(id->name.operator String());
+ }
}
}
}
@@ -2850,7 +2860,24 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
return OK;
}
} else {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ /*
+ // Because get_integer_constant_enum and get_integer_constant dont work on @GlobalScope
+ // We cannot determine the exact nature of the identifier here
+ // Otherwise these codes would work
+ StringName enumName = ClassDB::get_integer_constant_enum("@GlobalScope", p_symbol, true);
+ if (enumName != NULL) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM;
+ r_result.class_name = "@GlobalScope";
+ r_result.class_member = enumName;
+ return OK;
+ }
+ else {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name = "@GlobalScope";
+ r_result.class_member = p_symbol;
+ return OK;
+ }*/
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_TBD_GLOBALSCOPE;
r_result.class_name = "@GlobalScope";
r_result.class_member = p_symbol;
return OK;
@@ -2913,6 +2940,14 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
return OK;
}
+ StringName enumName = ClassDB::get_integer_constant_enum(t.obj_type, p_symbol, true);
+ if (enumName != StringName()) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM;
+ r_result.class_name = t.obj_type;
+ r_result.class_member = enumName;
+ return OK;
+ }
+
bool success;
ClassDB::get_integer_constant(t.obj_type, p_symbol, &success);
if (success) {
diff --git a/modules/gdscript/gdscript_highlighter.cpp b/modules/gdscript/gdscript_highlighter.cpp
new file mode 100644
index 0000000000..4e89851bf2
--- /dev/null
+++ b/modules/gdscript/gdscript_highlighter.cpp
@@ -0,0 +1,262 @@
+/*************************************************************************/
+/* gdscript_highlighter.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 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 "gdscript_highlighter.h"
+#include "scene/gui/text_edit.h"
+
+inline bool _is_symbol(CharType c) {
+
+ return is_symbol(c);
+}
+
+static bool _is_text_char(CharType c) {
+
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+}
+
+static bool _is_whitespace(CharType c) {
+ return c == '\t' || c == ' ';
+}
+
+static bool _is_char(CharType c) {
+
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
+}
+
+static bool _is_number(CharType c) {
+ return (c >= '0' && c <= '9');
+}
+
+static bool _is_hex_symbol(CharType c) {
+ return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
+}
+
+Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) {
+ Map<int, TextEdit::HighlighterInfo> color_map;
+
+ bool prev_is_char = false;
+ bool prev_is_number = false;
+ bool in_keyword = false;
+ bool in_word = false;
+ bool in_function_name = false;
+ bool in_member_variable = false;
+ bool is_hex_notation = false;
+ Color keyword_color;
+ Color color;
+
+ int in_region = text_editor->_is_line_in_region(p_line);
+ int deregion = 0;
+
+ const Map<int, TextEdit::Text::ColorRegionInfo> cri_map = text_editor->_get_line_color_region_info(p_line);
+ const String &str = text_editor->get_line(p_line);
+ Color prev_color;
+ for (int j = 0; j < str.length(); j++) {
+ TextEdit::HighlighterInfo highlighter_info;
+
+ if (deregion > 0) {
+ deregion--;
+ if (deregion == 0) {
+ in_region = -1;
+ }
+ }
+
+ if (deregion != 0) {
+ if (color != prev_color) {
+ prev_color = color;
+ highlighter_info.color = color;
+ color_map[j] = highlighter_info;
+ }
+ continue;
+ }
+
+ color = font_color;
+
+ bool is_char = _is_text_char(str[j]);
+ bool is_symbol = _is_symbol(str[j]);
+ bool is_number = _is_number(str[j]);
+
+ // allow ABCDEF in hex notation
+ if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
+ is_number = true;
+ } else {
+ is_hex_notation = false;
+ }
+
+ // check for dot or underscore or 'x' for hex notation in floating point number
+ if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) {
+ is_number = true;
+ is_symbol = false;
+ is_char = false;
+
+ if (str[j] == 'x' && str[j - 1] == '0') {
+ is_hex_notation = true;
+ }
+ }
+
+ if (!in_word && _is_char(str[j]) && !is_number) {
+ in_word = true;
+ }
+
+ if ((in_keyword || in_word) && !is_hex_notation) {
+ is_number = false;
+ }
+
+ if (is_symbol && str[j] != '.' && in_word) {
+ in_word = false;
+ }
+
+ if (is_symbol && cri_map.has(j)) {
+ const TextEdit::Text::ColorRegionInfo &cri = cri_map[j];
+
+ if (in_region == -1) {
+ if (!cri.end) {
+ in_region = cri.region;
+ }
+ } else {
+ TextEdit::ColorRegion cr = text_editor->_get_color_region(cri.region);
+ if (in_region == cri.region && !cr.line_only) { //ignore otherwise
+ if (cri.end || cr.eq) {
+ deregion = cr.eq ? cr.begin_key.length() : cr.end_key.length();
+ }
+ }
+ }
+ }
+
+ if (!is_char) {
+ in_keyword = false;
+ }
+
+ if (in_region == -1 && !in_keyword && is_char && !prev_is_char) {
+
+ int to = j;
+ while (to < str.length() && _is_text_char(str[to]))
+ to++;
+
+ String word = str.substr(j, to - j);
+ Color col = Color();
+ if (text_editor->has_keyword_color(word)) {
+ col = text_editor->get_keyword_color(word);
+ } else if (text_editor->has_member_color(word)) {
+ col = text_editor->get_member_color(word);
+ for (int k = j - 1; k >= 0; k--) {
+ if (str[k] == '.') {
+ col = Color(); //member indexing not allowed
+ break;
+ } else if (str[k] > 32) {
+ break;
+ }
+ }
+ }
+
+ if (col != Color()) {
+ in_keyword = true;
+ keyword_color = col;
+ }
+ }
+
+ if (!in_function_name && in_word && !in_keyword) {
+
+ int k = j;
+ while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ k++;
+ }
+
+ // check for space between name and bracket
+ while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
+ k++;
+ }
+
+ if (str[k] == '(') {
+ in_function_name = true;
+ }
+ }
+
+ if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
+ int k = j;
+ while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ k--;
+ }
+
+ if (str[k] == '.') {
+ in_member_variable = true;
+ }
+ }
+
+ if (is_symbol) {
+ in_function_name = false;
+ in_member_variable = false;
+ }
+
+ if (in_region >= 0)
+ color = text_editor->_get_color_region(in_region).color;
+ else if (in_keyword)
+ color = keyword_color;
+ else if (in_member_variable)
+ color = member_color;
+ else if (in_function_name)
+ color = function_color;
+ else if (is_symbol)
+ color = symbol_color;
+ else if (is_number)
+ color = number_color;
+
+ prev_is_char = is_char;
+ prev_is_number = is_number;
+
+ if (color != prev_color) {
+ prev_color = color;
+ highlighter_info.color = color;
+ color_map[j] = highlighter_info;
+ }
+ }
+ return color_map;
+}
+
+String GDScriptSyntaxHighlighter::get_name() {
+ return "GDScript";
+}
+
+List<String> GDScriptSyntaxHighlighter::get_supported_languages() {
+ List<String> languages;
+ languages.push_back("GDScript");
+ return languages;
+}
+
+void GDScriptSyntaxHighlighter::_update_cache() {
+ font_color = text_editor->get_color("font_color");
+ symbol_color = text_editor->get_color("symbol_color");
+ function_color = text_editor->get_color("function_color");
+ number_color = text_editor->get_color("number_color");
+ member_color = text_editor->get_color("member_variable_color");
+}
+
+SyntaxHighlighter *GDScriptSyntaxHighlighter::create() {
+ return memnew(GDScriptSyntaxHighlighter);
+}
diff --git a/modules/gdscript/gdscript_highlighter.h b/modules/gdscript/gdscript_highlighter.h
new file mode 100644
index 0000000000..ef1bdd4103
--- /dev/null
+++ b/modules/gdscript/gdscript_highlighter.h
@@ -0,0 +1,56 @@
+/*************************************************************************/
+/* gdscript_highlighter.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 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 GDSCRIPT_HIGHLIGHTER_H
+#define GDSCRIPT_HIGHLIGHTER_H
+
+#include "scene/gui/text_edit.h"
+
+class GDScriptSyntaxHighlighter : public SyntaxHighlighter {
+private:
+ // colours
+ Color font_color;
+ Color symbol_color;
+ Color function_color;
+ Color built_in_type_color;
+ Color number_color;
+ Color member_color;
+
+public:
+ static SyntaxHighlighter *create();
+
+ virtual void _update_cache();
+ virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line);
+
+ virtual String get_name();
+ virtual List<String> get_supported_languages();
+};
+
+#endif // GDSCRIPT_HIGHLIGHTER_H
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index 95efcda80f..85c94c3596 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -31,6 +31,7 @@
#include "register_types.h"
#include "gdscript.h"
+#include "gdscript_highlighter.h"
#include "gdscript_tokenizer.h"
#include "io/file_access_encrypted.h"
#include "io/resource_loader.h"
@@ -92,6 +93,7 @@ void register_gdscript_types() {
ResourceSaver::add_resource_format_saver(resource_saver_gd);
#ifdef TOOLS_ENABLED
+ ScriptEditor::register_create_syntax_highlighter_function(GDScriptSyntaxHighlighter::create);
EditorNode::add_init_callback(_editor_init);
#endif
}
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 869492232c..f523eef895 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -1046,14 +1046,14 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
options->get_popup()->add_item(TTR("Previous Floor"), MENU_OPTION_PREV_LEVEL, KEY_Q);
options->get_popup()->add_item(TTR("Next Floor"), MENU_OPTION_NEXT_LEVEL, KEY_E);
options->get_popup()->add_separator();
- options->get_popup()->add_check_item(TTR("Clip Disabled"), MENU_OPTION_CLIP_DISABLED);
+ options->get_popup()->add_radio_check_item(TTR("Clip Disabled"), MENU_OPTION_CLIP_DISABLED);
options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_CLIP_DISABLED), true);
- options->get_popup()->add_check_item(TTR("Clip Above"), MENU_OPTION_CLIP_ABOVE);
- options->get_popup()->add_check_item(TTR("Clip Below"), MENU_OPTION_CLIP_BELOW);
+ options->get_popup()->add_radio_check_item(TTR("Clip Above"), MENU_OPTION_CLIP_ABOVE);
+ options->get_popup()->add_radio_check_item(TTR("Clip Below"), MENU_OPTION_CLIP_BELOW);
options->get_popup()->add_separator();
- options->get_popup()->add_check_item(TTR("Edit X Axis"), MENU_OPTION_X_AXIS, KEY_Z);
- options->get_popup()->add_check_item(TTR("Edit Y Axis"), MENU_OPTION_Y_AXIS, KEY_X);
- options->get_popup()->add_check_item(TTR("Edit Z Axis"), MENU_OPTION_Z_AXIS, KEY_C);
+ options->get_popup()->add_radio_check_item(TTR("Edit X Axis"), MENU_OPTION_X_AXIS, KEY_Z);
+ options->get_popup()->add_radio_check_item(TTR("Edit Y Axis"), MENU_OPTION_Y_AXIS, KEY_X);
+ options->get_popup()->add_radio_check_item(TTR("Edit Z Axis"), MENU_OPTION_Z_AXIS, KEY_C);
options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_Y_AXIS), true);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Cursor Rotate X"), MENU_OPTION_CURSOR_ROTATE_X, KEY_A);
diff --git a/modules/mbedtls/stream_peer_mbed_tls.cpp b/modules/mbedtls/stream_peer_mbed_tls.cpp
index 4135eb40ff..a63e53ec1f 100755
--- a/modules/mbedtls/stream_peer_mbed_tls.cpp
+++ b/modules/mbedtls/stream_peer_mbed_tls.cpp
@@ -293,28 +293,10 @@ void StreamPeerMbedTLS::initialize_ssl() {
mbedtls_debug_set_threshold(1);
#endif
- String certs_path = GLOBAL_DEF("network/ssl/certificates", "");
- ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt"));
-
- if (certs_path != "") {
-
- FileAccess *f = FileAccess::open(certs_path, FileAccess::READ);
- if (f) {
- PoolByteArray arr;
- int flen = f->get_len();
- arr.resize(flen + 1);
- {
- PoolByteArray::Write w = arr.write();
- f->get_buffer(w.ptr(), flen);
- w[flen] = 0; //end f string
- }
-
- memdelete(f);
-
- _load_certs(arr);
- print_line("Loaded certs from '" + certs_path);
- }
- }
+ PoolByteArray cert_array = StreamPeerSSL::get_project_cert_array();
+
+ if (cert_array.size() > 0)
+ _load_certs(cert_array);
available = true;
}
diff --git a/modules/mbedtls/stream_peer_mbed_tls.h b/modules/mbedtls/stream_peer_mbed_tls.h
index ce17614d85..2b96a194a1 100755
--- a/modules/mbedtls/stream_peer_mbed_tls.h
+++ b/modules/mbedtls/stream_peer_mbed_tls.h
@@ -32,8 +32,6 @@
#define STREAM_PEER_OPEN_SSL_H
#include "io/stream_peer_ssl.h"
-#include "os/file_access.h"
-#include "project_settings.h"
#include "mbedtls/config.h"
#include "mbedtls/ctr_drbg.h"
diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp
index f1cf0bcdf5..1b5a303835 100644
--- a/modules/mono/editor/mono_bottom_panel.cpp
+++ b/modules/mono/editor/mono_bottom_panel.cpp
@@ -407,9 +407,14 @@ void MonoBuildTab::stop_build() {
void MonoBuildTab::_issue_activated(int p_idx) {
- ERR_FAIL_INDEX(p_idx, issues.size());
+ ERR_FAIL_INDEX(p_idx, issues_list->get_item_count());
- const BuildIssue &issue = issues[p_idx];
+ // Get correct issue idx from issue list
+ int issue_idx = this->issues_list->get_item_metadata(p_idx);
+
+ ERR_FAIL_INDEX(issue_idx, issues.size());
+
+ const BuildIssue &issue = issues[issue_idx];
if (issue.project_file.empty() && issue.file.empty())
return;
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 69503e631c..eb10c5e99f 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -3236,6 +3236,12 @@ void VisualScriptEditor::_member_option(int p_option) {
}
}
+void VisualScriptEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
+}
+
+void VisualScriptEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
+}
+
void VisualScriptEditor::_bind_methods() {
ClassDB::bind_method("_member_button", &VisualScriptEditor::_member_button);
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index 80bbf142d9..72b5e09222 100644
--- a/modules/visual_script/visual_script_editor.h
+++ b/modules/visual_script/visual_script_editor.h
@@ -246,6 +246,9 @@ protected:
static void _bind_methods();
public:
+ virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter);
+ virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter);
+
virtual void apply_code();
virtual Ref<Script> get_edited_script() const;
virtual Vector<String> get_functions();
diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp
index d3330d683c..e0b987b4d7 100644
--- a/modules/websocket/emws_peer.cpp
+++ b/modules/websocket/emws_peer.cpp
@@ -148,12 +148,14 @@ void EMWSPeer::close() {
IP_Address EMWSPeer::get_connected_host() const {
- return IP_Address();
+ ERR_EXPLAIN("Not supported in HTML5 export");
+ ERR_FAIL_V(IP_Address());
};
uint16_t EMWSPeer::get_connected_port() const {
- return 1025;
+ ERR_EXPLAIN("Not supported in HTML5 export");
+ ERR_FAIL_V(0);
};
EMWSPeer::EMWSPeer() {
diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp
index c9ddae0c8c..3eb93e4152 100644
--- a/modules/websocket/emws_server.cpp
+++ b/modules/websocket/emws_server.cpp
@@ -58,6 +58,19 @@ PoolVector<String> EMWSServer::get_protocols() const {
return out;
}
+IP_Address EMWSServer::get_peer_address(int p_peer_id) const {
+
+ return IP_Address();
+}
+
+int EMWSServer::get_peer_port(int p_peer_id) const {
+
+ return 0;
+}
+
+void EMWSServer::disconnect_peer(int p_peer_id) {
+}
+
EMWSServer::EMWSServer() {
}
diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h
index aa089ea40d..9ec4ce72c8 100644
--- a/modules/websocket/emws_server.h
+++ b/modules/websocket/emws_server.h
@@ -46,6 +46,9 @@ public:
bool is_listening() const;
bool has_peer(int p_id) const;
Ref<WebSocketPeer> get_peer(int p_id) const;
+ IP_Address get_peer_address(int p_peer_id) const;
+ int get_peer_port(int p_peer_id) const;
+ void disconnect_peer(int p_peer_id);
virtual void poll();
virtual PoolVector<String> get_protocols() const;
diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp
index bebf342f8c..2220c9adf2 100644
--- a/modules/websocket/lws_client.cpp
+++ b/modules/websocket/lws_client.cpp
@@ -31,6 +31,7 @@
#include "lws_client.h"
#include "core/io/ip.h"
+#include "core/io/stream_peer_ssl.h"
Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {
@@ -64,6 +65,9 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
info.uid = -1;
//info.ws_ping_pong_interval = 5;
info.user = _lws_ref;
+#if defined(LWS_OPENSSL_SUPPORT)
+ info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
+#endif
context = lws_create_context(&info);
if (context == NULL) {
@@ -87,7 +91,14 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
i.host = hbuf;
i.path = pbuf;
i.port = p_port;
- i.ssl_connection = p_ssl;
+
+ if (p_ssl) {
+ i.ssl_connection = LCCSCF_USE_SSL;
+ if (!verify_ssl)
+ i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
+ } else {
+ i.ssl_connection = 0;
+ }
lws_client_connect_via_info(&i);
return OK;
@@ -104,6 +115,13 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user;
switch (reason) {
+ case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: {
+ PoolByteArray arr = StreamPeerSSL::get_project_cert_array();
+ if (arr.size() > 0)
+ SSL_CTX_add_client_CA((SSL_CTX *)user, d2i_X509(NULL, &arr.read()[0], arr.size()));
+ else if (verify_ssl)
+ WARN_PRINTS("No CA cert specified in project settings, SSL will not work");
+ } break;
case LWS_CALLBACK_CLIENT_ESTABLISHED:
peer->set_wsi(wsi);
diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp
index ba45d7688f..8a064fb5a4 100644
--- a/modules/websocket/lws_peer.cpp
+++ b/modules/websocket/lws_peer.cpp
@@ -32,6 +32,13 @@
#include "lws_peer.h"
#include "core/io/ip.h"
+// Needed for socket_helpers on Android at least. UNIXes has it, just include if not windows
+#if !defined(WINDOWS_ENABLED)
+#include <netinet/in.h>
+#endif
+
+#include "drivers/unix/socket_helpers.h"
+
void LWSPeer::set_wsi(struct lws *p_wsi) {
wsi = p_wsi;
};
@@ -178,12 +185,40 @@ void LWSPeer::close() {
IP_Address LWSPeer::get_connected_host() const {
- return IP_Address();
+ ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address());
+
+ IP_Address ip;
+ int port = 0;
+
+ socklen_t len;
+ struct sockaddr_storage addr;
+ int fd = lws_get_socket_fd(wsi);
+
+ int ret = getpeername(fd, (struct sockaddr *)&addr, &len);
+ ERR_FAIL_COND_V(ret != 0, IP_Address());
+
+ _set_ip_addr_port(ip, port, &addr);
+
+ return ip;
};
uint16_t LWSPeer::get_connected_port() const {
- return 1025;
+ ERR_FAIL_COND_V(!is_connected_to_host(), 0);
+
+ IP_Address ip;
+ int port = 0;
+
+ socklen_t len;
+ struct sockaddr_storage addr;
+ int fd = lws_get_socket_fd(wsi);
+
+ int ret = getpeername(fd, (struct sockaddr *)&addr, &len);
+ ERR_FAIL_COND_V(ret != 0, 0);
+
+ _set_ip_addr_port(ip, port, &addr);
+
+ return port;
};
LWSPeer::LWSPeer() {
diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp
index 94fe4231ae..8d13dc7a98 100644
--- a/modules/websocket/lws_server.cpp
+++ b/modules/websocket/lws_server.cpp
@@ -164,6 +164,24 @@ Ref<WebSocketPeer> LWSServer::get_peer(int p_id) const {
return _peer_map[p_id];
}
+IP_Address LWSServer::get_peer_address(int p_peer_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_peer_id), IP_Address());
+
+ return _peer_map[p_peer_id]->get_connected_host();
+}
+
+int LWSServer::get_peer_port(int p_peer_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_peer_id), 0);
+
+ return _peer_map[p_peer_id]->get_connected_port();
+}
+
+void LWSServer::disconnect_peer(int p_peer_id) {
+ ERR_FAIL_COND(!has_peer(p_peer_id));
+
+ get_peer(p_peer_id)->close();
+}
+
LWSServer::LWSServer() {
context = NULL;
_lws_ref = NULL;
diff --git a/modules/websocket/lws_server.h b/modules/websocket/lws_server.h
index de8f59e5ae..9e3fb9b775 100644
--- a/modules/websocket/lws_server.h
+++ b/modules/websocket/lws_server.h
@@ -52,6 +52,9 @@ public:
bool is_listening() const;
bool has_peer(int p_id) const;
Ref<WebSocketPeer> get_peer(int p_id) const;
+ IP_Address get_peer_address(int p_peer_id) const;
+ int get_peer_port(int p_peer_id) const;
+ void disconnect_peer(int p_peer_id);
virtual void poll() { _lws_poll(); }
LWSServer();
diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp
index 591d9510ce..7701163085 100644
--- a/modules/websocket/websocket_client.cpp
+++ b/modules/websocket/websocket_client.cpp
@@ -32,6 +32,8 @@
GDCINULL(WebSocketClient);
WebSocketClient::WebSocketClient() {
+
+ verify_ssl = true;
}
WebSocketClient::~WebSocketClient() {
@@ -72,6 +74,16 @@ Error WebSocketClient::connect_to_url(String p_url, PoolVector<String> p_protoco
return connect_to_host(host, path, port, ssl, p_protocols);
}
+void WebSocketClient::set_verify_ssl_enabled(bool p_verify_ssl) {
+
+ verify_ssl = p_verify_ssl;
+}
+
+bool WebSocketClient::is_verify_ssl_enabled() const {
+
+ return verify_ssl;
+}
+
bool WebSocketClient::is_server() const {
return false;
@@ -116,6 +128,10 @@ void WebSocketClient::_on_error() {
void WebSocketClient::_bind_methods() {
ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api"), &WebSocketClient::connect_to_url, DEFVAL(PoolVector<String>()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("disconnect_from_host"), &WebSocketClient::disconnect_from_host);
+ ClassDB::bind_method(D_METHOD("set_verify_ssl_enabled", "enabled"), &WebSocketClient::set_verify_ssl_enabled);
+ ClassDB::bind_method(D_METHOD("is_verify_ssl_enabled"), &WebSocketClient::is_verify_ssl_enabled);
+
+ ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled");
ADD_SIGNAL(MethodInfo("data_received"));
ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol")));
diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h
index 5c863559bc..6165f37d40 100644
--- a/modules/websocket/websocket_client.h
+++ b/modules/websocket/websocket_client.h
@@ -41,12 +41,16 @@ class WebSocketClient : public WebSocketMultiplayerPeer {
protected:
Ref<WebSocketPeer> _peer;
+ bool verify_ssl;
static void _bind_methods();
public:
Error connect_to_url(String p_url, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false);
+ void set_verify_ssl_enabled(bool p_verify_ssl);
+ bool is_verify_ssl_enabled() const;
+
virtual void poll() = 0;
virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()) = 0;
virtual void disconnect_from_host() = 0;
diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp
index 6324047846..61f783e377 100644
--- a/modules/websocket/websocket_peer.cpp
+++ b/modules/websocket/websocket_peer.cpp
@@ -43,6 +43,8 @@ void WebSocketPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_connected_to_host"), &WebSocketPeer::is_connected_to_host);
ClassDB::bind_method(D_METHOD("was_string_packet"), &WebSocketPeer::was_string_packet);
ClassDB::bind_method(D_METHOD("close"), &WebSocketPeer::close);
+ ClassDB::bind_method(D_METHOD("get_connected_host"), &WebSocketPeer::get_connected_host);
+ ClassDB::bind_method(D_METHOD("get_connected_port"), &WebSocketPeer::get_connected_port);
BIND_ENUM_CONSTANT(WRITE_MODE_TEXT);
BIND_ENUM_CONSTANT(WRITE_MODE_BINARY);
diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp
index 5746f61e10..2693b26e47 100644
--- a/modules/websocket/websocket_server.cpp
+++ b/modules/websocket/websocket_server.cpp
@@ -44,6 +44,9 @@ void WebSocketServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(PoolVector<String>()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("stop"), &WebSocketServer::stop);
ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer);
+ ClassDB::bind_method(D_METHOD("get_peer_address"), &WebSocketServer::get_peer_address);
+ ClassDB::bind_method(D_METHOD("get_peer_port"), &WebSocketServer::get_peer_port);
+ ClassDB::bind_method(D_METHOD("disconnect_peer"), &WebSocketServer::disconnect_peer);
ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol")));
diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h
index 360ff9e6d4..64935f8a58 100644
--- a/modules/websocket/websocket_server.h
+++ b/modules/websocket/websocket_server.h
@@ -52,6 +52,10 @@ public:
virtual bool is_server() const;
ConnectionStatus get_connection_status() const;
+ virtual IP_Address get_peer_address(int p_peer_id) const = 0;
+ virtual int get_peer_port(int p_peer_id) const = 0;
+ virtual void disconnect_peer(int p_peer_id) = 0;
+
void _on_peer_packet(int32_t p_peer_id);
void _on_connect(int32_t p_peer_id, String p_protocol);
void _on_disconnect(int32_t p_peer_id);
diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp
index 9baf58c3eb..579c06f76b 100644
--- a/platform/android/java_glue.cpp
+++ b/platform/android/java_glue.cpp
@@ -936,6 +936,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj) {
+ if (step == 0)
+ return;
+
os_android->main_loop_request_go_back();
}
@@ -976,6 +979,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions) {
+ if (step == 0)
+ return;
+
Vector<OS_Android::TouchPos> points;
for (int i = 0; i < count; i++) {
@@ -1250,6 +1256,8 @@ static unsigned int android_get_keysym(unsigned int p_code) {
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jobject obj, jint p_device, jint p_button, jboolean p_pressed) {
+ if (step == 0)
+ return;
OS_Android::JoypadEvent jevent;
jevent.device = p_device;
@@ -1261,6 +1269,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value) {
+ if (step == 0)
+ return;
OS_Android::JoypadEvent jevent;
jevent.device = p_device;
@@ -1272,6 +1282,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env,
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jobject obj, jint p_device, jint p_hat_x, jint p_hat_y) {
+ if (step == 0)
+ return;
+
OS_Android::JoypadEvent jevent;
jevent.device = p_device;
jevent.type = OS_Android::JOY_EVENT_HAT;
@@ -1301,6 +1314,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_scancode, jint p_unicode_char, jboolean p_pressed) {
+ if (step == 0)
+ return;
Ref<InputEventKey> ievent;
ievent.instance();
@@ -1344,14 +1359,18 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jobject obj) {
- if (os_android && step > 0)
- os_android->main_loop_focusin();
+ if (step == 0)
+ return;
+
+ os_android->main_loop_focusin();
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jobject obj) {
- if (os_android && step > 0)
- os_android->main_loop_focusout();
+ if (step == 0)
+ return;
+
+ os_android->main_loop_focusout();
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jobject obj) {
diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm
index 5c3799ab09..9e6bbff1d7 100644
--- a/platform/iphone/app_delegate.mm
+++ b/platform/iphone/app_delegate.mm
@@ -79,6 +79,7 @@ static ViewController *mainViewController = nil;
}
NSMutableDictionary *ios_joysticks = nil;
+NSMutableArray *pending_ios_joysticks = nil;
- (GCControllerPlayerIndex)getFreePlayerIndex {
bool have_player_1 = false;
@@ -115,6 +116,30 @@ NSMutableDictionary *ios_joysticks = nil;
};
};
+void _ios_add_joystick(GCController *controller, AppDelegate *delegate) {
+ // get a new id for our controller
+ int joy_id = OSIPhone::get_singleton()->get_unused_joy_id();
+ if (joy_id != -1) {
+ // assign our player index
+ if (controller.playerIndex == GCControllerPlayerIndexUnset) {
+ controller.playerIndex = [delegate getFreePlayerIndex];
+ };
+
+ // tell Godot about our new controller
+ OSIPhone::get_singleton()->joy_connection_changed(
+ joy_id, true, [controller.vendorName UTF8String]);
+
+ // add it to our dictionary, this will retain our controllers
+ [ios_joysticks setObject:controller
+ forKey:[NSNumber numberWithInt:joy_id]];
+
+ // set our input handler
+ [delegate setControllerInputHandler:controller];
+ } else {
+ printf("Couldn't retrieve new joy id\n");
+ };
+}
+
- (void)controllerWasConnected:(NSNotification *)notification {
// create our dictionary if we don't have one yet
if (ios_joysticks == nil) {
@@ -127,28 +152,12 @@ NSMutableDictionary *ios_joysticks = nil;
printf("Couldn't retrieve new controller\n");
} else if ([[ios_joysticks allKeysForObject:controller] count] != 0) {
printf("Controller is already registered\n");
+ } else if (frame_count > 1) {
+ _ios_add_joystick(controller, self);
} else {
- // get a new id for our controller
- int joy_id = OSIPhone::get_singleton()->get_unused_joy_id();
- if (joy_id != -1) {
- // assign our player index
- if (controller.playerIndex == GCControllerPlayerIndexUnset) {
- controller.playerIndex = [self getFreePlayerIndex];
- };
-
- // tell Godot about our new controller
- OSIPhone::get_singleton()->joy_connection_changed(
- joy_id, true, [controller.vendorName UTF8String]);
-
- // add it to our dictionary, this will retain our controllers
- [ios_joysticks setObject:controller
- forKey:[NSNumber numberWithInt:joy_id]];
-
- // set our input handler
- [self setControllerInputHandler:controller];
- } else {
- printf("Couldn't retrieve new joy id\n");
- };
+ if (pending_ios_joysticks == nil)
+ pending_ios_joysticks = [[NSMutableArray alloc] init];
+ [pending_ios_joysticks addObject:controller];
};
};
@@ -352,6 +361,27 @@ NSMutableDictionary *ios_joysticks = nil;
[ios_joysticks dealloc];
ios_joysticks = nil;
};
+
+ if (pending_ios_joysticks != nil) {
+ [pending_ios_joysticks dealloc];
+ pending_ios_joysticks = nil;
+ };
+};
+
+OS::VideoMode _get_video_mode() {
+ int backingWidth;
+ int backingHeight;
+ glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
+ GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
+ glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
+ GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
+
+ OS::VideoMode vm;
+ vm.fullscreen = true;
+ vm.width = backingWidth;
+ vm.height = backingHeight;
+ vm.resizable = false;
+ return vm;
};
static int frame_count = 0;
@@ -360,19 +390,7 @@ static int frame_count = 0;
switch (frame_count) {
case 0: {
- int backingWidth;
- int backingHeight;
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
-
- OS::VideoMode vm;
- vm.fullscreen = true;
- vm.width = backingWidth;
- vm.height = backingHeight;
- vm.resizable = false;
- OS::get_singleton()->set_video_mode(vm);
+ OS::get_singleton()->set_video_mode(_get_video_mode());
if (!OS::get_singleton()) {
exit(0);
@@ -410,6 +428,14 @@ static int frame_count = 0;
Main::setup2();
++frame_count;
+ if (pending_ios_joysticks != nil) {
+ for (GCController *controller in pending_ios_joysticks) {
+ _ios_add_joystick(controller, self);
+ }
+ [pending_ios_joysticks dealloc];
+ pending_ios_joysticks = nil;
+ }
+
// this might be necessary before here
NSDictionary *dict = [[NSBundle mainBundle] infoDictionary];
for (NSString *key in dict) {
@@ -562,18 +588,13 @@ static int frame_count = 0;
//[glView setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
// UIViewAutoresizingFlexibleWidth];
- int backingWidth;
- int backingHeight;
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
+ OS::VideoMode vm = _get_video_mode();
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
- int err = iphone_main(backingWidth, backingHeight, gargc, gargv, String::utf8([documentsDirectory UTF8String]));
+ int err = iphone_main(vm.width, vm.height, gargc, gargv, String::utf8([documentsDirectory UTF8String]));
if (err != 0) {
// bail, things did not go very well for us, should probably output a message on screen with our error code...
exit(0);
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index e3119814f4..4c1e02baf7 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -147,6 +147,26 @@ Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_ge
return archs;
}
+struct LoadingScreenInfo {
+ const char *preset_key;
+ const char *export_name;
+};
+
+static const LoadingScreenInfo loading_screen_infos[] = {
+ { "landscape_launch_screens/iphone_2436x1125", "Default-Landscape-X.png" },
+ { "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png" },
+ { "landscape_launch_screens/ipad_1024x768", "Default-Landscape.png" },
+ { "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png" },
+
+ { "portrait_launch_screens/iphone_640x960", "Default-480h@2x.png" },
+ { "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png" },
+ { "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png" },
+ { "portrait_launch_screens/iphone_1125x2436", "Default-Portrait-X.png" },
+ { "portrait_launch_screens/ipad_768x1024", "Default-Portrait.png" },
+ { "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png" },
+ { "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png" }
+};
+
void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) {
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "zip"), ""));
@@ -172,6 +192,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/iphone_120x120", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPhone/iPod Touch with retina display
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/ipad_76x76", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPad
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/app_store_1024x1024", PROPERTY_HINT_FILE, "png"), "")); // App Store
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/iphone_180x180", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPhone with retina HD display
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_152x152", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPad with retina display
@@ -179,15 +200,9 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "png"), "")); // Spotlight
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "png"), "")); // Spotlight on devices with retina display
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "landscape_launch_screens/iphone_2208x1242", PROPERTY_HINT_FILE, "png"), "")); // iPhone 6 Plus, 6s Plus, 7 Plus
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "landscape_launch_screens/ipad_2732x2048", PROPERTY_HINT_FILE, "png"), "")); // 12.9-inch iPad Pro
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "landscape_launch_screens/ipad_2048x1536", PROPERTY_HINT_FILE, "png"), "")); // Other iPads
-
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/iphone_640x1136", PROPERTY_HINT_FILE, "png"), "")); // iPhone 5, 5s, SE
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/iphone_750x1334", PROPERTY_HINT_FILE, "png"), "")); // iPhone 6, 6s, 7
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/iphone_1242x2208", PROPERTY_HINT_FILE, "png"), "")); // iPhone 6 Plus, 6s Plus, 7 Plus
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/ipad_2048x2732", PROPERTY_HINT_FILE, "png"), "")); // 12.9-inch iPad Pro
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/ipad_1536x2048", PROPERTY_HINT_FILE, "png"), "")); // Other iPads
+ for (int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "png"), ""));
+ }
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
@@ -313,6 +328,7 @@ static const IconInfo icon_infos[] = {
{ "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", true },
{ "required_icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", false },
+ { "required_icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", false },
{ "optional_icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false },
@@ -380,23 +396,6 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
return OK;
}
-struct LoadingScreenInfo {
- const char *preset_key;
- const char *export_name;
-};
-
-static const LoadingScreenInfo loading_screen_infos[] = {
- { "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png" },
- { "landscape_launch_screens/ipad_2732x2048", "Default-Landscape-1366h@2x.png" },
- { "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png" },
-
- { "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png" },
- { "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png" },
- { "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png" },
- { "portrait_launch_screens/ipad_2048x2732", "Default-Portrait-1366h@2x.png" },
- { "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png" }
-};
-
Error EditorExportPlatformIOS::_export_loading_screens(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) {
DirAccess *da = DirAccess::open(p_dest_dir);
ERR_FAIL_COND_V(!da, ERR_CANT_OPEN);
@@ -404,12 +403,14 @@ Error EditorExportPlatformIOS::_export_loading_screens(const Ref<EditorExportPre
for (int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
LoadingScreenInfo info = loading_screen_infos[i];
String loading_screen_file = p_preset->get(info.preset_key);
- Error err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
- if (err) {
- memdelete(da);
- String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path: " + loading_screen_file;
- ERR_PRINT(err_str.utf8().get_data());
- return err;
+ if (loading_screen_file.size() > 0) {
+ Error err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
+ if (err) {
+ memdelete(da);
+ String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path: " + loading_screen_file;
+ ERR_PRINT(err_str.utf8().get_data());
+ return err;
+ }
}
}
memdelete(da);
@@ -887,7 +888,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
if (err)
return err;
- err = _export_loading_screens(p_preset, dest_dir + binary_name + "/");
+ err = _export_loading_screens(p_preset, dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/");
if (err)
return err;
diff --git a/platform/iphone/gl_view.h b/platform/iphone/gl_view.h
index 85376ebc08..0d101eb696 100644
--- a/platform/iphone/gl_view.h
+++ b/platform/iphone/gl_view.h
@@ -83,6 +83,8 @@
@property(strong, nonatomic) MPMoviePlayerController *moviePlayerController;
@property(strong, nonatomic) UIWindow *backgroundWindow;
+@property(nonatomic) UITextAutocorrectionType autocorrectionType;
+
- (void)startAnimation;
- (void)stopAnimation;
- (void)drawView;
diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm
index 69116c64c6..ff95cf6e5f 100644
--- a/platform/iphone/gl_view.mm
+++ b/platform/iphone/gl_view.mm
@@ -78,6 +78,16 @@ void _hide_keyboard() {
keyboard_text = "";
};
+Rect2 _get_ios_window_safe_area(float p_window_width, float p_window_height) {
+ UIEdgeInsets insets = UIEdgeInsetsMake(0, 0, 0, 0);
+ if (_instance != nil && [_instance respondsToSelector:@selector(safeAreaInsets)]) {
+ insets = [_instance safeAreaInsets];
+ }
+ ERR_FAIL_COND_V(insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0,
+ Rect2(0, 0, p_window_width, p_window_height));
+ return Rect2(insets.left, insets.top, p_window_width - insets.right - insets.left, p_window_height - insets.bottom - insets.top);
+}
+
bool _play_video(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) {
p_path = ProjectSettings::get_singleton()->globalize_path(p_path);
@@ -326,9 +336,7 @@ static void clear_touches() {
// Generate IDs for a framebuffer object and a color renderbuffer
UIScreen *mainscr = [UIScreen mainScreen];
printf("******** screen size %i, %i\n", (int)mainscr.currentMode.size.width, (int)mainscr.currentMode.size.height);
- float minPointSize = MIN(mainscr.bounds.size.width, mainscr.bounds.size.height);
- float minScreenSize = MIN(mainscr.currentMode.size.width, mainscr.currentMode.size.height);
- self.contentScaleFactor = minScreenSize / minPointSize;
+ self.contentScaleFactor = mainscr.nativeScale;
glGenFramebuffersOES(1, &viewFramebuffer);
glGenRenderbuffersOES(1, &viewRenderbuffer);
@@ -627,6 +635,7 @@ static void clear_touches() {
}
init_touches();
self.multipleTouchEnabled = YES;
+ self.autocorrectionType = UITextAutocorrectionTypeNo;
printf("******** adding observer for sound routing changes\n");
[[NSNotificationCenter defaultCenter]
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
index c4c59431f2..97ebc19335 100644
--- a/platform/iphone/os_iphone.cpp
+++ b/platform/iphone/os_iphone.cpp
@@ -504,6 +504,12 @@ Size2 OSIPhone::get_window_size() const {
return Vector2(video_mode.width, video_mode.height);
}
+extern Rect2 _get_ios_window_safe_area(float p_window_width, float p_window_height);
+
+Rect2 OSIPhone::get_window_safe_area() const {
+ return _get_ios_window_safe_area(video_mode.width, video_mode.height);
+}
+
bool OSIPhone::has_touchscreen_ui_hint() const {
return true;
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index 2e4458aeed..00d9efb01a 100644
--- a/platform/iphone/os_iphone.h
+++ b/platform/iphone/os_iphone.h
@@ -177,6 +177,7 @@ public:
virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
virtual Size2 get_window_size() const;
+ virtual Rect2 get_window_safe_area() const;
virtual bool has_touchscreen_ui_hint() const;
diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py
index be1866987a..a48cb872ee 100644
--- a/platform/javascript/detect.py
+++ b/platform/javascript/detect.py
@@ -139,13 +139,6 @@ def configure(env):
# This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1.
env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1'])
- # engine.js uses FS but is not currently evaluated by Emscripten, so export FS.
- # TODO: Getting rid of this export is desirable.
- extra_exports = [
- 'FS',
- ]
- env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS="%s"' % repr(extra_exports)])
-
env.Append(LINKFLAGS=['-s', 'INVOKE_RUN=0'])
# TODO: Reevaluate usage of this setting now that engine.js manages engine runtime.
diff --git a/platform/javascript/engine.js b/platform/javascript/engine.js
index e6fb48d0d2..e4839af433 100644
--- a/platform/javascript/engine.js
+++ b/platform/javascript/engine.js
@@ -1,3 +1,5 @@
+ exposedLibs['PATH'] = PATH;
+ exposedLibs['FS'] = FS;
return Module;
},
};
@@ -12,6 +14,13 @@
var loadingFiles = {};
+ function getPathLeaf(path) {
+
+ while (path.endsWith('/'))
+ path = path.slice(0, -1);
+ return path.slice(path.lastIndexOf('/') + 1);
+ }
+
function getBasePath(path) {
if (path.endsWith('/'))
@@ -23,14 +32,15 @@
function getBaseName(path) {
- path = getBasePath(path);
- return path.slice(path.lastIndexOf('/') + 1);
+ return getPathLeaf(getBasePath(path));
}
Engine = function Engine() {
this.rtenv = null;
+ var LIBS = {};
+
var initPromise = null;
var unloadAfterInit = true;
@@ -80,11 +90,11 @@
return new Promise(function(resolve, reject) {
rtenvProps.onRuntimeInitialized = resolve;
rtenvProps.onAbort = reject;
- rtenvProps.engine.rtenv = Engine.RuntimeEnvironment(rtenvProps);
+ rtenvProps.engine.rtenv = Engine.RuntimeEnvironment(rtenvProps, LIBS);
});
}
- this.preloadFile = function(pathOrBuffer, bufferFilename) {
+ this.preloadFile = function(pathOrBuffer, destPath) {
if (pathOrBuffer instanceof ArrayBuffer) {
pathOrBuffer = new Uint8Array(pathOrBuffer);
@@ -93,14 +103,14 @@
}
if (pathOrBuffer instanceof Uint8Array) {
preloadedFiles.push({
- name: bufferFilename,
+ path: destPath,
buffer: pathOrBuffer
});
return Promise.resolve();
} else if (typeof pathOrBuffer === 'string') {
return loadPromise(pathOrBuffer, preloadProgressTracker).then(function(xhr) {
preloadedFiles.push({
- name: pathOrBuffer,
+ path: destPath || pathOrBuffer,
buffer: xhr.response
});
});
@@ -119,7 +129,12 @@
this.startGame = function(mainPack) {
executableName = getBaseName(mainPack);
- return Promise.all([this.init(getBasePath(mainPack)), this.preloadFile(mainPack)]).then(
+ return Promise.all([
+ // Load from directory,
+ this.init(getBasePath(mainPack)),
+ // ...but write to root where the engine expects it.
+ this.preloadFile(mainPack, getPathLeaf(mainPack))
+ ]).then(
Function.prototype.apply.bind(synchronousStart, this, [])
);
};
@@ -163,7 +178,16 @@
this.rtenv.thisProgram = executableName || getBaseName(basePath);
preloadedFiles.forEach(function(file) {
- this.rtenv.FS.createDataFile('/', file.name, new Uint8Array(file.buffer), true, true, true);
+ var dir = LIBS.PATH.dirname(file.path);
+ try {
+ LIBS.FS.stat(dir);
+ } catch (e) {
+ if (e.code !== 'ENOENT') {
+ throw e;
+ }
+ LIBS.FS.mkdirTree(dir);
+ }
+ LIBS.FS.createDataFile('/', file.path, new Uint8Array(file.buffer), true, true, true);
}, this);
preloadedFiles = null;
diff --git a/platform/javascript/pre.js b/platform/javascript/pre.js
index 311aa44fda..02194bc75e 100644
--- a/platform/javascript/pre.js
+++ b/platform/javascript/pre.js
@@ -1,2 +1,2 @@
var Engine = {
- RuntimeEnvironment: function(Module) {
+ RuntimeEnvironment: function(Module, exposedLibs) {
diff --git a/platform/osx/godot_main_osx.mm b/platform/osx/godot_main_osx.mm
index 6ccbaf896b..64116fa1e0 100644
--- a/platform/osx/godot_main_osx.mm
+++ b/platform/osx/godot_main_osx.mm
@@ -101,5 +101,5 @@ int main(int argc, char **argv) {
Main::cleanup();
- return 0;
+ return os.get_exit_code();
};
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index ef23d61141..fbefd41bb7 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -192,6 +192,7 @@ static Vector2 get_mouse_pos(NSEvent *event) {
// Note: called before main loop init!
char *utfs = strdup([filename UTF8String]);
OS_OSX::singleton->open_with_filename.parse_utf8(utfs);
+ free(utfs);
return YES;
}
@@ -838,6 +839,108 @@ static int translateKey(unsigned int key) {
return table[key];
}
+struct _KeyCodeMap {
+ UniChar kchar;
+ int kcode;
+};
+
+static const _KeyCodeMap _keycodes[55] = {
+ { '`', KEY_QUOTELEFT },
+ { '~', KEY_ASCIITILDE },
+ { '0', KEY_KP_0 },
+ { '1', KEY_KP_1 },
+ { '2', KEY_KP_2 },
+ { '3', KEY_KP_3 },
+ { '4', KEY_KP_4 },
+ { '5', KEY_KP_5 },
+ { '6', KEY_KP_6 },
+ { '7', KEY_KP_7 },
+ { '8', KEY_KP_8 },
+ { '9', KEY_KP_9 },
+ { '-', KEY_MINUS },
+ { '_', KEY_UNDERSCORE },
+ { '=', KEY_EQUAL },
+ { '+', KEY_PLUS },
+ { 'q', KEY_Q },
+ { 'w', KEY_W },
+ { 'e', KEY_E },
+ { 'r', KEY_R },
+ { 't', KEY_T },
+ { 'y', KEY_Y },
+ { 'u', KEY_U },
+ { 'i', KEY_I },
+ { 'o', KEY_O },
+ { 'p', KEY_P },
+ { '[', KEY_BRACERIGHT },
+ { ']', KEY_BRACELEFT },
+ { '{', KEY_BRACERIGHT },
+ { '}', KEY_BRACELEFT },
+ { 'a', KEY_A },
+ { 's', KEY_S },
+ { 'd', KEY_D },
+ { 'f', KEY_F },
+ { 'g', KEY_G },
+ { 'h', KEY_H },
+ { 'j', KEY_J },
+ { 'k', KEY_K },
+ { 'l', KEY_L },
+ { ';', KEY_SEMICOLON },
+ { ':', KEY_COLON },
+ { '\'', KEY_APOSTROPHE },
+ { '\"', KEY_QUOTEDBL },
+ { '\\', KEY_BACKSLASH },
+ { '#', KEY_NUMBERSIGN },
+ { 'z', KEY_Z },
+ { 'x', KEY_X },
+ { 'c', KEY_C },
+ { 'v', KEY_V },
+ { 'b', KEY_B },
+ { 'n', KEY_N },
+ { 'm', KEY_M },
+ { ',', KEY_COMMA },
+ { '.', KEY_PERIOD },
+ { '/', KEY_SLASH }
+};
+
+static int remapKey(unsigned int key) {
+
+ TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
+ if (!currentKeyboard)
+ return translateKey(key);
+
+ CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
+ if (!layoutData)
+ return nil;
+
+ const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
+
+ UInt32 keysDown = 0;
+ UniChar chars[4];
+ UniCharCount realLength;
+
+ OSStatus err = UCKeyTranslate(keyboardLayout,
+ key,
+ kUCKeyActionDisplay,
+ 0,
+ LMGetKbdType(),
+ kUCKeyTranslateNoDeadKeysBit,
+ &keysDown,
+ sizeof(chars) / sizeof(chars[0]),
+ &realLength,
+ chars);
+
+ if (err != noErr) {
+ return translateKey(key);
+ }
+
+ for (unsigned int i = 0; i < 55; i++) {
+ if (_keycodes[i].kchar == chars[0]) {
+ return _keycodes[i].kcode;
+ }
+ }
+ return translateKey(key);
+}
+
- (void)keyDown:(NSEvent *)event {
//disable raw input in IME mode
@@ -847,7 +950,7 @@ static int translateKey(unsigned int key) {
ke.osx_state = [event modifierFlags];
ke.pressed = true;
ke.echo = [event isARepeat];
- ke.scancode = latin_keyboard_keycode_convert(translateKey([event keyCode]));
+ ke.scancode = remapKey([event keyCode]);
ke.unicode = 0;
push_to_key_event_buffer(ke);
@@ -900,7 +1003,7 @@ static int translateKey(unsigned int key) {
}
ke.osx_state = mod;
- ke.scancode = latin_keyboard_keycode_convert(translateKey(key));
+ ke.scancode = remapKey(key);
ke.unicode = 0;
push_to_key_event_buffer(ke);
@@ -916,7 +1019,7 @@ static int translateKey(unsigned int key) {
ke.osx_state = [event modifierFlags];
ke.pressed = false;
ke.echo = false;
- ke.scancode = latin_keyboard_keycode_convert(translateKey([event keyCode]));
+ ke.scancode = remapKey([event keyCode]);
ke.unicode = 0;
push_to_key_event_buffer(ke);
@@ -1371,6 +1474,11 @@ void OS_OSX::set_cursor_shape(CursorShape p_shape) {
if (cursor_shape == p_shape)
return;
+ if (mouse_mode != MOUSE_MODE_VISIBLE) {
+ cursor_shape = p_shape;
+ return;
+ }
+
if (cursors[p_shape] != NULL) {
[cursors[p_shape] set];
} else {
@@ -1404,9 +1512,7 @@ void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c
Ref<Texture> texture = p_cursor;
Ref<Image> image = texture->get_data();
- int image_size = 32 * 32;
-
- ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32);
+ ERR_FAIL_COND(texture->get_width() > 256 || texture->get_height() > 256);
NSBitmapImageRep *imgrep = [[[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 13fe781ff3..9c37b65d77 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -1299,7 +1299,9 @@ void OS_Windows::set_mouse_mode(MouseMode p_mode) {
if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_HIDDEN) {
hCursor = SetCursor(NULL);
} else {
- SetCursor(hCursor);
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ set_cursor_shape(c);
}
}
@@ -1863,6 +1865,11 @@ void OS_Windows::set_cursor_shape(CursorShape p_shape) {
if (cursor_shape == p_shape)
return;
+ if (mouse_mode != MOUSE_MODE_VISIBLE) {
+ cursor_shape = p_shape;
+ return;
+ }
+
static const LPCTSTR win_cursors[CURSOR_MAX] = {
IDC_ARROW,
IDC_IBEAM,
@@ -1888,6 +1895,7 @@ void OS_Windows::set_cursor_shape(CursorShape p_shape) {
} else {
SetCursor(LoadCursor(hInstance, win_cursors[p_shape]));
}
+
cursor_shape = p_shape;
}
@@ -1896,26 +1904,25 @@ void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shap
Ref<Texture> texture = p_cursor;
Ref<Image> image = texture->get_data();
- UINT image_size = 32 * 32;
+ UINT image_size = texture->get_width() * texture->get_height();
UINT size = sizeof(UINT) * image_size;
- ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32);
+ ERR_FAIL_COND(texture->get_width() > 256 || texture->get_height() > 256);
// Create the BITMAP with alpha channel
COLORREF *buffer = (COLORREF *)malloc(sizeof(COLORREF) * image_size);
image->lock();
for (UINT index = 0; index < image_size; index++) {
- int column_index = floor(index / 32);
- int row_index = index % 32;
+ int row_index = floor(index / texture->get_width());
+ int column_index = index % texture->get_width();
- Color pcColor = image->get_pixel(row_index, column_index);
- *(buffer + index) = image->get_pixel(row_index, column_index).to_argb32();
+ *(buffer + index) = image->get_pixel(column_index, row_index).to_argb32();
}
image->unlock();
// Using 4 channels, so 4 * 8 bits
- HBITMAP bitmap = CreateBitmap(32, 32, 1, 4 * 8, buffer);
+ HBITMAP bitmap = CreateBitmap(texture->get_width(), texture->get_height(), 1, 4 * 8, buffer);
COLORREF clrTransparent = -1;
// Create the AND and XOR masks for the bitmap
diff --git a/platform/x11/detect.py b/platform/x11/detect.py
index da2b0701b6..5820a926e9 100644
--- a/platform/x11/detect.py
+++ b/platform/x11/detect.py
@@ -234,10 +234,10 @@ def configure(env):
print("ALSA libraries not found, disabling driver")
if env['pulseaudio']:
- if (os.system("pkg-config --exists libpulse-simple") == 0): # 0 means found
+ if (os.system("pkg-config --exists libpulse") == 0): # 0 means found
print("Enabling PulseAudio")
env.Append(CPPFLAGS=["-DPULSEAUDIO_ENABLED"])
- env.ParseConfig('pkg-config --cflags --libs libpulse-simple')
+ env.ParseConfig('pkg-config --cflags --libs libpulse')
else:
print("PulseAudio development libraries not found, disabling driver")
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp
index c06c7516d0..1928800d8c 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -634,7 +634,7 @@ void OS_X11::set_mouse_mode(MouseMode p_mode) {
bool showCursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED);
if (showCursor) {
- XUndefineCursor(x11_display, x11_window); // show cursor
+ XDefineCursor(x11_display, x11_window, cursors[current_cursor]); // show cursor
} else {
XDefineCursor(x11_display, x11_window, null_cursor); // hide cursor
}
@@ -2375,11 +2375,11 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c
Ref<Texture> texture = p_cursor;
Ref<Image> image = texture->get_data();
- ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32);
+ ERR_FAIL_COND(texture->get_width() > 256 || texture->get_height() > 256);
// Create the cursor structure
XcursorImage *cursor_image = XcursorImageCreate(texture->get_width(), texture->get_height());
- XcursorUInt image_size = 32 * 32;
+ XcursorUInt image_size = texture->get_width() * texture->get_height();
XcursorDim size = sizeof(XcursorPixel) * image_size;
cursor_image->version = 1;
@@ -2393,10 +2393,10 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c
image->lock();
for (XcursorPixel index = 0; index < image_size; index++) {
- int column_index = floor(index / 32);
- int row_index = index % 32;
+ int row_index = floor(index / texture->get_width());
+ int column_index = index % texture->get_width();
- *(cursor_image->pixels + index) = image->get_pixel(row_index, column_index).to_argb32();
+ *(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32();
}
image->unlock();
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index 0e7c316ff2..60368816a9 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -420,7 +420,7 @@ void CanvasItem::_enter_canvas() {
RID canvas;
if (canvas_layer)
- canvas = canvas_layer->get_world_2d()->get_canvas();
+ canvas = canvas_layer->get_canvas();
else
canvas = get_viewport()->find_world_2d()->get_canvas();
@@ -829,6 +829,12 @@ float CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const
void CanvasItem::_notify_transform(CanvasItem *p_node) {
+ /* This check exists to avoid re-propagating the transform
+ * notification down the tree on dirty nodes. It provides
+ * optimization by avoiding redundancy (nodes are dirty, will get the
+ * notification anyway).
+ */
+
if (/*p_node->xform_change.in_list() &&*/ p_node->global_invalid) {
return; //nothing to do
}
@@ -862,7 +868,7 @@ RID CanvasItem::get_canvas() const {
ERR_FAIL_COND_V(!is_inside_tree(), RID());
if (canvas_layer)
- return canvas_layer->get_world_2d()->get_canvas();
+ return canvas_layer->get_canvas();
else
return get_viewport()->find_world_2d()->get_canvas();
}
@@ -883,9 +889,7 @@ Ref<World2D> CanvasItem::get_world_2d() const {
CanvasItem *tl = get_toplevel();
- if (tl->canvas_layer) {
- return tl->canvas_layer->get_world_2d();
- } else if (tl->get_viewport()) {
+ if (tl->get_viewport()) {
return tl->get_viewport()->find_world_2d();
} else {
return Ref<World2D>();
diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
index 050f98b02b..584c2f2c85 100644
--- a/scene/2d/parallax_layer.cpp
+++ b/scene/2d/parallax_layer.cpp
@@ -72,7 +72,7 @@ void ParallaxLayer::_update_mirroring() {
ParallaxBackground *pb = Object::cast_to<ParallaxBackground>(get_parent());
if (pb) {
- RID c = pb->get_world_2d()->get_canvas();
+ RID c = pb->get_canvas();
RID ci = get_canvas_item();
Point2 mirrorScale = mirroring * get_scale();
VisualServer::get_singleton()->canvas_set_item_mirroring(c, ci, mirrorScale);
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index 5a563143ad..255d2d38d5 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -101,7 +101,7 @@ void RayCast2D::set_enabled(bool p_enabled) {
enabled = p_enabled;
if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint())
- set_physics_process(p_enabled);
+ set_physics_process_internal(p_enabled);
if (!p_enabled)
collided = false;
}
@@ -141,9 +141,9 @@ void RayCast2D::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
if (enabled && !Engine::get_singleton()->is_editor_hint())
- set_physics_process(true);
+ set_physics_process_internal(true);
else
- set_physics_process(false);
+ set_physics_process_internal(false);
if (Object::cast_to<CollisionObject2D>(get_parent())) {
if (exclude_parent_body)
@@ -155,7 +155,7 @@ void RayCast2D::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
if (enabled)
- set_physics_process(false);
+ set_physics_process_internal(false);
} break;
@@ -183,7 +183,7 @@ void RayCast2D::_notification(int p_what) {
} break;
- case NOTIFICATION_PHYSICS_PROCESS: {
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (!enabled)
break;
diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp
index 8617bbc2f6..693b416f6d 100644
--- a/scene/3d/particles.cpp
+++ b/scene/3d/particles.cpp
@@ -849,9 +849,9 @@ void ParticlesMaterial::_update_shader() {
code += " vec4(1.250, -1.050, -0.203, 0.0),\n";
code += " vec4(0.000, 0.000, 0.000, 0.0)) * hue_rot_s;\n";
if (color_ramp.is_valid()) {
- code += " COLOR = textureLod(color_ramp,vec2(CUSTOM.y,0.0),0.0) * hue_rot_mat;\n";
+ code += " COLOR = hue_rot_mat * textureLod(color_ramp,vec2(CUSTOM.y,0.0),0.0);\n";
} else {
- code += " COLOR = color_value * hue_rot_mat;\n";
+ code += " COLOR = hue_rot_mat * color_value;\n";
}
if (emission_color_texture.is_valid() && emission_shape >= EMISSION_SHAPE_POINTS) {
code += " COLOR*= texelFetch(emission_texture_color,emission_tex_ofs,0);\n";
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index ff4a807de0..9aac391d80 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -781,7 +781,7 @@ String RigidBody::get_configuration_warning() const {
String warning = CollisionObject::get_configuration_warning();
- if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(0).length() - 1.0) > 0.05)) {
+ if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) {
if (warning != String()) {
warning += "\n";
}
diff --git a/scene/3d/ray_cast.cpp b/scene/3d/ray_cast.cpp
index dd5ae8a999..7f83e2c3ea 100644
--- a/scene/3d/ray_cast.cpp
+++ b/scene/3d/ray_cast.cpp
@@ -103,7 +103,7 @@ void RayCast::set_enabled(bool p_enabled) {
enabled = p_enabled;
if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint())
- set_physics_process(p_enabled);
+ set_physics_process_internal(p_enabled);
if (!p_enabled)
collided = false;
@@ -150,12 +150,12 @@ void RayCast::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
if (enabled && !Engine::get_singleton()->is_editor_hint()) {
- set_physics_process(true);
+ set_physics_process_internal(true);
if (get_tree()->is_debugging_collisions_hint())
_update_debug_shape();
} else
- set_physics_process(false);
+ set_physics_process_internal(false);
if (Object::cast_to<CollisionObject>(get_parent())) {
if (exclude_parent_body)
@@ -168,14 +168,14 @@ void RayCast::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
if (enabled) {
- set_physics_process(false);
+ set_physics_process_internal(false);
}
if (debug_shape)
_clear_debug_shape();
} break;
- case NOTIFICATION_PHYSICS_PROCESS: {
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (!enabled)
break;
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 232855c978..bc44c91f64 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -366,11 +366,17 @@ void Sprite3D::_draw() {
final_rect.position * pixel_size,
};
+
+ // Properly setup UVs for impostor textures (AtlasTexture).
+ RID texture_rid = texture->get_rid();
+ Vector2 src_tsize = Vector2(
+ VS::get_singleton()->texture_get_width(texture_rid),
+ VS::get_singleton()->texture_get_height(texture_rid));
Vector2 uvs[4] = {
- final_src_rect.position / tsize,
- (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / tsize,
- (final_src_rect.position + final_src_rect.size) / tsize,
- (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / tsize,
+ final_src_rect.position / src_tsize,
+ (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / src_tsize,
+ (final_src_rect.position + final_src_rect.size) / src_tsize,
+ (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / src_tsize,
};
if (is_flipped_h()) {
@@ -649,18 +655,23 @@ void AnimatedSprite3D::_draw() {
float pixel_size = get_pixel_size();
Vector2 vertices[4] = {
-
(final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size,
(final_rect.position + final_rect.size) * pixel_size,
(final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size,
final_rect.position * pixel_size,
};
+
+ // Properly setup UVs for impostor textures (AtlasTexture).
+ RID texture_rid = texture->get_rid();
+ Vector2 src_tsize = Vector2(
+ VS::get_singleton()->texture_get_width(texture_rid),
+ VS::get_singleton()->texture_get_height(texture_rid));
Vector2 uvs[4] = {
- final_src_rect.position / tsize,
- (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / tsize,
- (final_src_rect.position + final_src_rect.size) / tsize,
- (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / tsize,
+ final_src_rect.position / src_tsize,
+ (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / src_tsize,
+ (final_src_rect.position + final_src_rect.size) / src_tsize,
+ (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / src_tsize,
};
if (is_flipped_h()) {
diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp
index ed3bde9504..b72665aa2b 100644
--- a/scene/3d/vehicle_body.cpp
+++ b/scene/3d/vehicle_body.cpp
@@ -524,7 +524,7 @@ void VehicleBody::_update_suspension(PhysicsDirectBodyState *s) {
//bilateral constraint between two dynamic objects
void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1,
- PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse) {
+ PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, real_t p_rollInfluence) {
real_t normalLenSqr = normal.length_squared();
//ERR_FAIL_COND( normalLenSqr < real_t(1.1));
@@ -582,8 +582,12 @@ void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vec
rel_vel = normal.dot(vel);
- //TODO: move this into proper structure
- real_t contactDamping = real_t(0.4);
+ // !BAS! We had this set to 0.4, in bullet its 0.2
+ // real_t contactDamping = real_t(0.2);
+
+ // !BAS! But seeing we apply this frame by frame, makes more sense to me to make this time based
+ // keeping in mind our anti roll factor
+ real_t contactDamping = s->get_step() / p_rollInfluence;
#define ONLY_USE_LINEAR_MASS
#ifdef ONLY_USE_LINEAR_MASS
real_t massTerm = real_t(1.) / ((1.0 / mass) + b2invmass);
@@ -704,7 +708,7 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) {
_resolve_single_bilateral(s, wheelInfo.m_raycastInfo.m_contactPointWS,
wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS,
- m_axle[i], m_sideImpulse[i]);
+ m_axle[i], m_sideImpulse[i], wheelInfo.m_rollInfluence);
m_sideImpulse[i] *= sideFrictionStiffness2;
}
diff --git a/scene/3d/vehicle_body.h b/scene/3d/vehicle_body.h
index 7810a42e8a..1ac3693cc4 100644
--- a/scene/3d/vehicle_body.h
+++ b/scene/3d/vehicle_body.h
@@ -168,7 +168,7 @@ class VehicleBody : public RigidBody {
btVehicleWheelContactPoint(PhysicsDirectBodyState *s, PhysicsBody *body1, const Vector3 &frictionPosWorld, const Vector3 &frictionDirectionWorld, real_t maxImpulse);
};
- void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse);
+ void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, real_t p_rollInfluence);
real_t _calc_rolling_friction(btVehicleWheelContactPoint &contactPoint);
void _update_friction(PhysicsDirectBodyState *s);
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 04e7d5cc10..63580bcae6 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -182,8 +182,8 @@ void AnimationPlayer::_notification(int p_what) {
if (!processing) {
//make sure that a previous process state was not saved
//only process if "processing" is set
- set_physics_process(false);
- set_process(false);
+ set_physics_process_internal(false);
+ set_process_internal(false);
}
//_set_process(false);
clear_caches();
@@ -590,8 +590,8 @@ void AnimationPlayer::_animation_update_transforms() {
Transform t;
t.origin = nc->loc_accum;
- t.basis = nc->rot_accum;
t.basis.scale(nc->scale_accum);
+ t.basis.rotate(nc->rot_accum.get_euler());
if (nc->skeleton && nc->bone_idx >= 0) {
@@ -1025,6 +1025,13 @@ float AnimationPlayer::get_speed_scale() const {
return speed_scale;
}
+float AnimationPlayer::get_playing_speed() const {
+
+ if (!playing) {
+ return 0;
+ }
+ return speed_scale * playback.current.speed_scale;
+}
void AnimationPlayer::seek(float p_time, bool p_update) {
@@ -1316,6 +1323,7 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_speed_scale", "speed"), &AnimationPlayer::set_speed_scale);
ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimationPlayer::get_speed_scale);
+ ClassDB::bind_method(D_METHOD("get_playing_speed"), &AnimationPlayer::get_playing_speed);
ClassDB::bind_method(D_METHOD("set_autoplay", "name"), &AnimationPlayer::set_autoplay);
ClassDB::bind_method(D_METHOD("get_autoplay"), &AnimationPlayer::get_autoplay);
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index ef758bac44..af2022ddac 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -293,6 +293,7 @@ public:
void set_speed_scale(float p_speed);
float get_speed_scale() const;
+ float get_playing_speed() const;
void set_autoplay(const String &p_name);
String get_autoplay() const;
diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp
index 89f0e43a86..e811b7a7b3 100644
--- a/scene/animation/animation_tree_player.cpp
+++ b/scene/animation/animation_tree_player.cpp
@@ -895,13 +895,13 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
}
Transform xform;
- xform.basis = t.rot;
xform.origin = t.loc;
t.scale.x += 1.0;
t.scale.y += 1.0;
t.scale.z += 1.0;
xform.basis.scale(t.scale);
+ xform.basis.rotate(t.rot.get_euler());
if (t.bone_idx >= 0) {
if (t.skeleton)
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 562dd155f9..dbfb96697d 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -252,7 +252,7 @@ void BaseButton::_notification(int p_what) {
status.hovering = false;
update();
}
- if (p_what == NOTIFICATION_DRAG_BEGIN) {
+ if (p_what == NOTIFICATION_DRAG_BEGIN || p_what == NOTIFICATION_SCROLL_BEGIN) {
if (status.press_attempt) {
status.press_attempt = false;
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 9ad9df8b25..b7c1d35fd7 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -1289,22 +1289,24 @@ void Control::_size_changed() {
Size2 minimum_size = get_combined_minimum_size();
- if (data.h_grow == GROW_DIRECTION_BEGIN) {
- if (minimum_size.width > new_size_cache.width) {
- new_pos_cache.x = new_pos_cache.x + new_size_cache.width - minimum_size.width;
- new_size_cache.width = minimum_size.width;
+ if (minimum_size.width > new_size_cache.width) {
+ if (data.h_grow == GROW_DIRECTION_BEGIN) {
+ new_pos_cache.x += new_size_cache.width - minimum_size.width;
+ } else if (data.h_grow == GROW_DIRECTION_BOTH) {
+ new_pos_cache.x += 0.5 * (new_size_cache.width - minimum_size.width);
}
- } else {
- new_size_cache.width = MAX(minimum_size.width, new_size_cache.width);
+
+ new_size_cache.width = minimum_size.width;
}
- if (data.v_grow == GROW_DIRECTION_BEGIN) {
- if (minimum_size.height > new_size_cache.height) {
- new_pos_cache.y = new_pos_cache.y + new_size_cache.height - minimum_size.height;
- new_size_cache.height = minimum_size.height;
+ if (minimum_size.height > new_size_cache.height) {
+ if (data.v_grow == GROW_DIRECTION_BEGIN) {
+ new_pos_cache.y += new_size_cache.height - minimum_size.height;
+ } else if (data.v_grow == GROW_DIRECTION_BOTH) {
+ new_pos_cache.y += 0.5 * (new_size_cache.height - minimum_size.height);
}
- } else {
- new_size_cache.height = MAX(minimum_size.height, new_size_cache.height);
+
+ new_size_cache.height = minimum_size.height;
}
// We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot()
@@ -2846,8 +2848,8 @@ void Control::_bind_methods() {
ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "margin_bottom", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_BOTTOM);
ADD_GROUP("Grow Direction", "grow_");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Begin,End"), "set_h_grow_direction", "get_h_grow_direction");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End"), "set_v_grow_direction", "get_v_grow_direction");
+ ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_h_grow_direction", "get_h_grow_direction");
+ ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction");
ADD_GROUP("Rect", "rect_");
ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_position", "get_position");
@@ -2894,6 +2896,8 @@ void Control::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_FOCUS_EXIT);
BIND_CONSTANT(NOTIFICATION_THEME_CHANGED);
BIND_CONSTANT(NOTIFICATION_MODAL_CLOSE);
+ BIND_CONSTANT(NOTIFICATION_SCROLL_BEGIN);
+ BIND_CONSTANT(NOTIFICATION_SCROLL_END);
BIND_ENUM_CONSTANT(CURSOR_ARROW);
BIND_ENUM_CONSTANT(CURSOR_IBEAM);
@@ -2947,6 +2951,7 @@ void Control::_bind_methods() {
BIND_ENUM_CONSTANT(GROW_DIRECTION_BEGIN);
BIND_ENUM_CONSTANT(GROW_DIRECTION_END);
+ BIND_ENUM_CONSTANT(GROW_DIRECTION_BOTH);
BIND_ENUM_CONSTANT(ANCHOR_BEGIN);
BIND_ENUM_CONSTANT(ANCHOR_END);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 839b9eb87b..b5453e60f5 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -60,7 +60,8 @@ public:
enum GrowDirection {
GROW_DIRECTION_BEGIN,
- GROW_DIRECTION_END
+ GROW_DIRECTION_END,
+ GROW_DIRECTION_BOTH
};
enum FocusMode {
@@ -271,6 +272,8 @@ public:
NOTIFICATION_FOCUS_EXIT = 44,
NOTIFICATION_THEME_CHANGED = 45,
NOTIFICATION_MODAL_CLOSE = 46,
+ NOTIFICATION_SCROLL_BEGIN = 47,
+ NOTIFICATION_SCROLL_END = 48,
};
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index cc17e6bcd8..ecd98f054d 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -37,6 +37,7 @@ void ItemList::add_item(const String &p_item, const Ref<Texture> &p_texture, boo
Item item;
item.icon = p_texture;
item.icon_region = Rect2i();
+ item.icon_modulate = Color(1, 1, 1, 1);
item.text = p_item;
item.selectable = p_selectable;
item.selected = false;
@@ -54,6 +55,7 @@ void ItemList::add_icon_item(const Ref<Texture> &p_item, bool p_selectable) {
Item item;
item.icon = p_item;
item.icon_region = Rect2i();
+ item.icon_modulate = Color(1, 1, 1, 1);
//item.text=p_item;
item.selectable = p_selectable;
item.selected = false;
@@ -138,6 +140,21 @@ Rect2 ItemList::get_item_icon_region(int p_idx) const {
return items[p_idx].icon_region;
}
+void ItemList::set_item_icon_modulate(int p_idx, const Color &p_modulate) {
+
+ ERR_FAIL_INDEX(p_idx, items.size());
+
+ items[p_idx].icon_modulate = p_modulate;
+ update();
+}
+
+Color ItemList::get_item_icon_modulate(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx, items.size(), Color());
+
+ return items[p_idx].icon_modulate;
+}
+
void ItemList::set_item_custom_bg_color(int p_idx, const Color &p_custom_bg_color) {
ERR_FAIL_INDEX(p_idx, items.size());
@@ -1045,7 +1062,7 @@ void ItemList::_notification(int p_what) {
draw_rect.size = adj.size;
}
- Color modulate = Color(1, 1, 1, 1);
+ Color modulate = items[i].icon_modulate;
if (items[i].disabled)
modulate.a *= 0.5;
@@ -1389,6 +1406,9 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_icon_region", "idx", "rect"), &ItemList::set_item_icon_region);
ClassDB::bind_method(D_METHOD("get_item_icon_region", "idx"), &ItemList::get_item_icon_region);
+ ClassDB::bind_method(D_METHOD("set_item_icon_modulate", "idx", "modulate"), &ItemList::set_item_icon_modulate);
+ ClassDB::bind_method(D_METHOD("get_item_icon_modulate", "idx"), &ItemList::get_item_icon_modulate);
+
ClassDB::bind_method(D_METHOD("set_item_selectable", "idx", "selectable"), &ItemList::set_item_selectable);
ClassDB::bind_method(D_METHOD("is_item_selectable", "idx"), &ItemList::is_item_selectable);
@@ -1414,7 +1434,7 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_selected", "idx"), &ItemList::is_selected);
ClassDB::bind_method(D_METHOD("get_selected_items"), &ItemList::get_selected_items);
- ClassDB::bind_method(D_METHOD("move_item", "p_from_idx", "p_to_idx"), &ItemList::move_item);
+ ClassDB::bind_method(D_METHOD("move_item", "from_idx", "to_idx"), &ItemList::move_item);
ClassDB::bind_method(D_METHOD("get_item_count"), &ItemList::get_item_count);
ClassDB::bind_method(D_METHOD("remove_item", "idx"), &ItemList::remove_item);
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index 0fa0dd415b..58771c1777 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -54,6 +54,7 @@ private:
Ref<Texture> icon;
Rect2i icon_region;
+ Color icon_modulate;
Ref<Texture> tag_icon;
String text;
bool selectable;
@@ -135,6 +136,9 @@ public:
void set_item_icon_region(int p_idx, const Rect2 &p_region);
Rect2 get_item_icon_region(int p_idx) const;
+ void set_item_icon_modulate(int p_idx, const Color &p_modulate);
+ Color get_item_icon_modulate(int p_idx) const;
+
void set_item_selectable(int p_idx, bool p_selectable);
bool is_item_selectable(int p_idx) const;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 5c0e8fefc7..09c6a3b32c 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -602,6 +602,7 @@ void LineEdit::_notification(int p_what) {
}
int x_ofs = 0;
+ int cached_text_width = text.empty() ? cached_placeholder_width : cached_width;
switch (align) {
@@ -615,15 +616,15 @@ void LineEdit::_notification(int p_what) {
if (window_pos != 0)
x_ofs = style->get_offset().x;
else
- x_ofs = int(size.width - (cached_width)) / 2;
+ x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - (cached_text_width)) / 2);
} break;
case ALIGN_RIGHT: {
- x_ofs = int(size.width - style->get_offset().x - (cached_width));
+ x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_text_width)));
} break;
}
- int ofs_max = width - style->get_minimum_size().width;
+ int ofs_max = width - style->get_margin(MARGIN_RIGHT);
int char_ofs = window_pos;
int y_area = height - style->get_minimum_size().height;
@@ -881,7 +882,7 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) {
} break;
case ALIGN_RIGHT: {
- pixel_ofs = int(size.width - style->get_offset().x - (cached_width));
+ pixel_ofs = int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_width));
} break;
}
@@ -1014,6 +1015,15 @@ String LineEdit::get_text() const {
void LineEdit::set_placeholder(String p_text) {
placeholder = tr(p_text);
+ if ((max_length <= 0) || (placeholder.length() <= max_length)) {
+ Ref<Font> font = get_font("font");
+ cached_placeholder_width = 0;
+ if (font != NULL) {
+ for (int i = 0; i < placeholder.length(); i++) {
+ cached_placeholder_width += font->get_char_size(placeholder[i]).width;
+ }
+ }
+ }
update();
}
@@ -1127,6 +1137,7 @@ void LineEdit::clear_internal() {
_clear_undo_stack();
cached_width = 0;
+ cached_placeholder_width = 0;
cursor_pos = 0;
window_pos = 0;
undo_text = "";
@@ -1468,6 +1479,7 @@ LineEdit::LineEdit() {
_create_undo_state();
align = ALIGN_LEFT;
cached_width = 0;
+ cached_placeholder_width = 0;
cursor_pos = 0;
window_pos = 0;
window_has_focus = true;
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index e3ad3b17f1..c60ea36cc1 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -84,6 +84,7 @@ private:
int max_length; // 0 for no maximum
int cached_width;
+ int cached_placeholder_width;
struct Selection {
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index 2e74faa61d..87cf4dc334 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -59,7 +59,6 @@ void MenuButton::pressed() {
popup->set_size(Size2(size.width, 0));
popup->set_parent_rect(Rect2(Point2(gp - popup->get_global_position()), get_size()));
popup->popup();
- popup->set_invalidate_click_until_motion();
}
void MenuButton::_gui_input(Ref<InputEvent> p_event) {
@@ -109,7 +108,6 @@ MenuButton::MenuButton() {
add_child(popup);
popup->set_as_toplevel(true);
popup->set_pass_on_modal_close_click(false);
- connect("button_up", popup, "call_deferred", make_binds("grab_click_focus"));
set_process_unhandled_key_input(true);
set_action_mode(ACTION_MODE_BUTTON_PRESS);
}
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 6e53f11b99..a9402d6404 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -112,13 +112,13 @@ void OptionButton::pressed() {
void OptionButton::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID) {
- popup->add_icon_check_item(p_icon, p_label, p_ID);
+ popup->add_icon_radio_check_item(p_icon, p_label, p_ID);
if (popup->get_item_count() == 1)
select(0);
}
void OptionButton::add_item(const String &p_label, int p_ID) {
- popup->add_check_item(p_label, p_ID);
+ popup->add_radio_check_item(p_label, p_ID);
if (popup->get_item_count() == 1)
select(0);
}
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 747230e69f..fd2466407e 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -55,7 +55,7 @@ Size2 PopupMenu::get_minimum_size() const {
float max_w = 0;
int font_h = font->get_height();
- int check_w = get_icon("checked")->get_width();
+ int check_w = MAX(get_icon("checked")->get_width(), get_icon("radio_checked")->get_width());
int accel_max_w = 0;
for (int i = 0; i < items.size(); i++) {
@@ -74,7 +74,7 @@ Size2 PopupMenu::get_minimum_size() const {
size.width += items[i].h_ofs;
- if (items[i].checkable) {
+ if (items[i].checkable_type) {
size.width += check_w + hseparation;
}
@@ -284,7 +284,8 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
if (b->is_pressed())
return;
- switch (b->get_button_index()) {
+ int button_idx = b->get_button_index();
+ switch (button_idx) {
case BUTTON_WHEEL_DOWN: {
@@ -298,30 +299,37 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
_scroll(b->get_factor(), b->get_position());
}
} break;
- case BUTTON_LEFT: {
-
- int over = _get_mouse_over(b->get_position());
-
- if (invalidated_click) {
- invalidated_click = false;
- break;
- }
- if (over < 0) {
- hide();
- break; //non-activable
- }
-
- if (items[over].separator || items[over].disabled)
- break;
-
- if (items[over].submenu != "") {
-
- _activate_submenu(over);
- return;
+ default: {
+ // Allow activating item by releasing the LMB or any that was down when the popup appeared
+ if (button_idx == BUTTON_LEFT || (initial_button_mask & (1 << (button_idx - 1)))) {
+
+ bool was_during_grabbed_click = during_grabbed_click;
+ during_grabbed_click = false;
+
+ int over = _get_mouse_over(b->get_position());
+
+ if (invalidated_click) {
+ invalidated_click = false;
+ break;
+ }
+ if (over < 0) {
+ if (!was_during_grabbed_click) {
+ hide();
+ }
+ break; //non-activable
+ }
+
+ if (items[over].separator || items[over].disabled)
+ break;
+
+ if (items[over].submenu != "") {
+
+ _activate_submenu(over);
+ return;
+ }
+ activate_item(over);
}
- activate_item(over);
-
- } break;
+ }
}
//update();
@@ -408,8 +416,9 @@ void PopupMenu::_notification(int p_what) {
Ref<StyleBox> style = get_stylebox("panel");
Ref<StyleBox> hover = get_stylebox("hover");
Ref<Font> font = get_font("font");
- Ref<Texture> check = get_icon("checked");
- Ref<Texture> uncheck = get_icon("unchecked");
+ // In Item::checkable_type enum order (less the non-checkable member)
+ Ref<Texture> check[] = { get_icon("checked"), get_icon("radio_checked") };
+ Ref<Texture> uncheck[] = { get_icon("unchecked"), get_icon("radio_unchecked") };
Ref<Texture> submenu = get_icon("submenu");
Ref<StyleBox> separator = get_stylebox("separator");
@@ -452,14 +461,10 @@ void PopupMenu::_notification(int p_what) {
separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h)));
}
- if (items[i].checkable) {
-
- if (items[i].checked)
- check->draw(ci, item_ofs + Point2(0, Math::floor((h - check->get_height()) / 2.0)));
- else
- uncheck->draw(ci, item_ofs + Point2(0, Math::floor((h - check->get_height()) / 2.0)));
-
- item_ofs.x += check->get_width() + hseparation;
+ if (items[i].checkable_type) {
+ Texture *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr();
+ icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon->get_height()) / 2.0)));
+ item_ofs.x += icon->get_width() + hseparation;
}
if (!items[i].icon.is_null()) {
@@ -503,6 +508,11 @@ void PopupMenu::_notification(int p_what) {
update();
}
} break;
+ case NOTIFICATION_POST_POPUP: {
+
+ initial_button_mask = Input::get_singleton()->get_mouse_button_mask();
+ during_grabbed_click = (bool)initial_button_mask;
+ } break;
case NOTIFICATION_POPUP_HIDE: {
if (mouse_over >= 0) {
@@ -554,10 +564,11 @@ void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_
item.xl_text = tr(p_label);
item.accel = p_accel;
item.ID = p_ID;
- item.checkable = true;
+ item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
}
+
void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel) {
Item item;
@@ -565,11 +576,25 @@ void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel
item.xl_text = tr(p_label);
item.accel = p_accel;
item.ID = p_ID;
- item.checkable = true;
+ item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
}
+void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p_accel) {
+
+ add_check_item(p_label, p_ID, p_accel);
+ items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ update();
+}
+
+void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) {
+
+ add_icon_check_item(p_icon, p_label, p_ID, p_accel);
+ items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ update();
+}
+
void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
@@ -598,6 +623,7 @@ void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_g
items.push_back(item);
update();
}
+
void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
@@ -607,7 +633,7 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<Sh
Item item;
item.ID = p_ID;
item.shortcut = p_shortcut;
- item.checkable = true;
+ item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
item.icon = p_icon;
item.shortcut_is_global = p_global;
items.push_back(item);
@@ -624,11 +650,18 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bo
item.ID = p_ID;
item.shortcut = p_shortcut;
item.shortcut_is_global = p_global;
- item.checkable = true;
+ item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
}
+void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
+
+ add_check_shortcut(p_shortcut, p_ID, p_global);
+ items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ update();
+}
+
void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID, uint32_t p_accel) {
Item item;
@@ -636,7 +669,6 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
item.xl_text = tr(p_label);
item.accel = p_accel;
item.ID = p_ID;
- item.checkable = false;
item.max_states = p_max_states;
item.state = p_default_state;
items.push_back(item);
@@ -811,7 +843,14 @@ bool PopupMenu::is_item_separator(int p_idx) const {
void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].checkable = p_checkable;
+ items[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE;
+ update();
+}
+
+void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) {
+
+ ERR_FAIL_INDEX(p_idx, items.size());
+ items[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE;
update();
}
@@ -867,7 +906,12 @@ void PopupMenu::toggle_item_multistate(int p_idx) {
bool PopupMenu::is_item_checkable(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, items.size(), false);
- return items[p_idx].checkable;
+ return items[p_idx].checkable_type;
+}
+
+bool PopupMenu::is_item_radio_checkable(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, items.size(), false);
+ return items[p_idx].checkable_type == Item::CHECKABLE_TYPE_RADIO_BUTTON;
}
int PopupMenu::get_item_count() const {
@@ -941,7 +985,7 @@ void PopupMenu::activate_item(int p_item) {
// We close all parents that are chained together,
// with hide_on_item_selection enabled
- if (items[p_item].checkable) {
+ if (items[p_item].checkable_type) {
if (!hide_on_checkable_item_selection || !pop->is_hide_on_checkable_item_selection())
break;
} else if (0 < items[p_item].max_states) {
@@ -958,7 +1002,7 @@ void PopupMenu::activate_item(int p_item) {
// Hides popup by default; unless otherwise specified
// by using set_hide_on_item_selection and set_hide_on_checkable_item_selection
- if (items[p_item].checkable) {
+ if (items[p_item].checkable_type) {
if (!hide_on_checkable_item_selection)
return;
} else if (0 < items[p_item].max_states) {
@@ -1010,7 +1054,9 @@ Array PopupMenu::_get_items() const {
items.push_back(get_item_text(i));
items.push_back(get_item_icon(i));
- items.push_back(is_item_checkable(i));
+ // For compatibility, use false/true for no/checkbox and integers for other values
+ int ct = this->items[i].checkable_type;
+ items.push_back(Variant(ct <= Item::CHECKABLE_TYPE_CHECK_BOX ? is_item_checkable(i) : ct));
items.push_back(is_item_checked(i));
items.push_back(is_item_disabled(i));
@@ -1053,7 +1099,9 @@ void PopupMenu::_set_items(const Array &p_items) {
String text = p_items[i + 0];
Ref<Texture> icon = p_items[i + 1];
+ // For compatibility, use false/true for no/checkbox and integers for other values
bool checkable = p_items[i + 2];
+ bool radio_checkable = (int)p_items[i + 2] == Item::CHECKABLE_TYPE_RADIO_BUTTON;
bool checked = p_items[i + 3];
bool disabled = p_items[i + 4];
@@ -1066,7 +1114,13 @@ void PopupMenu::_set_items(const Array &p_items) {
int idx = get_item_count();
add_item(text, id);
set_item_icon(idx, icon);
- set_item_as_checkable(idx, checkable);
+ if (checkable) {
+ if (radio_checkable) {
+ set_item_as_radio_checkable(idx, true);
+ } else {
+ set_item_as_checkable(idx, true);
+ }
+ }
set_item_checked(idx, checked);
set_item_disabled(idx, disabled);
set_item_id(idx, id);
@@ -1147,12 +1201,14 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_item", "label", "id", "accel"), &PopupMenu::add_item, DEFVAL(-1), DEFVAL(0));
ClassDB::bind_method(D_METHOD("add_icon_check_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_check_item, DEFVAL(-1), DEFVAL(0));
ClassDB::bind_method(D_METHOD("add_check_item", "label", "id", "accel"), &PopupMenu::add_check_item, DEFVAL(-1), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("add_radio_check_item", "label", "id", "accel"), &PopupMenu::add_radio_check_item, DEFVAL(-1), DEFVAL(0));
ClassDB::bind_method(D_METHOD("add_submenu_item", "label", "submenu", "id"), &PopupMenu::add_submenu_item, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_shortcut", "shortcut", "id", "global"), &PopupMenu::add_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_icon_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_check_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_check_shortcut, DEFVAL(-1), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("add_radio_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_radio_check_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_item_text", "idx", "text"), &PopupMenu::set_item_text);
ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "icon"), &PopupMenu::set_item_icon);
@@ -1164,6 +1220,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_submenu", "idx", "submenu"), &PopupMenu::set_item_submenu);
ClassDB::bind_method(D_METHOD("set_item_as_separator", "idx", "enable"), &PopupMenu::set_item_as_separator);
ClassDB::bind_method(D_METHOD("set_item_as_checkable", "idx", "enable"), &PopupMenu::set_item_as_checkable);
+ ClassDB::bind_method(D_METHOD("set_item_as_radio_checkable", "idx", "enable"), &PopupMenu::set_item_as_radio_checkable);
ClassDB::bind_method(D_METHOD("set_item_tooltip", "idx", "tooltip"), &PopupMenu::set_item_tooltip);
ClassDB::bind_method(D_METHOD("set_item_shortcut", "idx", "shortcut", "global"), &PopupMenu::set_item_shortcut, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_item_multistate", "idx", "state"), &PopupMenu::set_item_multistate);
@@ -1182,6 +1239,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_submenu", "idx"), &PopupMenu::get_item_submenu);
ClassDB::bind_method(D_METHOD("is_item_separator", "idx"), &PopupMenu::is_item_separator);
ClassDB::bind_method(D_METHOD("is_item_checkable", "idx"), &PopupMenu::is_item_checkable);
+ ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "idx"), &PopupMenu::is_item_radio_checkable);
ClassDB::bind_method(D_METHOD("get_item_tooltip", "idx"), &PopupMenu::get_item_tooltip);
ClassDB::bind_method(D_METHOD("get_item_shortcut", "idx"), &PopupMenu::get_item_shortcut);
@@ -1216,15 +1274,20 @@ void PopupMenu::_bind_methods() {
ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index")));
}
-void PopupMenu::set_invalidate_click_until_motion() {
+void PopupMenu::popup(const Rect2 &p_bounds) {
+
+ grab_click_focus();
moved = Vector2();
invalidated_click = true;
+ Popup::popup(p_bounds);
}
PopupMenu::PopupMenu() {
mouse_over = -1;
submenu_over = -1;
+ initial_button_mask = 0;
+ during_grabbed_click = false;
set_focus_mode(FOCUS_ALL);
set_as_toplevel(true);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 60f36e95ec..fde91bd845 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -46,7 +46,11 @@ class PopupMenu : public Popup {
String text;
String xl_text;
bool checked;
- bool checkable;
+ enum {
+ CHECKABLE_TYPE_NONE,
+ CHECKABLE_TYPE_CHECK_BOX,
+ CHECKABLE_TYPE_RADIO_BUTTON,
+ } checkable_type;
int max_states;
int state;
bool separator;
@@ -63,7 +67,7 @@ class PopupMenu : public Popup {
Item() {
checked = false;
- checkable = false;
+ checkable_type = CHECKABLE_TYPE_NONE;
separator = false;
max_states = 0;
state = 0;
@@ -78,6 +82,8 @@ class PopupMenu : public Popup {
Timer *submenu_timer;
List<Rect2> autohide_areas;
Vector<Item> items;
+ int initial_button_mask;
+ bool during_grabbed_click;
int mouse_over;
int submenu_over;
Rect2 parent_rect;
@@ -115,12 +121,15 @@ public:
void add_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
void add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
void add_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
+ void add_radio_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
+ void add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
void add_submenu_item(const String &p_label, const String &p_submenu, int p_ID = -1);
void add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
void add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
void add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
void add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
+ void add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
void add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID = -1, uint32_t p_accel = 0);
@@ -134,6 +143,7 @@ public:
void set_item_submenu(int p_idx, const String &p_submenu);
void set_item_as_separator(int p_idx, bool p_separator);
void set_item_as_checkable(int p_idx, bool p_checkable);
+ void set_item_as_radio_checkable(int p_idx, bool p_radio_checkable);
void set_item_tooltip(int p_idx, const String &p_tooltip);
void set_item_shortcut(int p_idx, const Ref<ShortCut> &p_shortcut, bool p_global = false);
void set_item_h_offset(int p_idx, int p_offset);
@@ -154,6 +164,7 @@ public:
String get_item_submenu(int p_idx) const;
bool is_item_separator(int p_idx) const;
bool is_item_checkable(int p_idx) const;
+ bool is_item_radio_checkable(int p_idx) const;
String get_item_tooltip(int p_idx) const;
Ref<ShortCut> get_item_shortcut(int p_idx) const;
int get_item_state(int p_idx) const;
@@ -178,7 +189,6 @@ public:
void add_autohide_area(const Rect2 &p_area);
void clear_autohide_areas();
- void set_invalidate_click_until_motion();
void set_hide_on_item_selection(bool p_enabled);
bool is_hide_on_item_selection() const;
@@ -188,6 +198,8 @@ public:
void set_hide_on_multistate_item_selection(bool p_enabled);
bool is_hide_on_multistate_item_selection() const;
+ virtual void popup(const Rect2 &p_bounds = Rect2());
+
PopupMenu();
~PopupMenu();
};
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index ae07d5e671..6bfc4d4dee 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -516,6 +516,39 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
table->total_width += table->columns[i].width + hseparation;
}
+ //resize to max_width if needed and distribute the remaining space
+ bool table_need_fit = true;
+ while (table_need_fit) {
+ table_need_fit = false;
+ //fit slim
+ for (int i = 0; i < table->columns.size(); i++) {
+ if (!table->columns[i].expand)
+ continue;
+ int dif = table->columns[i].width - table->columns[i].max_width;
+ if (dif > 0) {
+ table_need_fit = true;
+ table->columns[i].width = table->columns[i].max_width;
+ table->total_width -= dif;
+ total_ratio -= table->columns[i].expand_ratio;
+ }
+ }
+ //grow
+ remaining_width = available_width - table->total_width;
+ if (remaining_width > 0 && total_ratio > 0) {
+ for (int i = 0; i < table->columns.size(); i++) {
+ if (table->columns[i].expand) {
+ int dif = table->columns[i].max_width - table->columns[i].width;
+ if (dif > 0) {
+ int slice = table->columns[i].expand_ratio * remaining_width / total_ratio;
+ int incr = MIN(dif, slice);
+ table->columns[i].width += incr;
+ table->total_width += incr;
+ }
+ }
+ }
+ }
+ }
+
//compute caches properly again with the right width
idx = 0;
for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index e1cabd3f88..6ec67aca6b 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -114,7 +114,7 @@ void ScrollBar::_gui_input(Ref<InputEvent> p_event) {
if (smooth_scroll_enabled) {
scrolling = true;
- set_physics_process(true);
+ set_physics_process_internal(true);
} else {
set_value(target_scroll);
}
@@ -138,7 +138,7 @@ void ScrollBar::_gui_input(Ref<InputEvent> p_event) {
if (smooth_scroll_enabled) {
scrolling = true;
- set_physics_process(true);
+ set_physics_process_internal(true);
} else {
set_value(target_scroll);
}
@@ -322,7 +322,7 @@ void ScrollBar::_notification(int p_what) {
drag_slave = NULL;
}
- if (p_what == NOTIFICATION_PHYSICS_PROCESS) {
+ if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
if (scrolling) {
if (get_value() != target_scroll) {
@@ -337,7 +337,7 @@ void ScrollBar::_notification(int p_what) {
}
} else {
scrolling = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
}
} else if (drag_slave_touching) {
@@ -397,7 +397,7 @@ void ScrollBar::_notification(int p_what) {
}
if (turnoff) {
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_slave_touching = false;
drag_slave_touching_deaccel = false;
}
@@ -566,7 +566,7 @@ void ScrollBar::_drag_slave_input(const Ref<InputEvent> &p_input) {
if (mb->is_pressed()) {
if (drag_slave_touching) {
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_slave_touching_deaccel = false;
drag_slave_touching = false;
drag_slave_speed = Vector2();
@@ -586,7 +586,7 @@ void ScrollBar::_drag_slave_input(const Ref<InputEvent> &p_input) {
drag_slave_touching_deaccel = false;
time_since_motion = 0;
if (drag_slave_touching) {
- set_physics_process(true);
+ set_physics_process_internal(true);
time_since_motion = 0;
}
}
@@ -598,7 +598,7 @@ void ScrollBar::_drag_slave_input(const Ref<InputEvent> &p_input) {
if (drag_slave_speed == Vector2()) {
drag_slave_touching_deaccel = false;
drag_slave_touching = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
} else {
drag_slave_touching_deaccel = true;
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index 33b3d46486..a1dcf3b002 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -68,13 +68,19 @@ Size2 ScrollContainer::get_minimum_size() const {
};
void ScrollContainer::_cancel_drag() {
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_touching_deaccel = false;
drag_touching = false;
drag_speed = Vector2();
drag_accum = Vector2();
last_drag_accum = Vector2();
drag_from = Vector2();
+
+ if (beyond_deadzone) {
+ emit_signal("scroll_ended");
+ propagate_notification(NOTIFICATION_SCROLL_END);
+ beyond_deadzone = false;
+ }
}
void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
@@ -122,13 +128,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (mb->is_pressed()) {
if (drag_touching) {
- set_physics_process(false);
- drag_touching_deaccel = false;
- drag_touching = false;
- drag_speed = Vector2();
- drag_accum = Vector2();
- last_drag_accum = Vector2();
- drag_from = Vector2();
+ _cancel_drag();
}
if (true) {
@@ -138,9 +138,10 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
drag_from = Vector2(h_scroll->get_value(), v_scroll->get_value());
drag_touching = OS::get_singleton()->has_touchscreen_ui_hint();
drag_touching_deaccel = false;
+ beyond_deadzone = false;
time_since_motion = 0;
if (drag_touching) {
- set_physics_process(true);
+ set_physics_process_internal(true);
time_since_motion = 0;
}
}
@@ -149,9 +150,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (drag_touching) {
if (drag_speed == Vector2()) {
- drag_touching_deaccel = false;
- drag_touching = false;
- set_physics_process(false);
+ _cancel_drag();
} else {
drag_touching_deaccel = true;
@@ -168,17 +167,27 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
Vector2 motion = Vector2(mm->get_relative().x, mm->get_relative().y);
drag_accum -= motion;
- Vector2 diff = drag_from + drag_accum;
-
- if (scroll_h)
- h_scroll->set_value(diff.x);
- else
- drag_accum.x = 0;
- if (scroll_v)
- v_scroll->set_value(diff.y);
- else
- drag_accum.y = 0;
- time_since_motion = 0;
+
+ if (beyond_deadzone || scroll_h && Math::abs(drag_accum.x) > deadzone || scroll_v && Math::abs(drag_accum.y) > deadzone) {
+ if (!beyond_deadzone) {
+ propagate_notification(NOTIFICATION_SCROLL_BEGIN);
+ emit_signal("scroll_started");
+
+ beyond_deadzone = true;
+ // resetting drag_accum here ensures smooth scrolling after reaching deadzone
+ drag_accum = -motion;
+ }
+ Vector2 diff = drag_from + drag_accum;
+ if (scroll_h)
+ h_scroll->set_value(diff.x);
+ else
+ drag_accum.x = 0;
+ if (scroll_v)
+ v_scroll->set_value(diff.y);
+ else
+ drag_accum.y = 0;
+ time_since_motion = 0;
+ }
}
}
@@ -269,7 +278,7 @@ void ScrollContainer::_notification(int p_what) {
update_scrollbars();
}
- if (p_what == NOTIFICATION_PHYSICS_PROCESS) {
+ if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
if (drag_touching) {
@@ -323,9 +332,7 @@ void ScrollContainer::_notification(int p_what) {
drag_speed = Vector2(sgn_x * val_x, sgn_y * val_y);
if (turnoff_h && turnoff_v) {
- set_physics_process(false);
- drag_touching = false;
- drag_touching_deaccel = false;
+ _cancel_drag();
}
} else {
@@ -430,6 +437,14 @@ void ScrollContainer::set_h_scroll(int p_pos) {
_cancel_drag();
}
+int ScrollContainer::get_deadzone() const {
+ return deadzone;
+}
+
+void ScrollContainer::set_deadzone(int p_deadzone) {
+ deadzone = p_deadzone;
+}
+
String ScrollContainer::get_configuration_warning() const {
int found = 0;
@@ -466,12 +481,20 @@ void ScrollContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_h_scroll"), &ScrollContainer::get_h_scroll);
ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &ScrollContainer::set_v_scroll);
ClassDB::bind_method(D_METHOD("get_v_scroll"), &ScrollContainer::get_v_scroll);
+ ClassDB::bind_method(D_METHOD("set_deadzone", "deadzone"), &ScrollContainer::set_deadzone);
+ ClassDB::bind_method(D_METHOD("get_deadzone"), &ScrollContainer::get_deadzone);
+
+ ADD_SIGNAL(MethodInfo("scroll_started"));
+ ADD_SIGNAL(MethodInfo("scroll_ended"));
ADD_GROUP("Scroll", "scroll_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_enabled"), "set_enable_h_scroll", "is_h_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_enabled"), "set_enable_v_scroll", "is_v_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_vertical"), "set_v_scroll", "get_v_scroll");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_deadzone"), "set_deadzone", "get_deadzone");
+
+ GLOBAL_DEF("gui/common/default_scroll_deadzone", 0);
};
ScrollContainer::ScrollContainer() {
@@ -490,8 +513,11 @@ ScrollContainer::ScrollContainer() {
drag_speed = Vector2();
drag_touching = false;
drag_touching_deaccel = false;
+ beyond_deadzone = false;
scroll_h = true;
scroll_v = true;
+ deadzone = GLOBAL_GET("gui/common/default_scroll_deadzone");
+
set_clip_contents(true);
};
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index 6e3387918b..3fe1ed447a 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -56,10 +56,13 @@ class ScrollContainer : public Container {
bool drag_touching;
bool drag_touching_deaccel;
bool click_handled;
+ bool beyond_deadzone;
bool scroll_h;
bool scroll_v;
+ int deadzone;
+
void _cancel_drag();
protected:
@@ -86,6 +89,9 @@ public:
void set_enable_v_scroll(bool p_enable);
bool is_v_scroll_enabled() const;
+ int get_deadzone() const;
+ void set_deadzone(int p_deadzone);
+
virtual bool clips_input() const;
virtual String get_configuration_warning() const;
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 6e85ce5eb4..0363dd44c2 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -31,6 +31,9 @@
#include "tab_container.h"
#include "message_queue.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/label.h"
+#include "scene/gui/texture_rect.h"
int TabContainer::_get_top_margin() const {
@@ -492,6 +495,141 @@ void TabContainer::_update_current_tab() {
set_current_tab(current);
}
+Variant TabContainer::get_drag_data(const Point2 &p_point) {
+
+ if (!drag_to_rearrange_enabled)
+ return Variant();
+
+ int tab_over = get_tab_idx_at_point(p_point);
+
+ if (tab_over < 0)
+ return Variant();
+
+ HBoxContainer *drag_preview = memnew(HBoxContainer);
+
+ Ref<Texture> icon = get_tab_icon(tab_over);
+ if (!icon.is_null()) {
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(icon);
+ drag_preview->add_child(tf);
+ }
+ Label *label = memnew(Label(get_tab_title(tab_over)));
+ drag_preview->add_child(label);
+ set_drag_preview(drag_preview);
+
+ Dictionary drag_data;
+ drag_data["type"] = "tabc_element";
+ drag_data["tabc_element"] = tab_over;
+ drag_data["from_path"] = get_path();
+ return drag_data;
+}
+
+bool TabContainer::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
+
+ if (!drag_to_rearrange_enabled)
+ return false;
+
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return false;
+
+ if (String(d["type"]) == "tabc_element") {
+
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ return true;
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between other TabContainers
+ Node *from_node = get_node(from_path);
+ TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node);
+ if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) {
+
+ if (!drag_to_rearrange_enabled)
+ return;
+
+ int hover_now = get_tab_idx_at_point(p_point);
+
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return;
+
+ if (String(d["type"]) == "tabc_element") {
+
+ int tab_from_id = d["tabc_element"];
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ if (hover_now < 0)
+ hover_now = get_tab_count() - 1;
+ move_child(get_tab_control(tab_from_id), hover_now);
+ set_current_tab(hover_now);
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between TabContainers
+ Node *from_node = get_node(from_path);
+ TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node);
+ if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ Control *moving_tabc = from_tabc->get_tab_control(tab_from_id);
+ from_tabc->remove_child(moving_tabc);
+ add_child(moving_tabc);
+ if (hover_now < 0)
+ hover_now = get_tab_count() - 1;
+ move_child(moving_tabc, hover_now);
+ set_current_tab(hover_now);
+ emit_signal("tab_changed", hover_now);
+ }
+ }
+ }
+ update();
+}
+
+int TabContainer::get_tab_idx_at_point(const Point2 &p_point) const {
+
+ if (get_tab_count() == 0)
+ return -1;
+
+ // must be on tabs in the tab header area.
+ if (p_point.x < tabs_ofs_cache || p_point.y > _get_top_margin())
+ return -1;
+
+ Size2 size = get_size();
+ int right_ofs = 0;
+
+ if (popup) {
+ Ref<Texture> menu = get_icon("menu");
+ right_ofs += menu->get_width();
+ }
+ if (buttons_visible_cache) {
+ Ref<Texture> increment = get_icon("increment");
+ Ref<Texture> decrement = get_icon("decrement");
+ right_ofs += increment->get_width() + decrement->get_width();
+ }
+ if (p_point.x > size.width - right_ofs) {
+ return -1;
+ }
+
+ // get the tab at the point
+ Vector<Control *> tabs = _get_tabs();
+ int px = p_point.x;
+ px -= tabs_ofs_cache;
+ for (int i = first_tab_cache; i <= last_tab_cache; i++) {
+ int tab_width = _get_tab_width(i);
+ if (px < tab_width) {
+ return i;
+ }
+ px -= tab_width;
+ }
+ return -1;
+}
+
void TabContainer::set_tab_align(TabAlign p_align) {
ERR_FAIL_INDEX(p_align, 3);
@@ -500,6 +638,7 @@ void TabContainer::set_tab_align(TabAlign p_align) {
_change_notify("tab_align");
}
+
TabContainer::TabAlign TabContainer::get_tab_align() const {
return align;
@@ -643,6 +782,21 @@ Popup *TabContainer::get_popup() const {
return popup;
}
+void TabContainer::set_drag_to_rearrange_enabled(bool p_enabled) {
+ drag_to_rearrange_enabled = p_enabled;
+}
+
+bool TabContainer::get_drag_to_rearrange_enabled() const {
+ return drag_to_rearrange_enabled;
+}
+void TabContainer::set_tabs_rearrange_group(int p_group_id) {
+ tabs_rearrange_group = p_group_id;
+}
+
+int TabContainer::get_tabs_rearrange_group() const {
+ return tabs_rearrange_group;
+}
+
void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &TabContainer::_gui_input);
@@ -664,6 +818,10 @@ void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &TabContainer::get_tab_disabled);
ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup);
ClassDB::bind_method(D_METHOD("get_popup"), &TabContainer::get_popup);
+ ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &TabContainer::set_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &TabContainer::get_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabContainer::set_tabs_rearrange_group);
+ ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabContainer::get_tabs_rearrange_group);
ClassDB::bind_method(D_METHOD("_child_renamed_callback"), &TabContainer::_child_renamed_callback);
ClassDB::bind_method(D_METHOD("_on_theme_changed"), &TabContainer::_on_theme_changed);
@@ -676,6 +834,7 @@ void TabContainer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align");
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tabs_visible"), "set_tabs_visible", "are_tabs_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
BIND_ENUM_CONSTANT(ALIGN_LEFT);
BIND_ENUM_CONSTANT(ALIGN_CENTER);
@@ -694,4 +853,6 @@ TabContainer::TabContainer() {
align = ALIGN_CENTER;
tabs_visible = true;
popup = NULL;
+ drag_to_rearrange_enabled = false;
+ tabs_rearrange_group = -1;
}
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index 4bc6e00145..1afe5f7541 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -58,6 +58,8 @@ private:
Control *_get_tab(int p_idx) const;
int _get_top_margin() const;
Popup *popup;
+ bool drag_to_rearrange_enabled;
+ int tabs_rearrange_group;
Vector<Control *> _get_tabs() const;
int _get_tab_width(int p_index) const;
@@ -71,6 +73,11 @@ protected:
virtual void add_child_notify(Node *p_child);
virtual void remove_child_notify(Node *p_child);
+ Variant get_drag_data(const Point2 &p_point);
+ bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
+ void drop_data(const Point2 &p_point, const Variant &p_data);
+ int get_tab_idx_at_point(const Point2 &p_point) const;
+
static void _bind_methods();
public:
@@ -104,6 +111,11 @@ public:
void set_popup(Node *p_popup);
Popup *get_popup() const;
+ void set_drag_to_rearrange_enabled(bool p_enabled);
+ bool get_drag_to_rearrange_enabled() const;
+ void set_tabs_rearrange_group(int p_group_id);
+ int get_tabs_rearrange_group() const;
+
TabContainer();
};
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index dee32aef7a..b114264de1 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -31,6 +31,9 @@
#include "tabs.h"
#include "message_queue.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/label.h"
+#include "scene/gui/texture_rect.h"
Size2 Tabs::get_minimum_size() const {
@@ -624,20 +627,105 @@ void Tabs::remove_tab(int p_idx) {
Variant Tabs::get_drag_data(const Point2 &p_point) {
- return get_tab_idx_at_point(p_point);
+ if (!drag_to_rearrange_enabled)
+ return Variant();
+
+ int tab_over = get_tab_idx_at_point(p_point);
+
+ if (tab_over < 0)
+ return Variant();
+
+ HBoxContainer *drag_preview = memnew(HBoxContainer);
+
+ if (!tabs[tab_over].icon.is_null()) {
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(tabs[tab_over].icon);
+ drag_preview->add_child(tf);
+ }
+ Label *label = memnew(Label(tabs[tab_over].text));
+ drag_preview->add_child(label);
+ if (!tabs[tab_over].right_button.is_null()) {
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(tabs[tab_over].right_button);
+ drag_preview->add_child(tf);
+ }
+ set_drag_preview(drag_preview);
+
+ Dictionary drag_data;
+ drag_data["type"] = "tab_element";
+ drag_data["tab_element"] = tab_over;
+ drag_data["from_path"] = get_path();
+ return drag_data;
}
bool Tabs::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
- return get_tab_idx_at_point(p_point) > -1;
+ if (!drag_to_rearrange_enabled)
+ return false;
+
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return false;
+
+ if (String(d["type"]) == "tab_element") {
+
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ return true;
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between other Tabs
+ Node *from_node = get_node(from_path);
+ Tabs *from_tabs = Object::cast_to<Tabs>(from_node);
+ if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ return true;
+ }
+ }
+ }
+ return false;
}
void Tabs::drop_data(const Point2 &p_point, const Variant &p_data) {
+ if (!drag_to_rearrange_enabled)
+ return;
+
int hover_now = get_tab_idx_at_point(p_point);
- ERR_FAIL_INDEX(hover_now, tabs.size());
- emit_signal("reposition_active_tab_request", hover_now);
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return;
+
+ if (String(d["type"]) == "tab_element") {
+
+ int tab_from_id = d["tab_element"];
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ if (hover_now < 0)
+ hover_now = get_tab_count() - 1;
+ move_tab(tab_from_id, hover_now);
+ emit_signal("reposition_active_tab_request", hover_now);
+ set_current_tab(hover_now);
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between Tabs
+ Node *from_node = get_node(from_path);
+ Tabs *from_tabs = Object::cast_to<Tabs>(from_node);
+ if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ if (tab_from_id >= from_tabs->get_tab_count())
+ return;
+ Tab moving_tab = from_tabs->tabs[tab_from_id];
+ if (hover_now < 0)
+ hover_now = get_tab_count();
+ tabs.insert(hover_now, moving_tab);
+ from_tabs->remove_tab(tab_from_id);
+ set_current_tab(hover_now);
+ emit_signal("tab_changed", hover_now);
+ _update_cache();
+ }
+ }
+ }
+ update();
}
int Tabs::get_tab_idx_at_point(const Point2 &p_point) const {
@@ -817,6 +905,21 @@ bool Tabs::get_scrolling_enabled() const {
return scrolling_enabled;
}
+void Tabs::set_drag_to_rearrange_enabled(bool p_enabled) {
+ drag_to_rearrange_enabled = p_enabled;
+}
+
+bool Tabs::get_drag_to_rearrange_enabled() const {
+ return drag_to_rearrange_enabled;
+}
+void Tabs::set_tabs_rearrange_group(int p_group_id) {
+ tabs_rearrange_group = p_group_id;
+}
+
+int Tabs::get_tabs_rearrange_group() const {
+ return tabs_rearrange_group;
+}
+
void Tabs::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input);
@@ -842,6 +945,10 @@ void Tabs::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_close_display_policy"), &Tabs::get_tab_close_display_policy);
ClassDB::bind_method(D_METHOD("set_scrolling_enabled", "enabled"), &Tabs::set_scrolling_enabled);
ClassDB::bind_method(D_METHOD("get_scrolling_enabled"), &Tabs::get_scrolling_enabled);
+ ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &Tabs::set_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &Tabs::get_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &Tabs::set_tabs_rearrange_group);
+ ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &Tabs::get_tabs_rearrange_group);
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab")));
@@ -854,6 +961,7 @@ void Tabs::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
BIND_ENUM_CONSTANT(ALIGN_LEFT);
BIND_ENUM_CONSTANT(ALIGN_CENTER);
@@ -884,4 +992,6 @@ Tabs::Tabs() {
scrolling_enabled = true;
buttons_visible = false;
hover = -1;
+ drag_to_rearrange_enabled = false;
+ tabs_rearrange_group = -1;
}
diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h
index 246b3cba67..3b38e7f2cb 100644
--- a/scene/gui/tabs.h
+++ b/scene/gui/tabs.h
@@ -90,6 +90,8 @@ private:
int hover; // hovered tab
int min_width;
bool scrolling_enabled;
+ bool drag_to_rearrange_enabled;
+ int tabs_rearrange_group;
int get_tab_width(int p_idx) const;
void _ensure_no_over_offset();
@@ -143,6 +145,11 @@ public:
void set_scrolling_enabled(bool p_enabled);
bool get_scrolling_enabled() const;
+ void set_drag_to_rearrange_enabled(bool p_enabled);
+ bool get_drag_to_rearrange_enabled() const;
+ void set_tabs_rearrange_group(int p_group_id);
+ int get_tabs_rearrange_group() const;
+
void ensure_tab_visible(int p_idx);
void set_min_width(int p_width);
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 48bd733e80..068ea9d4f5 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -184,6 +184,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
cri.region = j;
text[p_line].region_info[i] = cri;
i += lr - 1;
+
break;
}
@@ -211,6 +212,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
cri.region = j;
text[p_line].region_info[i] = cri;
i += lr - 1;
+
break;
}
}
@@ -537,7 +539,7 @@ void TextEdit::_notification(int p_what) {
draw_caret = false;
update();
} break;
- case NOTIFICATION_PHYSICS_PROCESS: {
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (scrolling && v_scroll->get_value() != target_v_scroll) {
double target_y = target_v_scroll - v_scroll->get_value();
double dist = sqrt(target_y * target_y);
@@ -546,17 +548,16 @@ void TextEdit::_notification(int p_what) {
if (Math::abs(vel) >= dist) {
v_scroll->set_value(target_v_scroll);
scrolling = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
} else {
v_scroll->set_value(v_scroll->get_value() + vel);
}
} else {
scrolling = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
}
} break;
case NOTIFICATION_DRAW: {
-
if ((!has_focus() && !menu->has_focus()) || !window_has_focus) {
draw_caret = false;
}
@@ -619,44 +620,10 @@ void TextEdit::_notification(int p_what) {
Color color = cache.font_color;
color.a *= readonly_alpha;
- int in_region = -1;
-
if (syntax_coloring) {
-
if (cache.background_color.a > 0.01) {
-
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), cache.background_color);
}
- //compute actual region to start (may be inside say, a comment).
- //slow in very large documents :( but ok for source!
-
- for (int i = 0; i < cursor.line_ofs; i++) {
-
- const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(i);
-
- if (in_region >= 0 && color_regions[in_region].line_only) {
- in_region = -1; //reset regions that end at end of line
- }
-
- for (const Map<int, Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) {
-
- const Text::ColorRegionInfo &cri = E->get();
-
- if (in_region == -1) {
-
- if (!cri.end) {
-
- in_region = cri.region;
- }
- } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
-
- if (cri.end || color_regions[cri.region].eq) {
-
- in_region = -1;
- }
- }
- }
- }
}
int brace_open_match_line = -1;
@@ -804,7 +771,6 @@ void TextEdit::_notification(int p_what) {
}
}
- int deregion = 0; //force it to clear inrgion
Point2 cursor_pos;
// get the highlighted words
@@ -848,19 +814,12 @@ void TextEdit::_notification(int p_what) {
if (smooth_scroll_enabled)
ofs_y -= ((v_scroll->get_value() - get_line_scroll_pos()) * get_row_height());
- bool prev_is_char = false;
- bool prev_is_number = false;
- bool in_keyword = false;
bool underlined = false;
- bool in_word = false;
- bool in_function_name = false;
- bool in_member_variable = false;
- bool is_hex_notation = false;
- Color keyword_color;
// check if line contains highlighted word
int highlighted_text_col = -1;
int search_text_col = -1;
+ int highlighted_word_col = -1;
if (!search_text.empty())
search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0);
@@ -868,7 +827,11 @@ void TextEdit::_notification(int p_what) {
if (highlighted_text.length() != 0 && highlighted_text != search_text)
highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
- const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(line);
+ if (select_identifiers_enabled && highlighted_word.length() != 0) {
+ if (_is_char(highlighted_word[0])) {
+ highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
+ }
+ }
if (text.is_marked(line)) {
@@ -936,170 +899,28 @@ void TextEdit::_notification(int p_what) {
cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, ofs_y + cache.font->get_ascent()), fc, cache.line_number_color);
}
- //loop through characters in one line
- for (int j = 0; j < str.length(); j++) {
-
- //look for keyword
- if (deregion > 0) {
- deregion--;
- if (deregion == 0)
- in_region = -1;
- }
- if (syntax_coloring && deregion == 0) {
-
- color = cache.font_color; //reset
- color.a *= readonly_alpha;
- //find keyword
- bool is_char = _is_text_char(str[j]);
- bool is_symbol = _is_symbol(str[j]);
- bool is_number = _is_number(str[j]);
-
- if (j == 0 && in_region >= 0 && color_regions[in_region].line_only) {
- in_region = -1; //reset regions that end at end of line
- }
-
- // allow ABCDEF in hex notation
- if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
- is_number = true;
- } else {
- is_hex_notation = false;
- }
-
- // check for dot or underscore or 'x' for hex notation in floating point number
- if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) {
- is_number = true;
- is_symbol = false;
- is_char = false;
-
- if (str[j] == 'x' && str[j - 1] == '0') {
- is_hex_notation = true;
- }
- }
-
- if (!in_word && _is_char(str[j]) && !is_number) {
- in_word = true;
- }
-
- if ((in_keyword || in_word) && !is_hex_notation) {
- is_number = false;
- }
-
- if (is_symbol && str[j] != '.' && in_word) {
- in_word = false;
- }
-
- if (is_symbol && cri_map.has(j)) {
-
- const Text::ColorRegionInfo &cri = cri_map[j];
-
- if (in_region == -1) {
-
- if (!cri.end) {
-
- in_region = cri.region;
- }
- } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
-
- if (cri.end || color_regions[cri.region].eq) {
-
- deregion = color_regions[cri.region].eq ? color_regions[cri.region].begin_key.length() : color_regions[cri.region].end_key.length();
- }
- }
- }
-
- if (!is_char) {
- in_keyword = false;
- underlined = false;
- }
-
- if (in_region == -1 && !in_keyword && is_char && !prev_is_char) {
-
- int to = j;
- while (to < str.length() && _is_text_char(str[to]))
- to++;
-
- uint32_t hash = String::hash(&str[j], to - j);
- StrRange range(&str[j], to - j);
-
- const Color *col = keywords.custom_getptr(range, hash);
-
- if (!col) {
- col = member_keywords.custom_getptr(range, hash);
-
- if (col) {
- for (int k = j - 1; k >= 0; k--) {
- if (str[k] == '.') {
- col = NULL; //member indexing not allowed
- break;
- } else if (str[k] > 32) {
- break;
- }
- }
- }
- }
-
- if (col) {
-
- in_keyword = true;
- keyword_color = *col;
- }
-
- if (select_identifiers_enabled && highlighted_word != String()) {
- if (highlighted_word == range) {
- underlined = true;
- }
- }
- }
-
- if (!in_function_name && in_word && !in_keyword) {
-
- int k = j;
- while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
- k++;
- }
-
- // check for space between name and bracket
- while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
- k++;
- }
-
- if (str[k] == '(') {
- in_function_name = true;
- }
- }
+ //loop through characters in one line
+ Map<int, HighlighterInfo> color_map;
+ if (syntax_coloring) {
+ color_map = _get_line_syntax_highlighting(line);
+ }
- if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
- int k = j;
- while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
- k--;
- }
+ // ensure we at least use the font color
+ Color current_color = cache.font_color;
+ if (readonly) {
+ current_color.a *= readonly_alpha;
+ }
+ for (int j = 0; j < str.length(); j++) {
- if (str[k] == '.') {
- in_member_variable = true;
+ if (syntax_coloring) {
+ if (color_map.has(j)) {
+ current_color = color_map[j].color;
+ if (readonly) {
+ current_color.a *= readonly_alpha;
}
}
-
- if (is_symbol) {
- in_function_name = false;
- in_member_variable = false;
- }
-
- if (in_region >= 0)
- color = color_regions[in_region].color;
- else if (in_keyword)
- color = keyword_color;
- else if (in_member_variable)
- color = cache.member_variable_color;
- else if (in_function_name)
- color = cache.function_color;
- else if (is_symbol)
- color = cache.symbol_color;
- else if (is_number)
- color = cache.number_color;
-
- prev_is_char = is_char;
- prev_is_number = is_number;
+ color = current_color;
}
int char_w;
@@ -1206,6 +1027,13 @@ void TextEdit::_notification(int p_what) {
}
}
+ if (highlighted_word_col != -1) {
+ if (j > highlighted_word_col + highlighted_word.length()) {
+ highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j);
+ }
+ underlined = (j >= highlighted_word_col && j < highlighted_word_col + highlighted_word.length());
+ }
+
if (brace_matching_enabled) {
if ((brace_open_match_line == line && brace_open_match_column == j) ||
(cursor.column == j && cursor.line == line && (brace_open_matching || brace_open_mismatch))) {
@@ -1512,6 +1340,7 @@ void TextEdit::_notification(int p_what) {
OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos + Point2(0, get_row_height()));
OS::get_singleton()->set_ime_intermediate_text_callback(_ime_text_callback, this);
}
+
} break;
case NOTIFICATION_FOCUS_ENTER: {
@@ -1528,7 +1357,6 @@ void TextEdit::_notification(int p_what) {
if (raised_from_completion) {
VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1);
}
-
} break;
case NOTIFICATION_FOCUS_EXIT: {
@@ -3199,7 +3027,7 @@ void TextEdit::_scroll_up(real_t p_delta) {
v_scroll->set_value(target_v_scroll);
} else {
scrolling = true;
- set_physics_process(true);
+ set_physics_process_internal(true);
}
} else {
v_scroll->set_value(target_v_scroll);
@@ -3232,7 +3060,7 @@ void TextEdit::_scroll_down(real_t p_delta) {
v_scroll->set_value(target_v_scroll);
} else {
scrolling = true;
- set_physics_process(true);
+ set_physics_process_internal(true);
}
} else {
v_scroll->set_value(target_v_scroll);
@@ -3356,6 +3184,7 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
text_changed_dirty = true;
}
+ _line_edited_from(p_line);
}
String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) const {
@@ -3406,6 +3235,7 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
text_changed_dirty = true;
}
+ _line_edited_from(p_from_line);
}
void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r_end_line, int *r_end_char) {
@@ -3528,6 +3358,13 @@ void TextEdit::_insert_text_at_cursor(const String &p_text) {
update();
}
+void TextEdit::_line_edited_from(int p_line) {
+ int cache_size = color_region_cache.size();
+ for (int i = p_line; i < cache_size; i++) {
+ color_region_cache.erase(i);
+ }
+}
+
int TextEdit::get_char_count() {
int totalsize = 0;
@@ -4150,12 +3987,88 @@ void TextEdit::_update_caches() {
cache.can_fold_icon = get_icon("GuiTreeArrowDown", "EditorIcons");
cache.folded_eol_icon = get_icon("GuiEllipsis", "EditorIcons");
text.set_font(cache.font);
+
+ if (syntax_highlighter) {
+ syntax_highlighter->_update_cache();
+ }
+}
+
+SyntaxHighlighter *TextEdit::_get_syntax_highlighting() {
+ return syntax_highlighter;
+}
+
+void TextEdit::_set_syntax_highlighting(SyntaxHighlighter *p_syntax_highlighter) {
+ syntax_highlighter = p_syntax_highlighter;
+ if (syntax_highlighter) {
+ syntax_highlighter->set_text_editor(this);
+ syntax_highlighter->_update_cache();
+ }
+ update();
+}
+
+int TextEdit::_is_line_in_region(int p_line) {
+
+ // do we have in cache?
+ if (color_region_cache.has(p_line)) {
+ return color_region_cache[p_line];
+ }
+
+ // if not find the closest line we have
+ int previous_line = p_line - 1;
+ for (previous_line; previous_line > -1; previous_line--) {
+ if (color_region_cache.has(p_line)) {
+ break;
+ }
+ }
+
+ // calculate up to line we need and update the cache along the way.
+ int in_region = color_region_cache[previous_line];
+ if (previous_line == -1) {
+ in_region = -1;
+ }
+ for (int i = previous_line; i < p_line; i++) {
+ const Map<int, Text::ColorRegionInfo> &cri_map = _get_line_color_region_info(i);
+ for (const Map<int, Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) {
+ const Text::ColorRegionInfo &cri = E->get();
+ if (in_region == -1) {
+ if (!cri.end) {
+ in_region = cri.region;
+ }
+ } else if (in_region == cri.region && !_get_color_region(cri.region).line_only) {
+ if (cri.end || _get_color_region(cri.region).eq) {
+ in_region = -1;
+ }
+ }
+ }
+
+ if (in_region >= 0 && _get_color_region(in_region).line_only) {
+ in_region = -1;
+ }
+
+ color_region_cache[i + 1] = in_region;
+ }
+ return in_region;
+}
+
+TextEdit::ColorRegion TextEdit::_get_color_region(int p_region) const {
+ if (p_region < 0 || p_region >= color_regions.size()) {
+ return ColorRegion();
+ }
+ return color_regions[p_region];
+}
+
+Map<int, TextEdit::Text::ColorRegionInfo> TextEdit::_get_line_color_region_info(int p_line) const {
+ if (p_line < 0 || p_line > text.size() - 1) {
+ return Map<int, Text::ColorRegionInfo>();
+ }
+ return text.get_color_region_info(p_line);
}
void TextEdit::clear_colors() {
keywords.clear();
color_regions.clear();
+ color_region_cache.clear();
text.clear_caches();
}
@@ -4165,6 +4078,14 @@ void TextEdit::add_keyword_color(const String &p_keyword, const Color &p_color)
update();
}
+bool TextEdit::has_keyword_color(String p_keyword) const {
+ return keywords.has(p_keyword);
+}
+
+Color TextEdit::get_keyword_color(String p_keyword) const {
+ return keywords[p_keyword];
+}
+
void TextEdit::add_color_region(const String &p_begin_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
color_regions.push_back(ColorRegion(p_begin_key, p_end_key, p_color, p_line_only));
@@ -4177,6 +4098,14 @@ void TextEdit::add_member_keyword(const String &p_keyword, const Color &p_color)
update();
}
+bool TextEdit::has_member_color(String p_member) const {
+ return member_keywords.has(p_member);
+}
+
+Color TextEdit::get_member_color(String p_member) const {
+ return member_keywords[p_member];
+}
+
void TextEdit::clear_member_keywords() {
member_keywords.clear();
update();
@@ -5691,6 +5620,8 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_v_scroll_speed"), &TextEdit::get_v_scroll_speed);
ClassDB::bind_method(D_METHOD("add_keyword_color", "keyword", "color"), &TextEdit::add_keyword_color);
+ ClassDB::bind_method(D_METHOD("has_keyword_color", "keyword"), &TextEdit::has_keyword_color);
+ ClassDB::bind_method(D_METHOD("get_keyword_color", "keyword"), &TextEdit::get_keyword_color);
ClassDB::bind_method(D_METHOD("add_color_region", "begin_key", "end_key", "color", "line_only"), &TextEdit::add_color_region, DEFVAL(false));
ClassDB::bind_method(D_METHOD("clear_colors"), &TextEdit::clear_colors);
ClassDB::bind_method(D_METHOD("menu_option", "option"), &TextEdit::menu_option);
@@ -5744,6 +5675,7 @@ TextEdit::TextEdit() {
clear();
wrap = false;
set_focus_mode(FOCUS_ALL);
+ syntax_highlighter = NULL;
_update_caches();
cache.size = Size2(1, 1);
cache.row_height = 1;
@@ -5860,3 +5792,200 @@ TextEdit::TextEdit() {
TextEdit::~TextEdit() {
}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Map<int, TextEdit::HighlighterInfo> TextEdit::_get_line_syntax_highlighting(int p_line) {
+ if (syntax_highlighter != NULL) {
+ return syntax_highlighter->_get_line_syntax_highlighting(p_line);
+ }
+
+ Map<int, HighlighterInfo> color_map;
+
+ bool prev_is_char = false;
+ bool prev_is_number = false;
+ bool in_keyword = false;
+ bool in_word = false;
+ bool in_function_name = false;
+ bool in_member_variable = false;
+ bool is_hex_notation = false;
+ Color keyword_color;
+ Color color;
+
+ int in_region = _is_line_in_region(p_line);
+ int deregion = 0;
+
+ const Map<int, TextEdit::Text::ColorRegionInfo> cri_map = text.get_color_region_info(p_line);
+ const String &str = text[p_line];
+ Color prev_color;
+ for (int j = 0; j < str.length(); j++) {
+ HighlighterInfo highlighter_info;
+
+ if (deregion > 0) {
+ deregion--;
+ if (deregion == 0) {
+ in_region = -1;
+ }
+ }
+
+ if (deregion != 0) {
+ if (color != prev_color) {
+ prev_color = color;
+ highlighter_info.color = color;
+ color_map[j] = highlighter_info;
+ }
+ continue;
+ }
+
+ color = cache.font_color;
+
+ bool is_char = _is_text_char(str[j]);
+ bool is_symbol = _is_symbol(str[j]);
+ bool is_number = _is_number(str[j]);
+
+ // allow ABCDEF in hex notation
+ if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
+ is_number = true;
+ } else {
+ is_hex_notation = false;
+ }
+
+ // check for dot or underscore or 'x' for hex notation in floating point number
+ if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) {
+ is_number = true;
+ is_symbol = false;
+ is_char = false;
+
+ if (str[j] == 'x' && str[j - 1] == '0') {
+ is_hex_notation = true;
+ }
+ }
+
+ if (!in_word && _is_char(str[j]) && !is_number) {
+ in_word = true;
+ }
+
+ if ((in_keyword || in_word) && !is_hex_notation) {
+ is_number = false;
+ }
+
+ if (is_symbol && str[j] != '.' && in_word) {
+ in_word = false;
+ }
+
+ if (is_symbol && cri_map.has(j)) {
+ const TextEdit::Text::ColorRegionInfo &cri = cri_map[j];
+
+ if (in_region == -1) {
+ if (!cri.end) {
+ in_region = cri.region;
+ }
+ } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
+ if (cri.end || color_regions[cri.region].eq) {
+ deregion = color_regions[cri.region].eq ? color_regions[cri.region].begin_key.length() : color_regions[cri.region].end_key.length();
+ }
+ }
+ }
+
+ if (!is_char) {
+ in_keyword = false;
+ }
+
+ if (in_region == -1 && !in_keyword && is_char && !prev_is_char) {
+
+ int to = j;
+ while (to < str.length() && _is_text_char(str[to]))
+ to++;
+
+ uint32_t hash = String::hash(&str[j], to - j);
+ StrRange range(&str[j], to - j);
+
+ const Color *col = keywords.custom_getptr(range, hash);
+
+ if (!col) {
+ col = member_keywords.custom_getptr(range, hash);
+
+ if (col) {
+ for (int k = j - 1; k >= 0; k--) {
+ if (str[k] == '.') {
+ col = NULL; //member indexing not allowed
+ break;
+ } else if (str[k] > 32) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (col) {
+ in_keyword = true;
+ keyword_color = *col;
+ }
+ }
+
+ if (!in_function_name && in_word && !in_keyword) {
+
+ int k = j;
+ while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ k++;
+ }
+
+ // check for space between name and bracket
+ while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
+ k++;
+ }
+
+ if (str[k] == '(') {
+ in_function_name = true;
+ }
+ }
+
+ if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
+ int k = j;
+ while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ k--;
+ }
+
+ if (str[k] == '.') {
+ in_member_variable = true;
+ }
+ }
+
+ if (is_symbol) {
+ in_function_name = false;
+ in_member_variable = false;
+ }
+
+ if (in_region >= 0)
+ color = color_regions[in_region].color;
+ else if (in_keyword)
+ color = keyword_color;
+ else if (in_member_variable)
+ color = cache.member_variable_color;
+ else if (in_function_name)
+ color = cache.function_color;
+ else if (is_symbol)
+ color = cache.symbol_color;
+ else if (is_number)
+ color = cache.number_color;
+
+ prev_is_char = is_char;
+ prev_is_number = is_number;
+
+ if (color != prev_color) {
+ prev_color = color;
+ highlighter_info.color = color;
+ color_map[j] = highlighter_info;
+ }
+ }
+
+ return color_map;
+}
+
+void SyntaxHighlighter::set_text_editor(TextEdit *p_text_editor) {
+ text_editor = p_text_editor;
+}
+
+TextEdit *SyntaxHighlighter::get_text_editor() {
+ return text_editor;
+}
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 8ac3b9fce6..30e70bfd0b 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -36,10 +36,82 @@
#include "scene/gui/scroll_bar.h"
#include "scene/main/timer.h"
+class SyntaxHighlighter;
+
class TextEdit : public Control {
- GDCLASS(TextEdit, Control);
+ GDCLASS(TextEdit, Control)
+
+public:
+ struct HighlighterInfo {
+ Color color;
+ };
+
+ struct ColorRegion {
+
+ Color color;
+ String begin_key;
+ String end_key;
+ bool line_only;
+ bool eq;
+ ColorRegion(const String &p_begin_key = "", const String &p_end_key = "", const Color &p_color = Color(), bool p_line_only = false) {
+ begin_key = p_begin_key;
+ end_key = p_end_key;
+ color = p_color;
+ line_only = p_line_only || p_end_key == "";
+ eq = begin_key == end_key;
+ }
+ };
+
+ class Text {
+ public:
+ struct ColorRegionInfo {
+
+ int region;
+ bool end;
+ };
+
+ struct Line {
+ int width_cache : 24;
+ bool marked : 1;
+ bool breakpoint : 1;
+ bool hidden : 1;
+ Map<int, ColorRegionInfo> region_info;
+ String data;
+ };
+
+ private:
+ const Vector<ColorRegion> *color_regions;
+ mutable Vector<Line> text;
+ Ref<Font> font;
+ int indent_size;
+
+ void _update_line_cache(int p_line) const;
+
+ public:
+ void set_indent_size(int p_indent_size);
+ void set_font(const Ref<Font> &p_font);
+ void set_color_regions(const Vector<ColorRegion> *p_regions) { color_regions = p_regions; }
+ int get_line_width(int p_line) const;
+ int get_max_width(bool p_exclude_hidden = false) const;
+ const Map<int, ColorRegionInfo> &get_color_region_info(int p_line) const;
+ void set(int p_line, const String &p_text);
+ void set_marked(int p_line, bool p_marked) { text[p_line].marked = p_marked; }
+ bool is_marked(int p_line) const { return text[p_line].marked; }
+ void set_breakpoint(int p_line, bool p_breakpoint) { text[p_line].breakpoint = p_breakpoint; }
+ bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
+ void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; }
+ bool is_hidden(int p_line) const { return text[p_line].hidden; }
+ void insert(int p_at, const String &p_text);
+ void remove(int p_at);
+ int size() const { return text.size(); }
+ void clear();
+ void clear_caches();
+ _FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; }
+ Text() { indent_size = 4; }
+ };
+private:
struct Cursor {
int last_fit_x;
int line, column; ///< cursor
@@ -115,69 +187,7 @@ class TextEdit : public Control {
Size2 size;
} cache;
- struct ColorRegion {
-
- Color color;
- String begin_key;
- String end_key;
- bool line_only;
- bool eq;
- ColorRegion(const String &p_begin_key = "", const String &p_end_key = "", const Color &p_color = Color(), bool p_line_only = false) {
- begin_key = p_begin_key;
- end_key = p_end_key;
- color = p_color;
- line_only = p_line_only || p_end_key == "";
- eq = begin_key == end_key;
- }
- };
-
- class Text {
- public:
- struct ColorRegionInfo {
-
- int region;
- bool end;
- };
-
- struct Line {
- int width_cache : 24;
- bool marked : 1;
- bool breakpoint : 1;
- bool hidden : 1;
- Map<int, ColorRegionInfo> region_info;
- String data;
- };
-
- private:
- const Vector<ColorRegion> *color_regions;
- mutable Vector<Line> text;
- Ref<Font> font;
- int indent_size;
-
- void _update_line_cache(int p_line) const;
-
- public:
- void set_indent_size(int p_indent_size);
- void set_font(const Ref<Font> &p_font);
- void set_color_regions(const Vector<ColorRegion> *p_regions) { color_regions = p_regions; }
- int get_line_width(int p_line) const;
- int get_max_width(bool p_exclude_hidden = false) const;
- const Map<int, ColorRegionInfo> &get_color_region_info(int p_line) const;
- void set(int p_line, const String &p_text);
- void set_marked(int p_line, bool p_marked) { text[p_line].marked = p_marked; }
- bool is_marked(int p_line) const { return text[p_line].marked; }
- void set_breakpoint(int p_line, bool p_breakpoint) { text[p_line].breakpoint = p_breakpoint; }
- bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
- void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; }
- bool is_hidden(int p_line) const { return text[p_line].hidden; }
- void insert(int p_at, const String &p_text);
- void remove(int p_at);
- int size() const { return text.size(); }
- void clear();
- void clear_caches();
- _FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; }
- Text() { indent_size = 4; }
- };
+ Map<int, int> color_region_cache;
struct TextOperation {
@@ -209,9 +219,12 @@ class TextEdit : public Control {
void _do_text_op(const TextOperation &p_op, bool p_reverse);
//syntax coloring
+ SyntaxHighlighter *syntax_highlighter;
HashMap<String, Color> keywords;
HashMap<String, Color> member_keywords;
+ Map<int, HighlighterInfo> _get_line_syntax_highlighting(int p_line);
+
Vector<ColorRegion> color_regions;
Set<String> completion_prefixes;
@@ -355,6 +368,7 @@ class TextEdit : public Control {
void _update_caches();
void _cursor_changed_emit();
void _text_changed_emit();
+ void _line_edited_from(int p_line);
void _push_current_op();
@@ -391,6 +405,13 @@ protected:
static void _bind_methods();
public:
+ SyntaxHighlighter *_get_syntax_highlighting();
+ void _set_syntax_highlighting(SyntaxHighlighter *p_syntax_highlighter);
+
+ int _is_line_in_region(int p_line);
+ ColorRegion _get_color_region(int p_region) const;
+ Map<int, Text::ColorRegionInfo> _get_line_color_region_info(int p_line) const;
+
enum MenuItems {
MENU_CUT,
MENU_COPY,
@@ -545,10 +566,15 @@ public:
bool is_insert_mode() const;
void add_keyword_color(const String &p_keyword, const Color &p_color);
+ bool has_keyword_color(String p_keyword) const;
+ Color get_keyword_color(String p_keyword) const;
+
void add_color_region(const String &p_begin_key = String(), const String &p_end_key = String(), const Color &p_color = Color(), bool p_line_only = false);
void clear_colors();
void add_member_keyword(const String &p_keyword, const Color &p_color);
+ bool has_member_color(String p_member) const;
+ Color get_member_color(String p_member) const;
void clear_member_keywords();
int get_v_scroll() const;
@@ -621,4 +647,19 @@ public:
VARIANT_ENUM_CAST(TextEdit::MenuItems);
VARIANT_ENUM_CAST(TextEdit::SearchFlags);
+class SyntaxHighlighter {
+protected:
+ TextEdit *text_editor;
+
+public:
+ virtual void _update_cache() = 0;
+ virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line) = 0;
+
+ virtual String get_name() = 0;
+ virtual List<String> get_supported_languages() = 0;
+
+ void set_text_editor(TextEdit *p_text_editor);
+ TextEdit *get_text_editor();
+};
+
#endif // TEXT_EDIT_H
diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp
index 4b3ba6df3c..82d983184b 100644
--- a/scene/gui/texture_progress.cpp
+++ b/scene/gui/texture_progress.cpp
@@ -106,6 +106,33 @@ Ref<Texture> TextureProgress::get_progress_texture() const {
return progress;
}
+void TextureProgress::set_tint_under(const Color &p_tint) {
+ tint_under = p_tint;
+ update();
+}
+
+Color TextureProgress::get_tint_under() const {
+ return tint_under;
+}
+
+void TextureProgress::set_tint_progress(const Color &p_tint) {
+ tint_progress = p_tint;
+ update();
+}
+
+Color TextureProgress::get_tint_progress() const {
+ return tint_progress;
+}
+
+void TextureProgress::set_tint_over(const Color &p_tint) {
+ tint_over = p_tint;
+ update();
+}
+
+Color TextureProgress::get_tint_over() const {
+ return tint_over;
+}
+
Point2 TextureProgress::unit_val_to_uv(float val) {
if (progress.is_null())
return Point2();
@@ -170,7 +197,7 @@ Point2 TextureProgress::get_relative_center() {
return p;
}
-void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio) {
+void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate) {
Vector2 texture_size = p_texture->get_size();
Vector2 topleft = Vector2(stretch_margin[MARGIN_LEFT], stretch_margin[MARGIN_TOP]);
Vector2 bottomright = Vector2(stretch_margin[MARGIN_RIGHT], stretch_margin[MARGIN_BOTTOM]);
@@ -240,7 +267,7 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F
}
RID ci = get_canvas_item();
- VS::get_singleton()->canvas_item_add_nine_patch(ci, dst_rect, src_rect, p_texture->get_rid(), topleft, bottomright);
+ VS::get_singleton()->canvas_item_add_nine_patch(ci, dst_rect, src_rect, p_texture->get_rid(), topleft, bottomright, VS::NINE_PATCH_STRETCH, VS::NINE_PATCH_STRETCH, true, p_modulate);
}
void TextureProgress::_notification(int p_what) {
@@ -251,42 +278,42 @@ void TextureProgress::_notification(int p_what) {
if (nine_patch_stretch && (mode == FILL_LEFT_TO_RIGHT || mode == FILL_RIGHT_TO_LEFT || mode == FILL_TOP_TO_BOTTOM || mode == FILL_BOTTOM_TO_TOP)) {
if (under.is_valid()) {
- draw_nine_patch_stretched(under, FILL_LEFT_TO_RIGHT, 1.0);
+ draw_nine_patch_stretched(under, FILL_LEFT_TO_RIGHT, 1.0, tint_under);
}
if (progress.is_valid()) {
- draw_nine_patch_stretched(progress, mode, get_as_ratio());
+ draw_nine_patch_stretched(progress, mode, get_as_ratio(), tint_progress);
}
if (over.is_valid()) {
- draw_nine_patch_stretched(over, FILL_LEFT_TO_RIGHT, 1.0);
+ draw_nine_patch_stretched(over, FILL_LEFT_TO_RIGHT, 1.0, tint_over);
}
} else {
if (under.is_valid())
- draw_texture(under, Point2());
+ draw_texture(under, Point2(), tint_under);
if (progress.is_valid()) {
Size2 s = progress->get_size();
switch (mode) {
case FILL_LEFT_TO_RIGHT: {
Rect2 region = Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y));
- draw_texture_rect_region(progress, region, region);
+ draw_texture_rect_region(progress, region, region, tint_progress);
} break;
case FILL_RIGHT_TO_LEFT: {
Rect2 region = Rect2(Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y));
- draw_texture_rect_region(progress, region, region);
+ draw_texture_rect_region(progress, region, region, tint_progress);
} break;
case FILL_TOP_TO_BOTTOM: {
Rect2 region = Rect2(Point2(), Size2(s.x, s.y * get_as_ratio()));
- draw_texture_rect_region(progress, region, region);
+ draw_texture_rect_region(progress, region, region, tint_progress);
} break;
case FILL_BOTTOM_TO_TOP: {
Rect2 region = Rect2(Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio()));
- draw_texture_rect_region(progress, region, region);
+ draw_texture_rect_region(progress, region, region, tint_progress);
} break;
case FILL_CLOCKWISE:
case FILL_COUNTER_CLOCKWISE: {
float val = get_as_ratio() * rad_max_degrees / 360;
if (val == 1) {
Rect2 region = Rect2(Point2(), s);
- draw_texture_rect_region(progress, region, region);
+ draw_texture_rect_region(progress, region, region, tint_progress);
} else if (val != 0) {
Array pts;
float direction = mode == FILL_CLOCKWISE ? 1 : -1;
@@ -311,7 +338,9 @@ void TextureProgress::_notification(int p_what) {
uvs.push_back(uv);
points.push_back(Point2(uv.x * s.x, uv.y * s.y));
}
- draw_polygon(points, Vector<Color>(), uvs, progress);
+ Vector<Color> colors;
+ colors.push_back(tint_progress);
+ draw_polygon(points, colors, uvs, progress);
}
if (Engine::get_singleton()->is_editor_hint()) {
Point2 p = progress->get_size();
@@ -323,11 +352,11 @@ void TextureProgress::_notification(int p_what) {
}
} break;
default:
- draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)));
+ draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress);
}
}
if (over.is_valid())
- draw_texture(over, Point2());
+ draw_texture(over, Point2(), tint_over);
}
} break;
@@ -389,6 +418,15 @@ void TextureProgress::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &TextureProgress::set_fill_mode);
ClassDB::bind_method(D_METHOD("get_fill_mode"), &TextureProgress::get_fill_mode);
+ ClassDB::bind_method(D_METHOD("set_tint_under", "tint"), &TextureProgress::set_tint_under);
+ ClassDB::bind_method(D_METHOD("get_tint_under"), &TextureProgress::get_tint_under);
+
+ ClassDB::bind_method(D_METHOD("set_tint_progress", "tint"), &TextureProgress::set_tint_progress);
+ ClassDB::bind_method(D_METHOD("get_tint_progress"), &TextureProgress::get_tint_progress);
+
+ ClassDB::bind_method(D_METHOD("set_tint_over", "tint"), &TextureProgress::set_tint_over);
+ ClassDB::bind_method(D_METHOD("get_tint_over"), &TextureProgress::get_tint_over);
+
ClassDB::bind_method(D_METHOD("set_radial_initial_angle", "mode"), &TextureProgress::set_radial_initial_angle);
ClassDB::bind_method(D_METHOD("get_radial_initial_angle"), &TextureProgress::get_radial_initial_angle);
@@ -409,6 +447,10 @@ void TextureProgress::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_over_texture", "get_over_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_progress_texture", "get_progress_texture");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise"), "set_fill_mode", "get_fill_mode");
+ ADD_GROUP("Tint", "tint_");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_under", "get_tint_under");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_over", "get_tint_over");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_progress", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_progress", "get_tint_progress");
ADD_GROUP("Radial Fill", "radial_");
ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "radial_initial_angle", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_radial_initial_angle", "get_radial_initial_angle");
ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "radial_fill_degrees", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_fill_degrees", "get_fill_degrees");
@@ -440,4 +482,6 @@ TextureProgress::TextureProgress() {
stretch_margin[MARGIN_RIGHT] = 0;
stretch_margin[MARGIN_BOTTOM] = 0;
stretch_margin[MARGIN_TOP] = 0;
+
+ tint_under = tint_progress = tint_over = Color(1, 1, 1);
}
diff --git a/scene/gui/texture_progress.h b/scene/gui/texture_progress.h
index 77c3980e29..34158b5db5 100644
--- a/scene/gui/texture_progress.h
+++ b/scene/gui/texture_progress.h
@@ -82,6 +82,15 @@ public:
void set_nine_patch_stretch(bool p_stretch);
bool get_nine_patch_stretch() const;
+ void set_tint_under(const Color &p_tint);
+ Color get_tint_under() const;
+
+ void set_tint_progress(const Color &p_tint);
+ Color get_tint_progress() const;
+
+ void set_tint_over(const Color &p_tint);
+ Color get_tint_over() const;
+
Size2 get_minimum_size() const;
TextureProgress();
@@ -93,10 +102,11 @@ private:
Point2 rad_center_off;
bool nine_patch_stretch;
int stretch_margin[4];
+ Color tint_under, tint_progress, tint_over;
Point2 unit_val_to_uv(float val);
Point2 get_relative_center();
- void draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio);
+ void draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate);
};
VARIANT_ENUM_CAST(TextureProgress::FillMode);
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index e7f63997f2..1d27612766 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -2545,7 +2545,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
if (drag_speed == 0) {
drag_touching_deaccel = false;
drag_touching = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
} else {
drag_touching_deaccel = true;
@@ -2611,7 +2611,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
break;
if (drag_touching) {
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_touching_deaccel = false;
drag_touching = false;
drag_speed = 0;
@@ -2626,7 +2626,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
drag_touching = OS::get_singleton()->has_touchscreen_ui_hint();
drag_touching_deaccel = false;
if (drag_touching) {
- set_physics_process(true);
+ set_physics_process_internal(true);
}
if (b->get_button_index() == BUTTON_LEFT) {
@@ -2829,7 +2829,7 @@ void Tree::_notification(int p_what) {
drop_mode_flags = 0;
scrolling = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
update();
}
if (p_what == NOTIFICATION_DRAG_BEGIN) {
@@ -2837,10 +2837,10 @@ void Tree::_notification(int p_what) {
single_select_defer = NULL;
if (cache.scroll_speed > 0 && get_rect().has_point(get_viewport()->get_mouse_position() - get_global_position())) {
scrolling = true;
- set_physics_process(true);
+ set_physics_process_internal(true);
}
}
- if (p_what == NOTIFICATION_PHYSICS_PROCESS) {
+ if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
if (drag_touching) {
@@ -2853,7 +2853,7 @@ void Tree::_notification(int p_what) {
if (pos < 0) {
pos = 0;
turnoff = true;
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_touching = false;
drag_touching_deaccel = false;
}
@@ -2873,7 +2873,7 @@ void Tree::_notification(int p_what) {
drag_speed = sgn * val;
if (turnoff) {
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_touching = false;
drag_touching_deaccel = false;
}
diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp
index 4eee0126d8..88e1847533 100644
--- a/scene/gui/video_player.cpp
+++ b/scene/gui/video_player.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "video_player.h"
+#include "scene/scene_string_names.h"
#include "os/os.h"
#include "servers/audio_server.h"
@@ -159,11 +160,7 @@ void VideoPlayer::_notification(int p_notification) {
bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus);
- if (stream.is_null())
- return;
- if (paused)
- return;
- if (!playback->is_playing())
+ if (stream.is_null() || paused || !playback->is_playing())
return;
double audio_time = USEC_TO_SEC(OS::get_singleton()->get_ticks_usec());
@@ -174,7 +171,11 @@ void VideoPlayer::_notification(int p_notification) {
if (delta == 0)
return;
- playback->update(delta);
+ playback->update(delta); // playback->is_playing() returns false in the last video frame
+
+ if (!playback->is_playing()) {
+ emit_signal(SceneStringNames::get_singleton()->finished);
+ }
} break;
@@ -467,6 +468,8 @@ void VideoPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_video_texture"), &VideoPlayer::get_video_texture);
+ ADD_SIGNAL(MethodInfo("finished"));
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_track", PROPERTY_HINT_RANGE, "0,128,1"), "set_audio_track", "get_audio_track");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "VideoStream"), "set_stream", "get_stream");
//ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), "set_loop", "has_loop") ;
diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp
index 31d45d8e4c..8414210952 100644
--- a/scene/main/canvas_layer.cpp
+++ b/scene/main/canvas_layer.cpp
@@ -35,7 +35,7 @@ void CanvasLayer::set_layer(int p_xform) {
layer = p_xform;
if (viewport.is_valid())
- VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas->get_canvas(), layer);
+ VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas, layer);
}
int CanvasLayer::get_layer() const {
@@ -48,7 +48,7 @@ void CanvasLayer::set_transform(const Transform2D &p_xform) {
transform = p_xform;
locrotscale_dirty = true;
if (viewport.is_valid())
- VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas->get_canvas(), transform);
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
}
Transform2D CanvasLayer::get_transform() const {
@@ -61,7 +61,7 @@ void CanvasLayer::_update_xform() {
transform.set_rotation_and_scale(rot, scale);
transform.set_origin(ofs);
if (viewport.is_valid())
- VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas->get_canvas(), transform);
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
}
void CanvasLayer::_update_locrotscale() {
@@ -133,11 +133,6 @@ Vector2 CanvasLayer::get_scale() const {
return scale;
}
-Ref<World2D> CanvasLayer::get_world_2d() const {
-
- return canvas;
-}
-
void CanvasLayer::_notification(int p_what) {
switch (p_what) {
@@ -153,14 +148,14 @@ void CanvasLayer::_notification(int p_what) {
ERR_FAIL_COND(!vp);
viewport = vp->get_viewport_rid();
- VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas->get_canvas());
- VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas->get_canvas(), layer);
- VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas->get_canvas(), transform);
+ VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas);
+ VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas, layer);
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
} break;
case NOTIFICATION_EXIT_TREE: {
- VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas->get_canvas());
+ VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas);
viewport = RID();
} break;
@@ -184,7 +179,7 @@ RID CanvasLayer::get_viewport() const {
void CanvasLayer::set_custom_viewport(Node *p_viewport) {
ERR_FAIL_NULL(p_viewport);
if (is_inside_tree()) {
- VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas->get_canvas());
+ VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas);
viewport = RID();
}
@@ -205,9 +200,9 @@ void CanvasLayer::set_custom_viewport(Node *p_viewport) {
viewport = vp->get_viewport_rid();
- VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas->get_canvas());
- VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas->get_canvas(), layer);
- VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas->get_canvas(), transform);
+ VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas);
+ VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas, layer);
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
}
}
@@ -225,6 +220,10 @@ int CanvasLayer::get_sort_index() {
return sort_index++;
}
+RID CanvasLayer::get_canvas() const {
+
+ return canvas;
+}
void CanvasLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_layer", "layer"), &CanvasLayer::set_layer);
@@ -248,7 +247,7 @@ void CanvasLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_custom_viewport", "viewport"), &CanvasLayer::set_custom_viewport);
ClassDB::bind_method(D_METHOD("get_custom_viewport"), &CanvasLayer::get_custom_viewport);
- ClassDB::bind_method(D_METHOD("get_world_2d"), &CanvasLayer::get_world_2d);
+ ClassDB::bind_method(D_METHOD("get_canvas"), &CanvasLayer::get_canvas);
//ClassDB::bind_method(D_METHOD("get_viewport"),&CanvasLayer::get_viewport);
ADD_PROPERTY(PropertyInfo(Variant::INT, "layer", PROPERTY_HINT_RANGE, "-128,128,1"), "set_layer", "get_layer");
@@ -268,8 +267,13 @@ CanvasLayer::CanvasLayer() {
rot = 0;
locrotscale_dirty = false;
layer = 1;
- canvas = Ref<World2D>(memnew(World2D));
+ canvas = VS::get_singleton()->canvas_create();
custom_viewport = NULL;
custom_viewport_id = 0;
sort_index = 0;
}
+
+CanvasLayer::~CanvasLayer() {
+
+ VS::get_singleton()->free(canvas);
+}
diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h
index c3352a6dba..aae23fbb12 100644
--- a/scene/main/canvas_layer.h
+++ b/scene/main/canvas_layer.h
@@ -45,7 +45,7 @@ class CanvasLayer : public Node {
real_t rot;
int layer;
Transform2D transform;
- Ref<World2D> canvas;
+ RID canvas;
ObjectID custom_viewport_id; // to check validity
Viewport *custom_viewport;
@@ -81,8 +81,6 @@ public:
void set_scale(const Size2 &p_scale);
Size2 get_scale() const;
- Ref<World2D> get_world_2d() const;
-
Size2 get_viewport_size() const;
RID get_viewport() const;
@@ -93,7 +91,10 @@ public:
void reset_sort_index();
int get_sort_index();
+ RID get_canvas() const;
+
CanvasLayer();
+ ~CanvasLayer();
};
#endif // CANVAS_LAYER_H
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 28b4540573..fcf8768094 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -477,7 +477,7 @@ bool Node::is_network_master() const {
ERR_FAIL_COND_V(!is_inside_tree(), false);
- return get_tree()->get_network_unique_id() == data.network_master;
+ return get_multiplayer_api()->get_network_unique_id() == data.network_master;
}
/***** RPC CONFIG ********/
@@ -667,200 +667,16 @@ Variant Node::_rpc_unreliable_id_bind(const Variant **p_args, int p_argcount, Va
}
void Node::rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) {
-
ERR_FAIL_COND(!is_inside_tree());
-
- bool skip_rpc = false;
- bool call_local_native = false;
- bool call_local_script = false;
-
- if (p_peer_id == 0 || p_peer_id == get_tree()->get_network_unique_id() || (p_peer_id < 0 && p_peer_id != -get_tree()->get_network_unique_id())) {
- //check that send mode can use local call
-
- Map<StringName, RPCMode>::Element *E = data.rpc_methods.find(p_method);
- if (E) {
-
- switch (E->get()) {
-
- case RPC_MODE_DISABLED: {
- //do nothing
- } break;
- case RPC_MODE_REMOTE: {
- //do nothing also, no need to call local
- } break;
- case RPC_MODE_SYNC: {
- //call it, sync always results in call
- call_local_native = true;
- } break;
- case RPC_MODE_MASTER: {
- call_local_native = is_network_master();
- if (call_local_native) {
- skip_rpc = true; //no other master so..
- }
- } break;
- case RPC_MODE_SLAVE: {
- call_local_native = !is_network_master();
- } break;
- }
- }
-
- if (call_local_native) {
- // done below
- } else if (get_script_instance()) {
- //attempt with script
- ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rpc_mode(p_method);
-
- switch (rpc_mode) {
-
- case ScriptInstance::RPC_MODE_DISABLED: {
- //do nothing
- } break;
- case ScriptInstance::RPC_MODE_REMOTE: {
- //do nothing also, no need to call local
- } break;
- case ScriptInstance::RPC_MODE_SYNC: {
- //call it, sync always results in call
- call_local_script = true;
- } break;
- case ScriptInstance::RPC_MODE_MASTER: {
- call_local_script = is_network_master();
- if (call_local_script) {
- skip_rpc = true; //no other master so..
- }
- } break;
- case ScriptInstance::RPC_MODE_SLAVE: {
- call_local_script = !is_network_master();
- } break;
- }
- }
- }
-
- if (!skip_rpc) {
- get_tree()->_rpc(this, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount);
- }
-
- if (call_local_native) {
- Variant::CallError ce;
- call(p_method, p_arg, p_argcount, ce);
- if (ce.error != Variant::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(this, p_method, p_arg, p_argcount, ce);
- error = "rpc() aborted in local call: - " + error;
- ERR_PRINTS(error);
- return;
- }
- }
-
- if (call_local_script) {
- Variant::CallError ce;
- ce.error = Variant::CallError::CALL_OK;
- get_script_instance()->call(p_method, p_arg, p_argcount, ce);
- if (ce.error != Variant::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(this, p_method, p_arg, p_argcount, ce);
- error = "rpc() aborted in script local call: - " + error;
- ERR_PRINTS(error);
- return;
- }
- }
+ get_multiplayer_api()->rpcp(this, p_peer_id, p_unreliable, p_method, p_arg, p_argcount);
}
-/******** RSET *********/
-
void Node::rsetp(int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) {
-
ERR_FAIL_COND(!is_inside_tree());
-
- bool skip_rset = false;
-
- if (p_peer_id == 0 || p_peer_id == get_tree()->get_network_unique_id() || (p_peer_id < 0 && p_peer_id != -get_tree()->get_network_unique_id())) {
- //check that send mode can use local call
-
- bool set_local = false;
-
- Map<StringName, RPCMode>::Element *E = data.rpc_properties.find(p_property);
- if (E) {
-
- switch (E->get()) {
-
- case RPC_MODE_DISABLED: {
- //do nothing
- } break;
- case RPC_MODE_REMOTE: {
- //do nothing also, no need to call local
- } break;
- case RPC_MODE_SYNC: {
- //call it, sync always results in call
- set_local = true;
- } break;
- case RPC_MODE_MASTER: {
- set_local = is_network_master();
- if (set_local) {
- skip_rset = true;
- }
-
- } break;
- case RPC_MODE_SLAVE: {
- set_local = !is_network_master();
- } break;
- }
- }
-
- if (set_local) {
- bool valid;
- set(p_property, p_value, &valid);
-
- if (!valid) {
- String error = "rset() aborted in local set, property not found: - " + String(p_property);
- ERR_PRINTS(error);
- return;
- }
- } else if (get_script_instance()) {
- //attempt with script
- ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rset_mode(p_property);
-
- switch (rpc_mode) {
-
- case ScriptInstance::RPC_MODE_DISABLED: {
- //do nothing
- } break;
- case ScriptInstance::RPC_MODE_REMOTE: {
- //do nothing also, no need to call local
- } break;
- case ScriptInstance::RPC_MODE_SYNC: {
- //call it, sync always results in call
- set_local = true;
- } break;
- case ScriptInstance::RPC_MODE_MASTER: {
- set_local = is_network_master();
- if (set_local) {
- skip_rset = true;
- }
- } break;
- case ScriptInstance::RPC_MODE_SLAVE: {
- set_local = !is_network_master();
- } break;
- }
-
- if (set_local) {
-
- bool valid = get_script_instance()->set(p_property, p_value);
-
- if (!valid) {
- String error = "rset() aborted in local script set, property not found: - " + String(p_property);
- ERR_PRINTS(error);
- return;
- }
- }
- }
- }
-
- if (skip_rset)
- return;
-
- const Variant *vptr = &p_value;
-
- get_tree()->_rpc(this, p_peer_id, p_unreliable, true, p_property, &vptr, 1);
+ get_multiplayer_api()->rsetp(this, p_peer_id, p_unreliable, p_property, p_value);
}
+/******** RSET *********/
void Node::rset(const StringName &p_property, const Variant &p_value) {
rsetp(0, false, p_property, p_value);
@@ -882,6 +698,30 @@ void Node::rset_unreliable_id(int p_peer_id, const StringName &p_property, const
}
//////////// end of rpc
+Ref<MultiplayerAPI> Node::get_multiplayer_api() const {
+ if (multiplayer_api.is_valid())
+ return multiplayer_api;
+ if (!is_inside_tree())
+ return Ref<MultiplayerAPI>();
+ return get_tree()->get_multiplayer_api();
+}
+
+Ref<MultiplayerAPI> Node::get_custom_multiplayer_api() const {
+ return multiplayer_api;
+}
+
+void Node::set_custom_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api) {
+
+ multiplayer_api = p_multiplayer_api;
+}
+
+const Map<StringName, Node::RPCMode>::Element *Node::get_node_rpc_mode(const StringName &p_method) {
+ return data.rpc_methods.find(p_method);
+}
+
+const Map<StringName, Node::RPCMode>::Element *Node::get_node_rset_mode(const StringName &p_property) {
+ return data.rpc_properties.find(p_property);
+}
bool Node::can_call_rpc(const StringName &p_method, int p_from) const {
@@ -1868,11 +1708,18 @@ bool Node::has_persistent_groups() const {
return false;
}
-void Node::_print_tree(const Node *p_node) {
+void Node::_print_tree_pretty(const String prefix, const bool last) {
- print_line(String(p_node->get_path_to(this)));
- for (int i = 0; i < data.children.size(); i++)
- data.children[i]->_print_tree(p_node);
+ String new_prefix = last ? String::utf8(" â”–â•´") : String::utf8(" â” â•´");
+ print_line(prefix + new_prefix + String(get_name()));
+ for (int i = 0; i < data.children.size(); i++) {
+ new_prefix = last ? String::utf8(" ") : String::utf8(" ┃ ");
+ data.children[i]->_print_tree_pretty(prefix + new_prefix, i == data.children.size() - 1);
+ }
+}
+
+void Node::print_tree_pretty() {
+ _print_tree_pretty("", true);
}
void Node::print_tree() {
@@ -1880,6 +1727,12 @@ void Node::print_tree() {
_print_tree(this);
}
+void Node::_print_tree(const Node *p_node) {
+ print_line(String(p_node->get_path_to(this)));
+ for (int i = 0; i < data.children.size(); i++)
+ data.children[i]->_print_tree(p_node);
+}
+
void Node::_propagate_reverse_notification(int p_notification) {
data.blocked++;
@@ -2163,13 +2016,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
if (name == script_property_name)
continue;
- Variant value = N->get()->get(name);
- // Duplicate dictionaries and arrays, mainly needed for __meta__
- if (value.get_type() == Variant::DICTIONARY) {
- value = Dictionary(value).duplicate();
- } else if (value.get_type() == Variant::ARRAY) {
- value = Array(value).duplicate();
- }
+ Variant value = N->get()->get(name).duplicate(true);
if (E->get().usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE) {
@@ -2313,13 +2160,7 @@ void Node::_duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p
continue;
String name = E->get().name;
- Variant value = get(name);
- // Duplicate dictionaries and arrays, mainly needed for __meta__
- if (value.get_type() == Variant::DICTIONARY) {
- value = Dictionary(value).duplicate();
- } else if (value.get_type() == Variant::ARRAY) {
- value = Array(value).duplicate();
- }
+ Variant value = get(name).duplicate(true);
node->set(name, value);
}
@@ -2840,6 +2681,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_and_skip"), &Node::remove_and_skip);
ClassDB::bind_method(D_METHOD("get_index"), &Node::get_index);
ClassDB::bind_method(D_METHOD("print_tree"), &Node::print_tree);
+ ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty);
ClassDB::bind_method(D_METHOD("set_filename", "filename"), &Node::set_filename);
ClassDB::bind_method(D_METHOD("get_filename"), &Node::get_filename);
ClassDB::bind_method(D_METHOD("propagate_notification", "what"), &Node::propagate_notification);
@@ -2889,6 +2731,9 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_network_master"), &Node::is_network_master);
+ ClassDB::bind_method(D_METHOD("get_multiplayer_api"), &Node::get_multiplayer_api);
+ ClassDB::bind_method(D_METHOD("get_custom_multiplayer_api"), &Node::get_custom_multiplayer_api);
+ ClassDB::bind_method(D_METHOD("set_custom_multiplayer_api", "api"), &Node::set_custom_multiplayer_api);
ClassDB::bind_method(D_METHOD("rpc_config", "method", "mode"), &Node::rpc_config);
ClassDB::bind_method(D_METHOD("rset_config", "property", "mode"), &Node::rset_config);
@@ -2970,6 +2815,8 @@ void Node::_bind_methods() {
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, "", 0), "set_name", "get_name");
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", 0), "set_filename", "get_filename");
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_owner", "get_owner");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "", "get_multiplayer_api");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "custom_multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_custom_multiplayer_api", "get_custom_multiplayer_api");
BIND_VMETHOD(MethodInfo("_process", PropertyInfo(Variant::REAL, "delta")));
BIND_VMETHOD(MethodInfo("_physics_process", PropertyInfo(Variant::REAL, "delta")));
diff --git a/scene/main/node.h b/scene/main/node.h
index 341869f7ba..b9bafb1ed1 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -151,6 +151,9 @@ private:
NAME_CASING_SNAKE_CASE
};
+ Ref<MultiplayerAPI> multiplayer_api;
+
+ void _print_tree_pretty(const String prefix, const bool last);
void _print_tree(const Node *p_node);
Node *_get_node(const NodePath &p_path) const;
@@ -287,6 +290,7 @@ public:
int get_index() const;
void print_tree();
+ void print_tree_pretty();
void set_filename(const String &p_filename);
String get_filename() const;
@@ -403,15 +407,20 @@ public:
void rpc_id(int p_peer_id, const StringName &p_method, VARIANT_ARG_LIST); //rpc call, honors RPCMode
void rpc_unreliable_id(int p_peer_id, const StringName &p_method, VARIANT_ARG_LIST); //rpc call, honors RPCMode
- void rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount);
-
void rset(const StringName &p_property, const Variant &p_value); //remote set call, honors RPCMode
void rset_unreliable(const StringName &p_property, const Variant &p_value); //remote set call, honors RPCMode
void rset_id(int p_peer_id, const StringName &p_property, const Variant &p_value); //remote set call, honors RPCMode
void rset_unreliable_id(int p_peer_id, const StringName &p_property, const Variant &p_value); //remote set call, honors RPCMode
+ void rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount);
void rsetp(int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value);
+ Ref<MultiplayerAPI> get_multiplayer_api() const;
+ Ref<MultiplayerAPI> get_custom_multiplayer_api() const;
+ void set_custom_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api);
+ const Map<StringName, RPCMode>::Element *get_node_rpc_mode(const StringName &p_method);
+ const Map<StringName, RPCMode>::Element *get_node_rset_mode(const StringName &p_property);
+
bool can_call_rpc(const StringName &p_method, int p_from) const;
bool can_call_rset(const StringName &p_property, int p_from) const;
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 037331dec1..4419dfe70f 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -484,7 +484,7 @@ bool SceneTree::idle(float p_time) {
idle_process_time = p_time;
- _network_poll();
+ multiplayer_api->poll();
emit_signal("idle_frame");
@@ -1633,16 +1633,11 @@ Ref<SceneTreeTimer> SceneTree::create_timer(float p_delay_sec, bool p_process_pa
void SceneTree::_network_peer_connected(int p_id) {
- connected_peers.insert(p_id);
- path_get_cache.insert(p_id, PathGetCache());
-
emit_signal("network_peer_connected", p_id);
}
void SceneTree::_network_peer_disconnected(int p_id) {
- connected_peers.erase(p_id);
- path_get_cache.erase(p_id); //I no longer need your cache, sorry
emit_signal("network_peer_disconnected", p_id);
}
@@ -1661,471 +1656,70 @@ void SceneTree::_server_disconnected() {
emit_signal("server_disconnected");
}
-void SceneTree::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer) {
- if (network_peer.is_valid()) {
- network_peer->disconnect("peer_connected", this, "_network_peer_connected");
- network_peer->disconnect("peer_disconnected", this, "_network_peer_disconnected");
- network_peer->disconnect("connection_succeeded", this, "_connected_to_server");
- network_peer->disconnect("connection_failed", this, "_connection_failed");
- network_peer->disconnect("server_disconnected", this, "_server_disconnected");
- connected_peers.clear();
- path_get_cache.clear();
- path_send_cache.clear();
- last_send_cache_id = 1;
+Ref<MultiplayerAPI> SceneTree::get_multiplayer_api() const {
+ return multiplayer_api;
+}
+
+void SceneTree::set_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api) {
+ ERR_FAIL_COND(!p_multiplayer_api.is_valid());
+
+ if (multiplayer_api.is_valid()) {
+ multiplayer_api->disconnect("network_peer_connected", this, "_network_peer_connected");
+ multiplayer_api->disconnect("network_peer_disconnected", this, "_network_peer_disconnected");
+ multiplayer_api->disconnect("connected_to_server", this, "_connected_to_server");
+ multiplayer_api->disconnect("connection_failed", this, "_connection_failed");
+ multiplayer_api->disconnect("server_disconnected", this, "_server_disconnected");
}
- ERR_EXPLAIN("Supplied NetworkedNetworkPeer must be connecting or connected.");
- ERR_FAIL_COND(p_network_peer.is_valid() && p_network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED);
+ multiplayer_api = p_multiplayer_api;
+ multiplayer_api->set_root_node(root);
- network_peer = p_network_peer;
+ multiplayer_api->connect("network_peer_connected", this, "_network_peer_connected");
+ multiplayer_api->connect("network_peer_disconnected", this, "_network_peer_disconnected");
+ multiplayer_api->connect("connected_to_server", this, "_connected_to_server");
+ multiplayer_api->connect("connection_failed", this, "_connection_failed");
+ multiplayer_api->connect("server_disconnected", this, "_server_disconnected");
+}
- if (network_peer.is_valid()) {
- network_peer->connect("peer_connected", this, "_network_peer_connected");
- network_peer->connect("peer_disconnected", this, "_network_peer_disconnected");
- network_peer->connect("connection_succeeded", this, "_connected_to_server");
- network_peer->connect("connection_failed", this, "_connection_failed");
- network_peer->connect("server_disconnected", this, "_server_disconnected");
- }
+void SceneTree::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer) {
+
+ multiplayer_api->set_network_peer(p_network_peer);
}
Ref<NetworkedMultiplayerPeer> SceneTree::get_network_peer() const {
- return network_peer;
+ return multiplayer_api->get_network_peer();
}
bool SceneTree::is_network_server() const {
- ERR_FAIL_COND_V(!network_peer.is_valid(), false);
- return network_peer->is_server();
+ return multiplayer_api->is_network_server();
}
bool SceneTree::has_network_peer() const {
- return network_peer.is_valid();
+ return multiplayer_api->has_network_peer();
}
int SceneTree::get_network_unique_id() const {
- ERR_FAIL_COND_V(!network_peer.is_valid(), 0);
- return network_peer->get_unique_id();
+ return multiplayer_api->get_network_unique_id();
}
Vector<int> SceneTree::get_network_connected_peers() const {
- ERR_FAIL_COND_V(!network_peer.is_valid(), Vector<int>());
-
- Vector<int> ret;
- for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
- ret.push_back(E->get());
- }
- return ret;
+ return multiplayer_api->get_network_connected_peers();
}
int SceneTree::get_rpc_sender_id() const {
- return rpc_sender_id;
+ return multiplayer_api->get_rpc_sender_id();
}
void SceneTree::set_refuse_new_network_connections(bool p_refuse) {
- ERR_FAIL_COND(!network_peer.is_valid());
- network_peer->set_refuse_new_connections(p_refuse);
+ multiplayer_api->set_refuse_new_network_connections(p_refuse);
}
bool SceneTree::is_refusing_new_network_connections() const {
-
- ERR_FAIL_COND_V(!network_peer.is_valid(), false);
-
- return network_peer->is_refusing_new_connections();
-}
-
-void SceneTree::_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount) {
-
- if (network_peer.is_null()) {
- ERR_EXPLAIN("Attempt to remote call/set when networking is not active in SceneTree.");
- ERR_FAIL();
- }
-
- if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING) {
- ERR_EXPLAIN("Attempt to remote call/set when networking is not connected yet in SceneTree.");
- ERR_FAIL();
- }
-
- if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) {
- ERR_EXPLAIN("Attempt to remote call/set when networking is disconnected.");
- ERR_FAIL();
- }
-
- if (p_argcount > 255) {
- ERR_EXPLAIN("Too many arguments >255.");
- ERR_FAIL();
- }
-
- if (p_to != 0 && !connected_peers.has(ABS(p_to))) {
- if (p_to == get_network_unique_id()) {
- ERR_EXPLAIN("Attempt to remote call/set yourself! unique ID: " + itos(get_network_unique_id()));
- } else {
- ERR_EXPLAIN("Attempt to remote call unexisting ID: " + itos(p_to));
- }
-
- ERR_FAIL();
- }
-
- NodePath from_path = p_from->get_path();
- ERR_FAIL_COND(from_path.is_empty());
-
- //see if the path is cached
- PathSentCache *psc = path_send_cache.getptr(from_path);
- if (!psc) {
- //path is not cached, create
- path_send_cache[from_path] = PathSentCache();
- psc = path_send_cache.getptr(from_path);
- psc->id = last_send_cache_id++;
- }
-
- //create base packet, lots of hardcode because it must be tight
-
- int ofs = 0;
-
-#define MAKE_ROOM(m_amount) \
- if (packet_cache.size() < m_amount) packet_cache.resize(m_amount);
-
- //encode type
- MAKE_ROOM(1);
- packet_cache[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL;
- ofs += 1;
-
- //encode ID
- MAKE_ROOM(ofs + 4);
- encode_uint32(psc->id, &packet_cache[ofs]);
- ofs += 4;
-
- //encode function name
- CharString name = String(p_name).utf8();
- int len = encode_cstring(name.get_data(), NULL);
- MAKE_ROOM(ofs + len);
- encode_cstring(name.get_data(), &packet_cache[ofs]);
- ofs += len;
-
- if (p_set) {
- //set argument
- Error err = encode_variant(*p_arg[0], NULL, len);
- ERR_FAIL_COND(err != OK);
- MAKE_ROOM(ofs + len);
- encode_variant(*p_arg[0], &packet_cache[ofs], len);
- ofs += len;
-
- } else {
- //call arguments
- MAKE_ROOM(ofs + 1);
- packet_cache[ofs] = p_argcount;
- ofs += 1;
- for (int i = 0; i < p_argcount; i++) {
- Error err = encode_variant(*p_arg[i], NULL, len);
- ERR_FAIL_COND(err != OK);
- MAKE_ROOM(ofs + len);
- encode_variant(*p_arg[i], &packet_cache[ofs], len);
- ofs += len;
- }
- }
-
- //see if all peers have cached path (is so, call can be fast)
- bool has_all_peers = true;
-
- List<int> peers_to_add; //if one is missing, take note to add it
-
- for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
-
- if (p_to < 0 && E->get() == -p_to)
- continue; //continue, excluded
-
- if (p_to > 0 && E->get() != p_to)
- continue; //continue, not for this peer
-
- Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get());
-
- if (!F || F->get() == false) {
- //path was not cached, or was cached but is unconfirmed
- if (!F) {
- //not cached at all, take note
- peers_to_add.push_back(E->get());
- }
-
- has_all_peers = false;
- }
- }
-
- //those that need to be added, send a message for this
-
- for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) {
-
- //encode function name
- CharString pname = String(from_path).utf8();
- int len = encode_cstring(pname.get_data(), NULL);
-
- Vector<uint8_t> packet;
-
- packet.resize(1 + 4 + len);
- packet[0] = NETWORK_COMMAND_SIMPLIFY_PATH;
- encode_uint32(psc->id, &packet[1]);
- encode_cstring(pname.get_data(), &packet[5]);
-
- network_peer->set_target_peer(E->get()); //to all of you
- network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
- network_peer->put_packet(packet.ptr(), packet.size());
-
- psc->confirmed_peers.insert(E->get(), false); //insert into confirmed, but as false since it was not confirmed
- }
-
- //take chance and set transfer mode, since all send methods will use it
- network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
-
- if (has_all_peers) {
-
- //they all have verified paths, so send fast
- network_peer->set_target_peer(p_to); //to all of you
- network_peer->put_packet(packet_cache.ptr(), ofs); //a message with love
- } else {
- //not all verified path, so send one by one
-
- //apend path at the end, since we will need it for some packets
- CharString pname = String(from_path).utf8();
- int path_len = encode_cstring(pname.get_data(), NULL);
- MAKE_ROOM(ofs + path_len);
- encode_cstring(pname.get_data(), &packet_cache[ofs]);
-
- for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
-
- if (p_to < 0 && E->get() == -p_to)
- continue; //continue, excluded
-
- if (p_to > 0 && E->get() != p_to)
- continue; //continue, not for this peer
-
- Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get());
- ERR_CONTINUE(!F); //should never happen
-
- network_peer->set_target_peer(E->get()); //to this one specifically
-
- if (F->get() == true) {
- //this one confirmed path, so use id
- encode_uint32(psc->id, &packet_cache[1]);
- network_peer->put_packet(packet_cache.ptr(), ofs);
- } else {
- //this one did not confirm path yet, so use entire path (sorry!)
- encode_uint32(0x80000000 | ofs, &packet_cache[1]); //offset to path and flag
- network_peer->put_packet(packet_cache.ptr(), ofs + path_len);
- }
- }
- }
-}
-
-void SceneTree::_network_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
-
- ERR_FAIL_COND(p_packet_len < 5);
-
- uint8_t packet_type = p_packet[0];
-
- switch (packet_type) {
-
- case NETWORK_COMMAND_REMOTE_CALL:
- case NETWORK_COMMAND_REMOTE_SET: {
-
- ERR_FAIL_COND(p_packet_len < 5);
- uint32_t target = decode_uint32(&p_packet[1]);
-
- Node *node = NULL;
-
- if (target & 0x80000000) {
- //use full path (not cached yet)
-
- int ofs = target & 0x7FFFFFFF;
- ERR_FAIL_COND(ofs >= p_packet_len);
-
- String paths;
- paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs);
-
- NodePath np = paths;
-
- node = get_root()->get_node(np);
- if (node == NULL) {
- ERR_EXPLAIN("Failed to get path from RPC: " + String(np));
- ERR_FAIL_COND(node == NULL);
- }
- } else {
- //use cached path
- int id = target;
-
- Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from);
- ERR_FAIL_COND(!E);
-
- Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id);
- ERR_FAIL_COND(!F);
-
- PathGetCache::NodeInfo *ni = &F->get();
- //do proper caching later
-
- node = get_root()->get_node(ni->path);
- if (node == NULL) {
- ERR_EXPLAIN("Failed to get cached path from RPC: " + String(ni->path));
- ERR_FAIL_COND(node == NULL);
- }
- }
-
- ERR_FAIL_COND(p_packet_len < 6);
-
- //detect cstring end
- int len_end = 5;
- for (; len_end < p_packet_len; len_end++) {
- if (p_packet[len_end] == 0) {
- break;
- }
- }
-
- ERR_FAIL_COND(len_end >= p_packet_len);
-
- StringName name = String::utf8((const char *)&p_packet[5]);
-
- if (packet_type == NETWORK_COMMAND_REMOTE_CALL) {
-
- if (!node->can_call_rpc(name, p_from))
- return;
-
- int ofs = len_end + 1;
-
- ERR_FAIL_COND(ofs >= p_packet_len);
-
- int argc = p_packet[ofs];
- Vector<Variant> args;
- Vector<const Variant *> argp;
- args.resize(argc);
- argp.resize(argc);
-
- ofs++;
-
- for (int i = 0; i < argc; i++) {
-
- ERR_FAIL_COND(ofs >= p_packet_len);
- int vlen;
- Error err = decode_variant(args[i], &p_packet[ofs], p_packet_len - ofs, &vlen);
- ERR_FAIL_COND(err != OK);
- //args[i]=p_packet[3+i];
- argp[i] = &args[i];
- ofs += vlen;
- }
-
- Variant::CallError ce;
-
- node->call(name, (const Variant **)argp.ptr(), argc, ce);
- if (ce.error != Variant::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(node, name, (const Variant **)argp.ptr(), argc, ce);
- error = "RPC - " + error;
- ERR_PRINTS(error);
- }
-
- } else {
-
- if (!node->can_call_rset(name, p_from))
- return;
-
- int ofs = len_end + 1;
-
- ERR_FAIL_COND(ofs >= p_packet_len);
-
- Variant value;
- decode_variant(value, &p_packet[ofs], p_packet_len - ofs);
-
- bool valid;
-
- node->set(name, value, &valid);
- if (!valid) {
- String error = "Error setting remote property '" + String(name) + "', not found in object of type " + node->get_class();
- ERR_PRINTS(error);
- }
- }
-
- } break;
- case NETWORK_COMMAND_SIMPLIFY_PATH: {
-
- ERR_FAIL_COND(p_packet_len < 5);
- int id = decode_uint32(&p_packet[1]);
-
- String paths;
- paths.parse_utf8((const char *)&p_packet[5], p_packet_len - 5);
-
- NodePath path = paths;
-
- if (!path_get_cache.has(p_from)) {
- path_get_cache[p_from] = PathGetCache();
- }
-
- PathGetCache::NodeInfo ni;
- ni.path = path;
- ni.instance = 0;
-
- path_get_cache[p_from].nodes[id] = ni;
-
- {
- //send ack
-
- //encode path
- CharString pname = String(path).utf8();
- int len = encode_cstring(pname.get_data(), NULL);
-
- Vector<uint8_t> packet;
-
- packet.resize(1 + len);
- packet[0] = NETWORK_COMMAND_CONFIRM_PATH;
- encode_cstring(pname.get_data(), &packet[1]);
-
- network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
- network_peer->set_target_peer(p_from);
- network_peer->put_packet(packet.ptr(), packet.size());
- }
- } break;
- case NETWORK_COMMAND_CONFIRM_PATH: {
-
- String paths;
- paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1);
-
- NodePath path = paths;
-
- PathSentCache *psc = path_send_cache.getptr(path);
- ERR_FAIL_COND(!psc);
-
- Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from);
- ERR_FAIL_COND(!E);
- E->get() = true;
- } break;
- }
-}
-
-void SceneTree::_network_poll() {
-
- if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED)
- return;
-
- network_peer->poll();
-
- if (!network_peer.is_valid()) //it's possible that polling might have resulted in a disconnection, so check here
- return;
-
- while (network_peer->get_available_packet_count()) {
-
- int sender = network_peer->get_packet_peer();
- const uint8_t *packet;
- int len;
-
- Error err = network_peer->get_packet(&packet, len);
- if (err != OK) {
- ERR_PRINT("Error getting packet!");
- }
-
- rpc_sender_id = sender;
- _network_process_packet(sender, packet, len);
- rpc_sender_id = 0;
-
- if (!network_peer.is_valid()) {
- break; //it's also possible that a packet or RPC caused a disconnection, so also check here
- }
- }
+ return multiplayer_api->is_refusing_new_network_connections();
}
void SceneTree::_bind_methods() {
@@ -2196,6 +1790,8 @@ void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("_change_scene"), &SceneTree::_change_scene);
+ ClassDB::bind_method(D_METHOD("set_multiplayer_api", "multiplayer_api"), &SceneTree::set_multiplayer_api);
+ ClassDB::bind_method(D_METHOD("get_multiplayer_api"), &SceneTree::get_multiplayer_api);
ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &SceneTree::set_network_peer);
ClassDB::bind_method(D_METHOD("get_network_peer"), &SceneTree::get_network_peer);
ClassDB::bind_method(D_METHOD("is_network_server"), &SceneTree::is_network_server);
@@ -2225,6 +1821,7 @@ void SceneTree::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "current_scene", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_current_scene", "get_current_scene");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", 0), "set_network_peer", "get_network_peer");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "", "get_root");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_multiplayer_api", "get_multiplayer_api");
ADD_SIGNAL(MethodInfo("tree_changed"));
ADD_SIGNAL(MethodInfo("node_added", PropertyInfo(Variant::OBJECT, "node")));
@@ -2320,7 +1917,6 @@ SceneTree::SceneTree() {
call_lock = 0;
root_lock = 0;
node_count = 0;
- rpc_sender_id = 0;
//create with mainloop
@@ -2329,6 +1925,9 @@ SceneTree::SceneTree() {
if (!root->get_world().is_valid())
root->set_world(Ref<World>(memnew(World)));
+ // Initialize network state
+ set_multiplayer_api(Ref<MultiplayerAPI>(memnew(MultiplayerAPI)));
+
//root->set_world_2d( Ref<World2D>( memnew( World2D )));
root->set_as_audio_listener(true);
root->set_as_audio_listener_2d(true);
@@ -2423,8 +2022,6 @@ SceneTree::SceneTree() {
live_edit_root = NodePath("/root");
- last_send_cache_id = 1;
-
#endif
use_font_oversampling = false;
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index c5357762ec..203c1d9c9c 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -31,7 +31,7 @@
#ifndef SCENE_MAIN_LOOP_H
#define SCENE_MAIN_LOOP_H
-#include "io/networked_multiplayer_peer.h"
+#include "io/multiplayer_api.h"
#include "os/main_loop.h"
#include "os/thread_safe.h"
#include "scene/resources/mesh.h"
@@ -185,16 +185,8 @@ private:
///network///
- enum NetworkCommands {
- NETWORK_COMMAND_REMOTE_CALL,
- NETWORK_COMMAND_REMOTE_SET,
- NETWORK_COMMAND_SIMPLIFY_PATH,
- NETWORK_COMMAND_CONFIRM_PATH,
- };
-
- Ref<NetworkedMultiplayerPeer> network_peer;
+ Ref<MultiplayerAPI> multiplayer_api;
- Set<int> connected_peers;
void _network_peer_connected(int p_id);
void _network_peer_disconnected(int p_id);
@@ -202,39 +194,9 @@ private:
void _connection_failed();
void _server_disconnected();
- int rpc_sender_id;
-
- //path sent caches
- struct PathSentCache {
- Map<int, bool> confirmed_peers;
- int id;
- };
-
- HashMap<NodePath, PathSentCache> path_send_cache;
- int last_send_cache_id;
-
- //path get caches
- struct PathGetCache {
- struct NodeInfo {
- NodePath path;
- ObjectID instance;
- };
-
- Map<int, NodeInfo> nodes;
- };
-
- Map<int, PathGetCache> path_get_cache;
-
- Vector<uint8_t> packet_cache;
-
- void _network_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len);
- void _network_poll();
-
static SceneTree *singleton;
friend class Node;
- void _rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount);
-
void tree_changed();
void node_added(Node *p_node);
void node_removed(Node *p_node);
@@ -450,6 +412,8 @@ public:
//network API
+ Ref<MultiplayerAPI> get_multiplayer_api() const;
+ void set_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api);
void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer);
Ref<NetworkedMultiplayerPeer> get_network_peer() const;
bool is_network_server() const;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 08fbf44469..568a765420 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -181,6 +181,7 @@ public:
Viewport::GUI::GUI() {
mouse_focus = NULL;
+ mouse_click_grabber = NULL;
mouse_focus_button = -1;
key_focus = NULL;
mouse_over = NULL;
@@ -1813,7 +1814,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
if (!over) {
- OS::get_singleton()->set_cursor_shape(OS::CURSOR_ARROW);
+ OS::get_singleton()->set_cursor_shape((OS::CursorShape)Input::get_singleton()->get_default_cursor_shape());
return;
}
@@ -2278,7 +2279,7 @@ List<Control *>::Element *Viewport::_gui_show_modal(Control *p_control) {
else
p_control->_modal_set_prev_focus_owner(0);
- if (gui.mouse_focus && !p_control->is_a_parent_of(gui.mouse_focus)) {
+ if (gui.mouse_focus && !p_control->is_a_parent_of(gui.mouse_focus) && !gui.mouse_click_grabber) {
Ref<InputEventMouseButton> mb;
mb.instance();
mb->set_position(gui.mouse_focus->get_local_mouse_position());
@@ -2300,9 +2301,22 @@ Control *Viewport::_gui_get_focus_owner() {
void Viewport::_gui_grab_click_focus(Control *p_control) {
+ gui.mouse_click_grabber = p_control;
+ call_deferred("_post_gui_grab_click_focus");
+}
+
+void Viewport::_post_gui_grab_click_focus() {
+
+ Control *focus_grabber = gui.mouse_click_grabber;
+ if (!focus_grabber) {
+ // Redundant grab requests were made
+ return;
+ }
+ gui.mouse_click_grabber = NULL;
+
if (gui.mouse_focus) {
- if (gui.mouse_focus == p_control)
+ if (gui.mouse_focus == focus_grabber)
return;
Ref<InputEventMouseButton> mb;
mb.instance();
@@ -2313,9 +2327,9 @@ void Viewport::_gui_grab_click_focus(Control *p_control) {
mb->set_position(click);
mb->set_button_index(gui.mouse_focus_button);
mb->set_pressed(false);
- gui.mouse_focus->call_deferred(SceneStringNames::get_singleton()->_gui_input, mb);
+ gui.mouse_focus->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb);
- gui.mouse_focus = p_control;
+ gui.mouse_focus = focus_grabber;
gui.focus_inv_xform = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse();
click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
mb->set_position(click);
@@ -2648,6 +2662,7 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_show_tooltip"), &Viewport::_gui_show_tooltip);
ClassDB::bind_method(D_METHOD("_gui_remove_focus"), &Viewport::_gui_remove_focus);
+ ClassDB::bind_method(D_METHOD("_post_gui_grab_click_focus"), &Viewport::_post_gui_grab_click_focus);
ClassDB::bind_method(D_METHOD("set_shadow_atlas_size", "size"), &Viewport::set_shadow_atlas_size);
ClassDB::bind_method(D_METHOD("get_shadow_atlas_size"), &Viewport::get_shadow_atlas_size);
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 07bbd3f1fa..94e49033e0 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -248,6 +248,7 @@ private:
bool key_event_accepted;
Control *mouse_focus;
+ Control *mouse_click_grabber;
int mouse_focus_button;
Control *key_focus;
Control *mouse_over;
@@ -323,6 +324,7 @@ private:
bool _gui_control_has_focus(const Control *p_control);
void _gui_control_grab_focus(Control *p_control);
void _gui_grab_click_focus(Control *p_control);
+ void _post_gui_grab_click_focus();
void _gui_accept_event();
Control *_gui_get_focus_owner();
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 3e244aa8f8..ea70797530 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -582,6 +582,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("checked", "PopupMenu", make_icon(checked_png));
theme->set_icon("unchecked", "PopupMenu", make_icon(unchecked_png));
+ theme->set_icon("radio_checked", "PopupMenu", make_icon(radio_checked_png));
+ theme->set_icon("radio_unchecked", "PopupMenu", make_icon(radio_unchecked_png));
theme->set_icon("submenu", "PopupMenu", make_icon(submenu_png));
theme->set_font("font", "PopupMenu", default_font);
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 3df9285bb6..846f6e356e 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -249,6 +249,8 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
//must make a copy, because this res is local to scene
}
}
+ } else if (p_edit_state == GEN_EDIT_STATE_INSTANCE) {
+ value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor
}
node->set(snames[nprops[j].name], value, &valid);
}
diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp
index 030b822f3b..597866eb74 100644
--- a/scene/resources/scene_format_text.cpp
+++ b/scene/resources/scene_format_text.cpp
@@ -1672,7 +1672,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
f->store_string(vars);
}
- f->store_line("]\n");
+ f->store_line("]");
for (int j = 0; j < state->get_node_property_count(i); j++) {
@@ -1682,10 +1682,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
f->store_string(_valprop(String(state->get_node_property_name(i, j))) + " = " + vars + "\n");
}
- if (state->get_node_property_count(i)) {
- //add space
- f->store_line(String());
- }
+ f->store_line(String());
}
for (int i = 0; i < state->get_connection_count(); i++) {
@@ -1708,14 +1705,12 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
f->store_string(" binds= " + vars);
}
- f->store_line("]\n");
+ f->store_line("]");
}
- f->store_line(String());
-
Vector<NodePath> editable_instances = state->get_editable_instances();
for (int i = 0; i < editable_instances.size(); i++) {
- f->store_line("[editable path=\"" + editable_instances[i].operator String() + "\"]");
+ f->store_line("\n[editable path=\"" + editable_instances[i].operator String() + "\"]");
}
}
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 07c1036a10..5a42873d79 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -861,7 +861,7 @@ void SurfaceTool::generate_tangents() {
}
}
-void SurfaceTool::generate_normals() {
+void SurfaceTool::generate_normals(bool p_flip) {
ERR_FAIL_COND(primitive != Mesh::PRIMITIVE_TRIANGLES);
@@ -887,7 +887,11 @@ void SurfaceTool::generate_normals() {
ERR_FAIL_COND(!v[2]);
E = v[2]->next();
- Vector3 normal = Plane(v[0]->get().vertex, v[1]->get().vertex, v[2]->get().vertex).normal;
+ Vector3 normal;
+ if (!p_flip)
+ normal = Plane(v[0]->get().vertex, v[1]->get().vertex, v[2]->get().vertex).normal;
+ else
+ normal = Plane(v[2]->get().vertex, v[1]->get().vertex, v[0]->get().vertex).normal;
if (smooth) {
@@ -980,7 +984,7 @@ void SurfaceTool::_bind_methods() {
ClassDB::bind_method(D_METHOD("index"), &SurfaceTool::index);
ClassDB::bind_method(D_METHOD("deindex"), &SurfaceTool::deindex);
- ClassDB::bind_method(D_METHOD("generate_normals"), &SurfaceTool::generate_normals);
+ ClassDB::bind_method(D_METHOD("generate_normals", "flip"), &SurfaceTool::generate_normals, DEFVAL(false));
ClassDB::bind_method(D_METHOD("generate_tangents"), &SurfaceTool::generate_tangents);
ClassDB::bind_method(D_METHOD("add_to_format", "flags"), &SurfaceTool::add_to_format);
diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h
index 7a9aa349bb..459d399380 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -116,7 +116,7 @@ public:
void index();
void deindex();
- void generate_normals();
+ void generate_normals(bool p_flip = false);
void generate_tangents();
void add_to_format(int p_flags) { format |= p_flags; }
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 4463f98b07..bebbf6e238 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -347,6 +347,7 @@ void TileSet::tile_set_modulate(int p_id, const Color &p_modulate) {
ERR_FAIL_COND(!tile_map.has(p_id));
tile_map[p_id].modulate = p_modulate;
emit_changed();
+ _change_notify("modulate");
}
Color TileSet::tile_get_modulate(int p_id) const {
@@ -918,6 +919,8 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("tile_get_shape_count", "id"), &TileSet::tile_get_shape_count);
ClassDB::bind_method(D_METHOD("tile_set_shapes", "id", "shapes"), &TileSet::_tile_set_shapes);
ClassDB::bind_method(D_METHOD("tile_get_shapes", "id"), &TileSet::_tile_get_shapes);
+ ClassDB::bind_method(D_METHOD("tile_set_tile_mode", "id", "tilemode"), &TileSet::tile_set_tile_mode);
+ ClassDB::bind_method(D_METHOD("tile_get_tile_mode", "id"), &TileSet::tile_get_tile_mode);
ClassDB::bind_method(D_METHOD("tile_set_navigation_polygon", "id", "navigation_polygon"), &TileSet::tile_set_navigation_polygon);
ClassDB::bind_method(D_METHOD("tile_get_navigation_polygon", "id"), &TileSet::tile_get_navigation_polygon);
ClassDB::bind_method(D_METHOD("tile_set_navigation_polygon_offset", "id", "navigation_polygon_offset"), &TileSet::tile_set_navigation_polygon_offset);
@@ -947,6 +950,10 @@ void TileSet::_bind_methods() {
BIND_ENUM_CONSTANT(BIND_BOTTOMLEFT);
BIND_ENUM_CONSTANT(BIND_BOTTOM);
BIND_ENUM_CONSTANT(BIND_BOTTOMRIGHT);
+
+ BIND_ENUM_CONSTANT(SINGLE_TILE);
+ BIND_ENUM_CONSTANT(AUTO_TILE);
+ BIND_ENUM_CONSTANT(ANIMATED_TILE);
}
TileSet::TileSet() {
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 46f34b6252..706d04998f 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -238,5 +238,6 @@ public:
VARIANT_ENUM_CAST(TileSet::AutotileBindings);
VARIANT_ENUM_CAST(TileSet::BitmaskMode);
+VARIANT_ENUM_CAST(TileSet::TileMode);
#endif // TILE_SET_H
diff --git a/servers/arvr_server.cpp b/servers/arvr_server.cpp
index f9d402fe7b..f48bedbdac 100644
--- a/servers/arvr_server.cpp
+++ b/servers/arvr_server.cpp
@@ -58,6 +58,8 @@ void ARVRServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_primary_interface"), &ARVRServer::get_primary_interface);
ClassDB::bind_method(D_METHOD("set_primary_interface", "interface"), &ARVRServer::set_primary_interface);
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "primary_interface"), "set_primary_interface", "get_primary_interface");
+
ClassDB::bind_method(D_METHOD("get_last_process_usec"), &ARVRServer::get_last_process_usec);
ClassDB::bind_method(D_METHOD("get_last_commit_usec"), &ARVRServer::get_last_commit_usec);
ClassDB::bind_method(D_METHOD("get_last_frame_usec"), &ARVRServer::get_last_frame_usec);
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index c3b0de6d9a..b08e41301a 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -101,6 +101,18 @@ int AudioDriver::get_total_channels_by_speaker_mode(AudioDriver::SpeakerMode p_m
ERR_FAIL_V(2);
}
+Array AudioDriver::get_device_list() {
+ Array list;
+
+ list.push_back("Default");
+
+ return list;
+}
+
+String AudioDriver::get_device() {
+ return "Default";
+}
+
AudioDriver::AudioDriver() {
_last_mix_time = 0;
@@ -1108,6 +1120,21 @@ Ref<AudioBusLayout> AudioServer::generate_bus_layout() const {
return state;
}
+Array AudioServer::get_device_list() {
+
+ return AudioDriver::get_singleton()->get_device_list();
+}
+
+String AudioServer::get_device() {
+
+ return AudioDriver::get_singleton()->get_device();
+}
+
+void AudioServer::set_device(String device) {
+
+ AudioDriver::get_singleton()->set_device(device);
+}
+
void AudioServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bus_count", "amount"), &AudioServer::set_bus_count);
@@ -1154,6 +1181,9 @@ void AudioServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_speaker_mode"), &AudioServer::get_speaker_mode);
ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioServer::get_mix_rate);
+ ClassDB::bind_method(D_METHOD("get_device_list"), &AudioServer::get_device_list);
+ ClassDB::bind_method(D_METHOD("get_device"), &AudioServer::get_device);
+ ClassDB::bind_method(D_METHOD("set_device"), &AudioServer::set_device);
ClassDB::bind_method(D_METHOD("set_bus_layout", "bus_layout"), &AudioServer::set_bus_layout);
ClassDB::bind_method(D_METHOD("generate_bus_layout"), &AudioServer::generate_bus_layout);
diff --git a/servers/audio_server.h b/servers/audio_server.h
index a8be48b4c3..af2668b69e 100644
--- a/servers/audio_server.h
+++ b/servers/audio_server.h
@@ -70,6 +70,9 @@ public:
virtual void start() = 0;
virtual int get_mix_rate() const = 0;
virtual SpeakerMode get_speaker_mode() const = 0;
+ virtual Array get_device_list();
+ virtual String get_device();
+ virtual void set_device(String device) {}
virtual void lock() = 0;
virtual void unlock() = 0;
virtual void finish() = 0;
@@ -300,6 +303,10 @@ public:
void set_bus_layout(const Ref<AudioBusLayout> &p_bus_layout);
Ref<AudioBusLayout> generate_bus_layout() const;
+ Array get_device_list();
+ String get_device();
+ void set_device(String device);
+
AudioServer();
virtual ~AudioServer();
};
diff --git a/servers/physics/collision_object_sw.h b/servers/physics/collision_object_sw.h
index f5d32e56a0..dee28bb6df 100644
--- a/servers/physics/collision_object_sw.h
+++ b/servers/physics/collision_object_sw.h
@@ -37,7 +37,8 @@
#include "shape_sw.h"
#ifdef DEBUG_ENABLED
-#define MAX_OBJECT_DISTANCE 10000000.0
+#define MAX_OBJECT_DISTANCE 3.1622776601683791e+18
+
#define MAX_OBJECT_DISTANCE_X2 (MAX_OBJECT_DISTANCE * MAX_OBJECT_DISTANCE)
#endif
diff --git a/servers/physics_2d/collision_object_2d_sw.cpp b/servers/physics_2d/collision_object_2d_sw.cpp
index ce06aa9a2b..23084a4241 100644
--- a/servers/physics_2d/collision_object_2d_sw.cpp
+++ b/servers/physics_2d/collision_object_2d_sw.cpp
@@ -73,6 +73,27 @@ void CollisionObject2DSW::set_shape_transform(int p_index, const Transform2D &p_
_shapes_changed();
}
+void CollisionObject2DSW::set_shape_as_disabled(int p_idx, bool p_disabled) {
+ ERR_FAIL_INDEX(p_idx, shapes.size());
+
+ CollisionObject2DSW::Shape &shape = shapes[p_idx];
+ if (shape.disabled == p_disabled)
+ return;
+
+ shape.disabled = p_disabled;
+
+ if (!space)
+ return;
+
+ if (p_disabled && shape.bpid != 0) {
+ space->get_broadphase()->remove(shape.bpid);
+ shape.bpid = 0;
+ _update_shapes();
+ } else if (!p_disabled && shape.bpid == 0) {
+ _update_shapes(); // automatically adds shape with bpid == 0
+ }
+}
+
void CollisionObject2DSW::remove_shape(Shape2DSW *p_shape) {
//remove a shape, all the times it appears
@@ -139,6 +160,10 @@ void CollisionObject2DSW::_update_shapes() {
for (int i = 0; i < shapes.size(); i++) {
Shape &s = shapes[i];
+
+ if (s.disabled)
+ continue;
+
if (s.bpid == 0) {
s.bpid = space->get_broadphase()->create(this, i);
space->get_broadphase()->set_static(s.bpid, _static);
@@ -163,6 +188,9 @@ void CollisionObject2DSW::_update_shapes_with_motion(const Vector2 &p_motion) {
for (int i = 0; i < shapes.size(); i++) {
Shape &s = shapes[i];
+ if (s.disabled)
+ continue;
+
if (s.bpid == 0) {
s.bpid = space->get_broadphase()->create(this, i);
space->get_broadphase()->set_static(s.bpid, _static);
diff --git a/servers/physics_2d/collision_object_2d_sw.h b/servers/physics_2d/collision_object_2d_sw.h
index 5f25c27158..ab3e219ac0 100644
--- a/servers/physics_2d/collision_object_2d_sw.h
+++ b/servers/physics_2d/collision_object_2d_sw.h
@@ -136,10 +136,7 @@ public:
_FORCE_INLINE_ Transform2D get_inv_transform() const { return inv_transform; }
_FORCE_INLINE_ Space2DSW *get_space() const { return space; }
- _FORCE_INLINE_ void set_shape_as_disabled(int p_idx, bool p_disabled) {
- ERR_FAIL_INDEX(p_idx, shapes.size());
- shapes[p_idx].disabled = p_disabled;
- }
+ void set_shape_as_disabled(int p_idx, bool p_disabled);
_FORCE_INLINE_ bool is_shape_set_as_disabled(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, shapes.size(), false);
return shapes[p_idx].disabled;
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp
index d5a9bfb606..37aeef8999 100644
--- a/servers/visual/shader_language.cpp
+++ b/servers/visual/shader_language.cpp
@@ -1492,11 +1492,6 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
{ "abs", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID } },
{ "abs", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID } },
- { "abs", TYPE_UINT, { TYPE_UINT, TYPE_VOID } },
- { "abs", TYPE_UVEC2, { TYPE_UVEC2, TYPE_VOID } },
- { "abs", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID } },
- { "abs", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID } },
-
{ "sign", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } },
{ "sign", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } },
{ "sign", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } },
diff --git a/servers/visual/shader_types.cpp b/servers/visual/shader_types.cpp
index 9042649337..2ab52d13b8 100644
--- a/servers/visual/shader_types.cpp
+++ b/servers/visual/shader_types.cpp
@@ -218,9 +218,8 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["LIGHT_HEIGHT"] = ShaderLanguage::TYPE_FLOAT;
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["LIGHT_COLOR"] = ShaderLanguage::TYPE_VEC4;
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["LIGHT_UV"] = ShaderLanguage::TYPE_VEC2;
- shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["LIGHT_SHADOW"] = ShaderLanguage::TYPE_VEC4;
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["LIGHT"] = ShaderLanguage::TYPE_VEC4;
- shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["SHADOW"] = ShaderLanguage::TYPE_VEC4;
+ shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["SHADOW_COLOR"] = ShaderLanguage::TYPE_VEC4;
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["POINT_COORD"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].can_discard = true;
@@ -254,7 +253,6 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_PARTICLES].functions["vertex"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT);
shader_modes[VS::SHADER_PARTICLES].functions["vertex"].can_discard = false;
- shader_modes[VS::SHADER_PARTICLES].modes.insert("billboard");
shader_modes[VS::SHADER_PARTICLES].modes.insert("disable_force");
shader_modes[VS::SHADER_PARTICLES].modes.insert("disable_velocity");
shader_modes[VS::SHADER_PARTICLES].modes.insert("keep_data");
diff --git a/thirdparty/README.md b/thirdparty/README.md
index d3fa0e4664..2f765a4000 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -89,7 +89,7 @@ Use UI font variant if available, because it has tight vertical metrics and good
### Hack Regular
- Upstream: https://github.com/source-foundry/Hack
-- Version: 3.000
+- Version: 3.003
- License: MIT + Bitstream Vera License
### DroidSans*.ttf
@@ -234,31 +234,35 @@ changes are marked with `// -- GODOT --` comments.
## libwebsockets
- Upstream: https://github.com/warmcat/libwebsockets
-- Version: 2.4.1
+- Version: 2.4.2
- License: LGPLv2.1 + static linking exception
File extracted from upstream source:
- Everything in `lib/` except `minilex.c`, `http2/`, `event-libs/`.
- From `misc/` exclude `lws-genhash.c`, `lws-ring.c`, `romfs.{c,h}`, `smtp.c`.
- From `plat/` exclude `lws-plat-{esp*,optee}.c`.
- - From `server/` exclude `access-log.c`, `cgi.c`, `daemonize.c`, `lws-spa.c`,
+ - From `server/` exclude `access-log.c`, `cgi.c`, `daemonize.c`, `lws-spa.c`,
`peer-limits.c`, `rewrite.c`
- Also copy `win32helpers/` from `win32port/`
- `mbedtls_wrapper/include/platform/ssl_port.h` has a small change to check for OSX and FreeBSD (missing `malloc.h`).
The bug is fixed in upstream master via `LWS_HAVE_MALLOC_H`, but not in the 2.4.1 branch (as the file structure has changed).
+- You might need to apply the patch in `thirdparty/lws/mbedtls_verify.diff` (port of PR 1215) to future `2.4.x` releases if it does not get cherry picked.
-Important: `lws_config.h` and `lws_config_private.h` contains custom
+Important: `lws_config.h` and `lws_config_private.h` contains custom
Godot build configurations, check them out when updating.
## mbedTLS
- Upstream: https://tls.mbed.org/
-- Version: 2.7.0
+- Version: 2.8.0
- License: Apache 2.0
-File extracted from upstream release tarball `mbedtls-2.7.0-apache.tgz`:
-- All `*.h` from `include/mbedtls/` to `thirdparty/include/mbedtls/`
-- All `*.c` from `library/` to `thirdparty/library/`
+File extracted from upstream release tarball `mbedtls-2.8.0-apache.tgz`:
+- All `*.h` from `include/mbedtls/` to `thirdparty/mbedtls/include/mbedtls/`
+- All `*.c` from `library/` to `thirdparty/mbedtls/library/`
+- In file `thirdparty/mbedtls/library/net_sockets.c` mbedTLS overrides the `_WIN32_WINNT` define.
+ Be sure to check the Godot addition to only redfine it when undefined or `< 0x0501` (PRed upstream).
+- Applied the patch in `thirdparty/mbedtls/1453.diff` (PR 1453). Soon to be merged upstream. Check it out at next update.
## minizip
diff --git a/thirdparty/fonts/Hack_Regular.ttf b/thirdparty/fonts/Hack_Regular.ttf
index f342700811..92a90cb06e 100644
--- a/thirdparty/fonts/Hack_Regular.ttf
+++ b/thirdparty/fonts/Hack_Regular.ttf
Binary files differ
diff --git a/thirdparty/fonts/LICENSE_Hack.md b/thirdparty/fonts/LICENSE_Hack.md
index ddd23a2b81..08927e504f 100644
--- a/thirdparty/fonts/LICENSE_Hack.md
+++ b/thirdparty/fonts/LICENSE_Hack.md
@@ -1,4 +1,4 @@
-The work in the Hack project is Copyright 2017 Source Foundry Authors and licensed under the MIT License
+The work in the Hack project is Copyright 2018 Source Foundry Authors and licensed under the MIT License
The work in the DejaVu project was committed to the public domain.
@@ -6,7 +6,7 @@ Bitstream Vera Sans Mono Copyright 2003 Bitstream Inc. and licensed under the Bi
### MIT License
-Copyright (c) 2017 Source Foundry Authors
+Copyright (c) 2018 Source Foundry Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/thirdparty/lws/client/client.c b/thirdparty/lws/client/client.c
index 20450aa923..ded4e4bf0b 100644
--- a/thirdparty/lws/client/client.c
+++ b/thirdparty/lws/client/client.c
@@ -258,9 +258,10 @@ start_ws_handshake:
#ifdef LWS_OPENSSL_SUPPORT
/* we can retry this... just cook the SSL BIO the first time */
- if (wsi->use_ssl && !wsi->ssl) {
- if (lws_ssl_client_bio_create(wsi))
- return -1;
+ if (wsi->use_ssl && !wsi->ssl &&
+ lws_ssl_client_bio_create(wsi) < 0) {
+ cce = "bio_create failed";
+ goto bail3;
}
if (wsi->use_ssl) {
@@ -727,9 +728,10 @@ lws_client_interpret_server_handshake(struct lws *wsi)
return 0;
}
- if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) {
- lwsl_info("no ACCEPT\n");
- cce = "HS: ACCEPT missing";
+ if (p && !strncmp(p, "401", 3)) {
+ lwsl_warn(
+ "lws_client_handshake: got bad HTTP response '%s'\n", p);
+ cce = "HS: ws upgrade unauthorized";
goto bail3;
}
@@ -740,6 +742,12 @@ lws_client_interpret_server_handshake(struct lws *wsi)
goto bail3;
}
+ if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) {
+ lwsl_info("no ACCEPT\n");
+ cce = "HS: ACCEPT missing";
+ goto bail3;
+ }
+
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE);
if (!p) {
lwsl_info("no UPGRADE\n");
diff --git a/thirdparty/lws/client/ssl-client.c b/thirdparty/lws/client/ssl-client.c
index b69fd2da30..962c6e3cb5 100644
--- a/thirdparty/lws/client/ssl-client.c
+++ b/thirdparty/lws/client/ssl-client.c
@@ -176,11 +176,7 @@ lws_ssl_client_bio_create(struct lws *wsi)
#endif
#else
#if defined(LWS_WITH_MBEDTLS)
- if (wsi->vhost->x509_client_CA)
- SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
- else
- SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, OpenSSL_client_verify_callback);
-
+ SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
#else
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
SSL_set_tlsext_host_name(wsi->ssl, hostname);
@@ -284,9 +280,13 @@ some_wait:
char *p = (char *)&pt->serv_buf[0];
char *sb = p;
- lwsl_err("ssl hs1 error, X509_V_ERR = %d: %s\n",
- n, ERR_error_string(n, sb));
+ lwsl_err("ssl hs1 error, X509_V_ERR = %d: errno %d: %s\n",
+ n, errno, ERR_error_string(n, sb));
lws_ssl_elaborate_error();
+#if defined(LWS_WITH_MBEDTLS)
+ if (n == SSL_ERROR_SYSCALL)
+ return -1;
+#endif
}
n = -1;
diff --git a/thirdparty/lws/context.c b/thirdparty/lws/context.c
index f67476b1e3..9f221f50f1 100644
--- a/thirdparty/lws/context.c
+++ b/thirdparty/lws/context.c
@@ -1621,7 +1621,7 @@ lws_context_destroy2(struct lws_context *context)
lws_check_deferred_free(context, 1);
#if LWS_MAX_SMP > 1
- pthread_mutex_destroy(&context->lock, NULL);
+ pthread_mutex_destroy(&context->lock);
#endif
lws_free(context);
diff --git a/thirdparty/lws/libwebsockets.c b/thirdparty/lws/libwebsockets.c
index 50f975d21e..8fe0854041 100644
--- a/thirdparty/lws/libwebsockets.c
+++ b/thirdparty/lws/libwebsockets.c
@@ -482,8 +482,9 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE)
goto just_kill_connection;
- if (wsi->mode == LWSCM_HTTP_SERVING ||
- wsi->mode == LWSCM_HTTP2_SERVING) {
+ if (!wsi->told_user_closed &&
+ (wsi->mode == LWSCM_HTTP_SERVING ||
+ wsi->mode == LWSCM_HTTP2_SERVING)) {
if (wsi->user_space)
wsi->vhost->protocols->callback(wsi,
LWS_CALLBACK_HTTP_DROP_PROTOCOL,
@@ -583,7 +584,7 @@ just_kill_connection:
lws_remove_child_from_any_parent(wsi);
n = 0;
- if (wsi->user_space) {
+ if (!wsi->told_user_closed && wsi->user_space) {
lwsl_debug("%s: %p: DROP_PROTOCOL %s\n", __func__, wsi,
wsi->protocol->name);
wsi->protocol->callback(wsi,
@@ -656,8 +657,10 @@ just_kill_connection:
__func__, wsi, (int)(long)wsi->desc.sockfd,
wsi->state);
if (!wsi->socket_is_permanently_unusable &&
- lws_sockfd_valid(wsi->desc.sockfd))
+ lws_sockfd_valid(wsi->desc.sockfd)) {
+ wsi->socket_is_permanently_unusable = 1;
n = shutdown(wsi->desc.sockfd, SHUT_WR);
+ }
}
if (n)
lwsl_debug("closing: shutdown (state %d) ret %d\n",
diff --git a/thirdparty/lws/libwebsockets.h b/thirdparty/lws/libwebsockets.h
index ef996c5d78..460c732602 100644
--- a/thirdparty/lws/libwebsockets.h
+++ b/thirdparty/lws/libwebsockets.h
@@ -1073,7 +1073,7 @@ enum lws_callback_reasons {
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS = 22,
/**< if configured for
* including OpenSSL support, this callback allows your user code
- * to load extra certifcates into the server which allow it to
+ * to load extra certificates into the server which allow it to
* verify the validity of certificates returned by clients. user
* is the server's OpenSSL SSL_CTX* */
LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION = 23,
@@ -4013,9 +4013,6 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
#if !defined(LWS_SIZEOFPTR)
#define LWS_SIZEOFPTR (sizeof (void *))
#endif
-#if !defined(u_int64_t)
-#define u_int64_t unsigned long long
-#endif
#if defined(__x86_64__)
#define _LWS_PAD_SIZE 16 /* Intel recommended for best performance */
@@ -4808,7 +4805,7 @@ LWS_VISIBLE LWS_EXTERN unsigned long
lws_now_secs(void);
/**
- * lws_get_context - Allow geting lws_context from a Websocket connection
+ * lws_get_context - Allow getting lws_context from a Websocket connection
* instance
*
* With this function, users can access context in the callback function.
diff --git a/thirdparty/lws/lws_config.h b/thirdparty/lws/lws_config.h
index 3a918747b1..6005d94ec6 100644
--- a/thirdparty/lws/lws_config.h
+++ b/thirdparty/lws/lws_config.h
@@ -1,5 +1,10 @@
/* lws_config.h Generated from lws_config.h.in */
-#include "lws_config_private.h"
+
+/* GODOT ADDITION */
+#ifndef DEBUG_ENABLED
+#define LWS_WITH_NO_LOGS
+#endif
+/* END GODOT ADDITION */
#ifndef NDEBUG
#ifndef _DEBUG
@@ -25,54 +30,45 @@
/* #undef LWS_WITH_PLUGINS */
/* #undef LWS_WITH_NO_LOGS */
-#ifndef DEBUG_ENABLED
-#define LWS_WITH_NO_LOGS
-#endif
/* The Libwebsocket version */
-#define LWS_LIBRARY_VERSION "2.4.1"
+#define LWS_LIBRARY_VERSION "2.4.2"
#define LWS_LIBRARY_VERSION_MAJOR 2
#define LWS_LIBRARY_VERSION_MINOR 4
-#define LWS_LIBRARY_VERSION_PATCH 1
+#define LWS_LIBRARY_VERSION_PATCH 2
/* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */
#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR*1000000)+(LWS_LIBRARY_VERSION_MINOR*1000)+LWS_LIBRARY_VERSION_PATCH
/* The current git commit hash that we're building from */
-#define LWS_BUILD_HASH "55f97b7806e07db2d4c8a158172cd309d0faf450"
+#define LWS_BUILD_HASH "8964ce9db75a98e463dfafd2e89f2bc8a95ec6ed"
/* Build with OpenSSL support */
#define LWS_OPENSSL_SUPPORT
/* The client should load and trust CA root certs it finds in the OS */
-#define LWS_SSL_CLIENT_USE_OS_CA_CERTS
+/* #undef LWS_SSL_CLIENT_USE_OS_CA_CERTS */
/* Sets the path where the client certs should be installed. */
-#define LWS_OPENSSL_CLIENT_CERTS "../share"
+/* #undef LWS_OPENSSL_CLIENT_CERTS "../share" */
/* Turn off websocket extensions */
/* #undef LWS_NO_EXTENSIONS */
/* Enable libev io loop */
/* #undef LWS_WITH_LIBEV */
-#undef LWS_WITH_LIBEV
/* Enable libuv io loop */
/* #undef LWS_WITH_LIBUV */
-#undef LWS_WITH_LIBUV
/* Enable libevent io loop */
/* #undef LWS_WITH_LIBEVENT */
-#undef LWS_WITH_LIBEVENT
/* Build with support for ipv6 */
/* #undef LWS_WITH_IPV6 */
/* Build with support for UNIX domain socket */
/* #undef LWS_WITH_UNIX_SOCK */
-#ifdef WINDOWS_ENABLED
-#undef LWS_USE_UNIX_SOCK
-#endif
/* Build with support for HTTP2 */
/* #undef LWS_WITH_HTTP2 */
@@ -100,7 +96,7 @@
/* SSL server using ECDH certificate */
/* #undef LWS_SSL_SERVER_WITH_ECDH_CERT */
-#define LWS_HAVE_SSL_CTX_set1_param
+/* #undef LWS_HAVE_SSL_CTX_set1_param */
#define LWS_HAVE_X509_VERIFY_PARAM_set1_host
/* #undef LWS_HAVE_RSA_SET0_KEY */
@@ -110,7 +106,7 @@
/* #undef LWS_WITH_CGI */
/* whether the Openssl is recent enough, and / or built with, ecdh */
-#define LWS_HAVE_OPENSSL_ECDH_H
+/* #undef LWS_HAVE_OPENSSL_ECDH_H */
/* HTTP Proxy support */
/* #undef LWS_WITH_HTTP_PROXY */
@@ -157,9 +153,9 @@
/* OpenSSL various APIs */
-/* #undef LWS_HAVE_TLS_CLIENT_METHOD */
-#define LWS_HAVE_TLSV1_2_CLIENT_METHOD
-#define LWS_HAVE_SSL_SET_INFO_CALLBACK
+#define LWS_HAVE_TLS_CLIENT_METHOD
+/* #undef LWS_HAVE_TLSV1_2_CLIENT_METHOD */
+/* #undef LWS_HAVE_SSL_SET_INFO_CALLBACK */
#define LWS_HAS_INTPTR_T
diff --git a/thirdparty/lws/mbedtls_verify.diff b/thirdparty/lws/mbedtls_verify.diff
new file mode 100644
index 0000000000..d320645d67
--- /dev/null
+++ b/thirdparty/lws/mbedtls_verify.diff
@@ -0,0 +1,74 @@
+diff --git a/thirdparty/lws/client/ssl-client.c b/thirdparty/lws/client/ssl-client.c
+index 6626e0844..962c6e3cb 100644
+--- a/thirdparty/lws/client/ssl-client.c
++++ b/thirdparty/lws/client/ssl-client.c
+@@ -176,11 +176,7 @@ lws_ssl_client_bio_create(struct lws *wsi)
+ #endif
+ #else
+ #if defined(LWS_WITH_MBEDTLS)
+- if (wsi->vhost->x509_client_CA)
+- SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
+- else
+- SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, OpenSSL_client_verify_callback);
+-
++ SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
+ #else
+ #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ SSL_set_tlsext_host_name(wsi->ssl, hostname);
+diff --git a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c b/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
+index 63504919c..4e3d61109 100644
+--- a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
++++ b/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
+@@ -218,7 +218,7 @@ static int ssl_pm_reload_crt(SSL *ssl)
+ struct x509_pm *crt_pm = (struct x509_pm *)ssl->cert->x509->x509_pm;
+
+ if (ssl->verify_mode == SSL_VERIFY_PEER)
+- mode = MBEDTLS_SSL_VERIFY_REQUIRED;
++ mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
+ else if (ssl->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+ mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
+ else if (ssl->verify_mode == SSL_VERIFY_CLIENT_ONCE)
+@@ -712,11 +712,39 @@ long ssl_pm_get_verify_result(const SSL *ssl)
+ struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
+
+ ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl);
+- if (ret) {
+- SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_get_verify_result() return 0x%x", ret);
++
++ if (!ret)
++ return X509_V_OK;
++
++ if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED ||
++ (ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED))
++ // Allows us to use LCCSCF_ALLOW_SELFSIGNED to skip verification
++ verify_result = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
++
++ else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
++ verify_result = X509_V_ERR_HOSTNAME_MISMATCH;
++
++ else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) ||
++ (ret & MBEDTLS_X509_BADCRL_BAD_KEY))
++ verify_result = X509_V_ERR_CA_KEY_TOO_SMALL;
++
++ else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) ||
++ (ret & MBEDTLS_X509_BADCRL_BAD_MD))
++ verify_result = X509_V_ERR_CA_MD_TOO_WEAK;
++
++ else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) ||
++ (ret & MBEDTLS_X509_BADCRL_FUTURE))
++ verify_result = X509_V_ERR_CERT_NOT_YET_VALID;
++
++ else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) ||
++ (ret & MBEDTLS_X509_BADCRL_EXPIRED))
++ verify_result = X509_V_ERR_CERT_HAS_EXPIRED;
++
++ else
+ verify_result = X509_V_ERR_UNSPECIFIED;
+- } else
+- verify_result = X509_V_OK;
++
++ SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL,
++ "mbedtls_ssl_get_verify_result() return 0x%x", ret);
+
+ return verify_result;
+ }
diff --git a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h b/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h
index 45198bc978..2ca438c422 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h
+++ b/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h
@@ -215,6 +215,7 @@ struct ssl_st
int (*verify_callback) (int ok, X509_STORE_CTX *ctx);
int rwstate;
+ int interrupted_remaining_write;
long verify_result;
diff --git a/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h b/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h
index 2ffd7e7544..eca68f20d1 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h
+++ b/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h
@@ -25,11 +25,13 @@
*/
#include "string.h"
-#if defined(__APPLE__) || defined(__FreeBSD__)
+/* GODOT ADDITION */
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#include <stdlib.h>
#else
#include "malloc.h"
#endif
+/* END GODOT ADDITION */
void *ssl_mem_zalloc(size_t size);
diff --git a/thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c b/thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c
index 187fc9f005..d8fdd06fad 100644
--- a/thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c
+++ b/thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c
@@ -142,9 +142,9 @@ int SSL_get_error(const SSL *ssl, int ret_code)
ret = SSL_ERROR_NONE;
else if (ret_code < 0)
{
- if (SSL_want_read(ssl))
+ if (ssl->err == SSL_ERROR_WANT_READ || SSL_want_read(ssl))
ret = SSL_ERROR_WANT_READ;
- else if (SSL_want_write(ssl))
+ else if (ssl->err == SSL_ERROR_WANT_WRITE || SSL_want_write(ssl))
ret = SSL_ERROR_WANT_WRITE;
else
ret = SSL_ERROR_SYSCALL; //unknown
@@ -457,7 +457,7 @@ int SSL_read(SSL *ssl, void *buffer, int len)
int SSL_write(SSL *ssl, const void *buffer, int len)
{
int ret;
- int send_bytes;
+ int send_bytes, bytes;
const unsigned char *pbuf;
SSL_ASSERT1(ssl);
@@ -470,25 +470,36 @@ int SSL_write(SSL *ssl, const void *buffer, int len)
pbuf = (const unsigned char *)buffer;
do {
- int bytes;
-
if (send_bytes > SSL_SEND_DATA_MAX_LENGTH)
bytes = SSL_SEND_DATA_MAX_LENGTH;
else
bytes = send_bytes;
+ if (ssl->interrupted_remaining_write) {
+ bytes = ssl->interrupted_remaining_write;
+ ssl->interrupted_remaining_write = 0;
+ }
+
ret = SSL_METHOD_CALL(send, ssl, pbuf, bytes);
+ //printf("%s: ssl_pm said %d for %d requested (cum %d)\n", __func__, ret, bytes, len -send_bytes);
+ /* the return is a NEGATIVE OpenSSL error code, or the length sent */
if (ret > 0) {
pbuf += ret;
send_bytes -= ret;
- }
- } while (ret > 0 && send_bytes);
+ } else
+ ssl->interrupted_remaining_write = bytes;
+ } while (ret > 0 && send_bytes && ret == bytes);
if (ret >= 0) {
ret = len - send_bytes;
- ssl->rwstate = SSL_NOTHING;
- } else
- ret = -1;
+ if (!ret)
+ ssl->rwstate = SSL_NOTHING;
+ } else {
+ if (send_bytes == len)
+ ret = -1;
+ else
+ ret = len - send_bytes;
+ }
return ret;
}
diff --git a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c b/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
index 536733fbab..4e3d611095 100644
--- a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
+++ b/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
@@ -218,7 +218,7 @@ static int ssl_pm_reload_crt(SSL *ssl)
struct x509_pm *crt_pm = (struct x509_pm *)ssl->cert->x509->x509_pm;
if (ssl->verify_mode == SSL_VERIFY_PEER)
- mode = MBEDTLS_SSL_VERIFY_REQUIRED;
+ mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
else if (ssl->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
else if (ssl->verify_mode == SSL_VERIFY_CLIENT_ONCE)
@@ -360,17 +360,52 @@ int ssl_pm_read(SSL *ssl, void *buffer, int len)
return ret;
}
+/*
+ * This returns -1, or the length sent.
+ * If -1, then you need to find out if the error was
+ * fatal or recoverable using SSL_get_error()
+ */
int ssl_pm_send(SSL *ssl, const void *buffer, int len)
{
int ret;
struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
ret = mbedtls_ssl_write(&ssl_pm->ssl, buffer, len);
+ /*
+ * We can get a positive number, which may be less than len... that
+ * much was sent successfully and you can call again to send more.
+ *
+ * We can get a negative mbedtls error code... if WANT_WRITE or WANT_READ,
+ * it's nonfatal and means it should be retried as-is. If something else,
+ * it's fatal actually.
+ *
+ * If this function returns something other than a positive value or
+ * MBEDTLS_ERR_SSL_WANT_READ/WRITE, the ssl context becomes unusable, and
+ * you should either free it or call mbedtls_ssl_session_reset() on it
+ * before re-using it for a new connection; the current connection must
+ * be closed.
+ *
+ * When this function returns MBEDTLS_ERR_SSL_WANT_WRITE/READ, it must be
+ * called later with the same arguments, until it returns a positive value.
+ */
+
if (ret < 0) {
- if (ret == MBEDTLS_ERR_NET_CONN_RESET)
+ SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_write() return -0x%x", -ret);
+ switch (ret) {
+ case MBEDTLS_ERR_NET_CONN_RESET:
ssl->err = SSL_ERROR_SYSCALL;
- SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_write() return -0x%x", -ret);
- ret = -1;
+ break;
+ case MBEDTLS_ERR_SSL_WANT_WRITE:
+ ssl->err = SSL_ERROR_WANT_WRITE;
+ break;
+ case MBEDTLS_ERR_SSL_WANT_READ:
+ ssl->err = SSL_ERROR_WANT_READ;
+ break;
+ default:
+ break;
+ }
+
+ ret = -1;
}
return ret;
@@ -677,11 +712,39 @@ long ssl_pm_get_verify_result(const SSL *ssl)
struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl);
- if (ret) {
- SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_get_verify_result() return 0x%x", ret);
+
+ if (!ret)
+ return X509_V_OK;
+
+ if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED ||
+ (ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED))
+ // Allows us to use LCCSCF_ALLOW_SELFSIGNED to skip verification
+ verify_result = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+
+ else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
+ verify_result = X509_V_ERR_HOSTNAME_MISMATCH;
+
+ else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) ||
+ (ret & MBEDTLS_X509_BADCRL_BAD_KEY))
+ verify_result = X509_V_ERR_CA_KEY_TOO_SMALL;
+
+ else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) ||
+ (ret & MBEDTLS_X509_BADCRL_BAD_MD))
+ verify_result = X509_V_ERR_CA_MD_TOO_WEAK;
+
+ else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) ||
+ (ret & MBEDTLS_X509_BADCRL_FUTURE))
+ verify_result = X509_V_ERR_CERT_NOT_YET_VALID;
+
+ else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) ||
+ (ret & MBEDTLS_X509_BADCRL_EXPIRED))
+ verify_result = X509_V_ERR_CERT_HAS_EXPIRED;
+
+ else
verify_result = X509_V_ERR_UNSPECIFIED;
- } else
- verify_result = X509_V_OK;
+
+ SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL,
+ "mbedtls_ssl_get_verify_result() return 0x%x", ret);
return verify_result;
}
diff --git a/thirdparty/lws/misc/lejp.c b/thirdparty/lws/misc/lejp.c
index 5407c90f97..38efa8b122 100644
--- a/thirdparty/lws/misc/lejp.c
+++ b/thirdparty/lws/misc/lejp.c
@@ -444,7 +444,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
goto append_npos;
}
if (c == '.') {
- if (ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) {
+ if (!ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) {
ret = LEJP_REJECT_MP_VAL_NUM_FORMAT;
goto reject;
}
diff --git a/thirdparty/lws/misc/sha-1.c b/thirdparty/lws/misc/sha-1.c
index 9353fbefe4..50205a0100 100644
--- a/thirdparty/lws/misc/sha-1.c
+++ b/thirdparty/lws/misc/sha-1.c
@@ -45,7 +45,7 @@ struct sha1_ctxt {
} h;
union {
unsigned char b8[8];
- u_int64_t b64[1];
+ uint64_t b64[1];
} c;
union {
unsigned char b8[64];
diff --git a/thirdparty/lws/output.c b/thirdparty/lws/output.c
index ed4752490e..375ff3ef99 100644
--- a/thirdparty/lws/output.c
+++ b/thirdparty/lws/output.c
@@ -270,9 +270,12 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
if (wsi->state != LWSS_ESTABLISHED &&
((wsi->state != LWSS_RETURNED_CLOSE_ALREADY &&
+ wsi->state != LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION &&
wsi->state != LWSS_AWAITING_CLOSE_ACK) ||
- wp != LWS_WRITE_CLOSE))
+ wp != LWS_WRITE_CLOSE)) {
+ lwsl_debug("binning\n");
return 0;
+ }
/* if we are continuing a frame that already had its header done */
@@ -507,7 +510,7 @@ send_raw:
(wp & 0x1f) == LWS_WRITE_HTTP_FINAL) &&
wsi->u.http.tx_content_length) {
wsi->u.http.tx_content_remain -= len;
- lwsl_info("%s: content_remain = %llu\n", __func__,
+ lwsl_info("%s: wsi %p: tx_content_remain = %llu\n", __func__, wsi,
(unsigned long long)wsi->u.http.tx_content_remain);
if (!wsi->u.http.tx_content_remain) {
lwsl_info("%s: selecting final write mode\n", __func__);
@@ -639,6 +642,9 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
poss = context->pt_serv_buf_size - n - LWS_H2_FRAME_HEADER_LENGTH;
+ if (poss > wsi->u.http.tx_content_remain)
+ poss = wsi->u.http.tx_content_remain;
+
/*
* if there is a hint about how much we will do well to send at one time,
* restrict ourselves to only trying to send that.
diff --git a/thirdparty/lws/pollfd.c b/thirdparty/lws/pollfd.c
index 4d6704d41c..54a4a86057 100644
--- a/thirdparty/lws/pollfd.c
+++ b/thirdparty/lws/pollfd.c
@@ -537,9 +537,14 @@ LWS_VISIBLE int
lws_callback_on_writable_all_protocol(const struct lws_context *context,
const struct lws_protocols *protocol)
{
- struct lws_vhost *vhost = context->vhost_list;
+ struct lws_vhost *vhost;
int n;
+ if (!context)
+ return 0;
+
+ vhost = context->vhost_list;
+
while (vhost) {
for (n = 0; n < vhost->count_protocols; n++)
if (protocol->callback ==
diff --git a/thirdparty/lws/private-libwebsockets.h b/thirdparty/lws/private-libwebsockets.h
index 4f0b374332..535fa0be57 100644
--- a/thirdparty/lws/private-libwebsockets.h
+++ b/thirdparty/lws/private-libwebsockets.h
@@ -356,9 +356,6 @@ esp8266_tcp_stream_bind(lws_sockfd_type fd, int port, struct lws *wsi);
#ifndef BYTE_ORDER
#define BYTE_ORDER LITTLE_ENDIAN
#endif
-#ifndef u_int64_t
-typedef unsigned __int64 u_int64_t;
-#endif
#undef __P
#ifndef __P
@@ -1633,7 +1630,6 @@ struct lws_h2_netconn {
unsigned int pad_length:1;
unsigned int collected_priority:1;
unsigned int is_first_header_char:1;
- unsigned int seen_nonpseudoheader:1;
unsigned int zero_huff_padding:1;
unsigned int last_action_dyntable_resize:1;
@@ -1922,6 +1918,7 @@ struct lws {
unsigned int hdr_parsing_completed:1;
unsigned int http2_substream:1;
unsigned int upgraded_to_http2:1;
+ unsigned int seen_nonpseudoheader:1;
unsigned int listener:1;
unsigned int user_space_externally_allocated:1;
unsigned int socket_is_permanently_unusable:1;
diff --git a/thirdparty/lws/server/ssl-server.c b/thirdparty/lws/server/ssl-server.c
index a9516f2239..c4362824bf 100644
--- a/thirdparty/lws/server/ssl-server.c
+++ b/thirdparty/lws/server/ssl-server.c
@@ -155,7 +155,7 @@ lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
*/
vh = context->vhost_list;
while (vh) {
- if (!vh->being_destroyed && vh->ssl_ctx == SSL_get_SSL_CTX(ssl))
+ if (!vh->being_destroyed && ssl && vh->ssl_ctx == SSL_get_SSL_CTX(ssl))
break;
vh = vh->vhost_next;
}
diff --git a/thirdparty/lws/service.c b/thirdparty/lws/service.c
index 6748e30bd4..8cf455e2c9 100644
--- a/thirdparty/lws/service.c
+++ b/thirdparty/lws/service.c
@@ -1073,6 +1073,8 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
c = lws_token_to_string(m);
if (!c)
break;
+ if (!(*c))
+ break;
len = lws_hdr_total_length(wsi, m);
if (!len || len > sizeof(buf) - 1) {
@@ -1090,6 +1092,11 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
m++;
} while (1);
+ /* explicitly detach the ah */
+
+ lws_header_table_force_to_detachable_state(wsi);
+ lws_header_table_detach(wsi, 0);
+
/* ... and then drop the connection */
if (wsi->desc.sockfd == our_fd)
@@ -1098,7 +1105,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
- ah = ah->next;
+ ah = pt->ah_list;
}
#ifdef LWS_WITH_CGI
@@ -1644,6 +1651,14 @@ drain:
break;
}
#endif
+ /*
+ * something went wrong with parsing the handshake, and
+ * we ended up back in the event loop without completing it
+ */
+ case LWSCM_PRE_WS_SERVING_ACCEPT:
+ wsi->socket_is_permanently_unusable = 1;
+ goto close_and_handled;
+
default:
#ifdef LWS_NO_CLIENT
break;
diff --git a/thirdparty/lws/ssl.c b/thirdparty/lws/ssl.c
index 0a647b469c..4ff3088ab3 100644
--- a/thirdparty/lws/ssl.c
+++ b/thirdparty/lws/ssl.c
@@ -20,11 +20,7 @@
*/
#include "private-libwebsockets.h"
-
-/* workaround for mingw */
-#if !defined(ECONNABORTED)
-#define ECONNABORTED 103
-#endif
+#include <errno.h>
int lws_alloc_vfs_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
@@ -463,7 +459,7 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
lwsl_debug("%p: SSL_read says %d\n", wsi, n);
/* manpage: returning 0 means connection shut down */
- if (!n) {
+ if (!n || (n == -1 && errno == ENOTCONN)) {
wsi->socket_is_permanently_unusable = 1;
return LWS_SSL_CAPABLE_ERROR;
@@ -476,12 +472,12 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
m == SSL_ERROR_SYSCALL)
return LWS_SSL_CAPABLE_ERROR;
- if (SSL_want_read(wsi->ssl)) {
+ if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
lwsl_debug("%s: WANT_READ\n", __func__);
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
- if (SSL_want_write(wsi->ssl)) {
+ if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
lwsl_debug("%s: WANT_WRITE\n", __func__);
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
return LWS_SSL_CAPABLE_MORE_SERVICE;
@@ -885,6 +881,7 @@ go_again:
failed:
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1);
+ wsi->socket_is_permanently_unusable = 1;
lwsl_info("SSL_accept failed socket %u: %s\n", wsi->desc.sockfd,
lws_ssl_get_error_string(m, n, buf, sizeof(buf)));
lws_ssl_elaborate_error();
@@ -903,7 +900,7 @@ accepted:
/* adapt our vhost to match the SNI SSL_CTX that was chosen */
vh = context->vhost_list;
while (vh) {
- if (!vh->being_destroyed &&
+ if (!vh->being_destroyed && wsi->ssl &&
vh->ssl_ctx == SSL_get_SSL_CTX(wsi->ssl)) {
lwsl_info("setting wsi to vh %s\n", vh->name);
wsi->vhost = vh;
diff --git a/thirdparty/mbedtls/1453.diff b/thirdparty/mbedtls/1453.diff
new file mode 100644
index 0000000000..acc3654cd4
--- /dev/null
+++ b/thirdparty/mbedtls/1453.diff
@@ -0,0 +1,120 @@
+diff --git a/thirdparty/mbedtls/library/entropy_poll.c b/thirdparty/mbedtls/library/entropy_poll.c
+index 67900c46c8..cefe882d2a 100644
+--- a/thirdparty/mbedtls/library/entropy_poll.c
++++ b/thirdparty/mbedtls/library/entropy_poll.c
+@@ -54,28 +54,43 @@
+ #define _WIN32_WINNT 0x0400
+ #endif
+ #include <windows.h>
+-#include <wincrypt.h>
++#include <bcrypt.h>
++#if defined(_MSC_VER) && _MSC_VER <= 1600
++/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and
++ * <intsafe.h> are included, as they redefine a number of <TYPE>_MAX constants.
++ * These constants are guaranteed to be the same, though, so we suppress the
++ * warning when including intsafe.h.
++ */
++#pragma warning( push )
++#pragma warning( disable : 4005 )
++#endif
++#include <intsafe.h>
++#if defined(_MSC_VER) && _MSC_VER <= 1600
++#pragma warning( pop )
++#endif
+
+ int mbedtls_platform_entropy_poll( void *data, unsigned char *output, size_t len,
+ size_t *olen )
+ {
+- HCRYPTPROV provider;
++ ULONG len_as_ulong = 0;
+ ((void) data);
+ *olen = 0;
+
+- if( CryptAcquireContext( &provider, NULL, NULL,
+- PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) == FALSE )
++ /*
++ * BCryptGenRandom takes ULONG for size, which is smaller than size_t on
++ * 64-bit Windows platforms. Ensure len's value can be safely converted into
++ * a ULONG.
++ */
++ if ( FAILED( SizeTToULong( len, &len_as_ulong ) ) )
+ {
+ return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED );
+ }
+
+- if( CryptGenRandom( provider, (DWORD) len, output ) == FALSE )
++ if ( !BCRYPT_SUCCESS( BCryptGenRandom( NULL, output, len_as_ulong, BCRYPT_USE_SYSTEM_PREFERRED_RNG ) ) )
+ {
+- CryptReleaseContext( provider, 0 );
+ return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED );
+ }
+
+- CryptReleaseContext( provider, 0 );
+ *olen = len;
+
+ return( 0 );
+diff --git a/thirdparty/mbedtls/library/x509_crt.c b/thirdparty/mbedtls/library/x509_crt.c
+index afff4e18bf..7960fa1a1a 100644
+--- a/thirdparty/mbedtls/library/x509_crt.c
++++ b/thirdparty/mbedtls/library/x509_crt.c
+@@ -64,6 +64,19 @@
+
+ #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
+ #include <windows.h>
++#if defined(_MSC_VER) && _MSC_VER <= 1600
++/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and
++ * <intsafe.h> are included, as they redefine a number of <TYPE>_MAX constants.
++ * These constants are guaranteed to be the same, though, so we suppress the
++ * warning when including intsafe.h.
++ */
++#pragma warning( push )
++#pragma warning( disable : 4005 )
++#endif
++#include <intsafe.h>
++#if defined(_MSC_VER) && _MSC_VER <= 1600
++#pragma warning( pop )
++#endif
+ #else
+ #include <time.h>
+ #endif
+@@ -1130,6 +1143,7 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
+ char filename[MAX_PATH];
+ char *p;
+ size_t len = strlen( path );
++ int lengthAsInt = 0;
+
+ WIN32_FIND_DATAW file_data;
+ HANDLE hFind;
+@@ -1144,7 +1158,18 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
+ p = filename + len;
+ filename[len++] = '*';
+
+- w_ret = MultiByteToWideChar( CP_ACP, 0, filename, (int)len, szDir,
++ if ( FAILED ( SizeTToInt( len, &lengthAsInt ) ) )
++ return( MBEDTLS_ERR_X509_FILE_IO_ERROR );
++
++ /*
++ * Note this function uses the code page CP_ACP, and assumes the incoming
++ * string is encoded in ANSI, before translating it into Unicode. If the
++ * incoming string were changed to be UTF-8, then the length check needs to
++ * change to check the number of characters, not the number of bytes, in the
++ * incoming string are less than MAX_PATH to avoid a buffer overrun with
++ * MultiByteToWideChar().
++ */
++ w_ret = MultiByteToWideChar( CP_ACP, 0, filename, lengthAsInt, szDir,
+ MAX_PATH - 3 );
+ if( w_ret == 0 )
+ return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
+@@ -1161,8 +1186,11 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
+ if( file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
+ continue;
+
++ if ( FAILED( SizeTToInt( wcslen( file_data.cFileName ), &lengthAsInt ) ) )
++ return( MBEDTLS_ERR_X509_FILE_IO_ERROR );
++
+ w_ret = WideCharToMultiByte( CP_ACP, 0, file_data.cFileName,
+- lstrlenW( file_data.cFileName ),
++ lengthAsInt,
+ p, (int) len - 1,
+ NULL, NULL );
+ if( w_ret == 0 )
diff --git a/thirdparty/mbedtls/include/mbedtls/asn1.h b/thirdparty/mbedtls/include/mbedtls/asn1.h
index fde328a128..96c1c9a8ab 100644
--- a/thirdparty/mbedtls/include/mbedtls/asn1.h
+++ b/thirdparty/mbedtls/include/mbedtls/asn1.h
@@ -88,6 +88,21 @@
#define MBEDTLS_ASN1_PRIMITIVE 0x00
#define MBEDTLS_ASN1_CONSTRUCTED 0x20
#define MBEDTLS_ASN1_CONTEXT_SPECIFIC 0x80
+
+/*
+ * Bit masks for each of the components of an ASN.1 tag as specified in
+ * ITU X.690 (08/2015), section 8.1 "General rules for encoding",
+ * paragraph 8.1.2.2:
+ *
+ * Bit 8 7 6 5 1
+ * +-------+-----+------------+
+ * | Class | P/C | Tag number |
+ * +-------+-----+------------+
+ */
+#define MBEDTLS_ASN1_TAG_CLASS_MASK 0xC0
+#define MBEDTLS_ASN1_TAG_PC_MASK 0x20
+#define MBEDTLS_ASN1_TAG_VALUE_MASK 0x1F
+
/* \} name */
/* \} addtogroup asn1_module */
diff --git a/thirdparty/mbedtls/include/mbedtls/ccm.h b/thirdparty/mbedtls/include/mbedtls/ccm.h
index 5a9ee4a1cd..630b7fdf6c 100644
--- a/thirdparty/mbedtls/include/mbedtls/ccm.h
+++ b/thirdparty/mbedtls/include/mbedtls/ccm.h
@@ -105,7 +105,7 @@ void mbedtls_ccm_free( mbedtls_ccm_context *ctx );
* Must be at least \p length Bytes wide.
* \param tag The buffer holding the tag.
* \param tag_len The length of the tag to generate in Bytes:
- * 4, 6, 8, 10, 14 or 16.
+ * 4, 6, 8, 10, 12, 14 or 16.
*
* \note The tag is written to a separate buffer. To concatenate
* the \p tag with the \p output, as done in <em>RFC-3610:
@@ -131,10 +131,13 @@ int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length,
* \param iv_len The length of the IV in Bytes: 7, 8, 9, 10, 11, 12, or 13.
* \param add The additional data field.
* \param add_len The length of additional data in Bytes.
+ * Must be less than 2^16 - 2^8.
* \param input The buffer holding the input data.
* \param output The buffer holding the output data.
+ * Must be at least \p length Bytes wide.
* \param tag The buffer holding the tag.
* \param tag_len The length of the tag in Bytes.
+ * 4, 6, 8, 10, 12, 14 or 16.
*
* \return 0 if successful and authenticated, or
* #MBEDTLS_ERR_CCM_AUTH_FAILED if the tag does not match.
diff --git a/thirdparty/mbedtls/include/mbedtls/check_config.h b/thirdparty/mbedtls/include/mbedtls/check_config.h
index 1143aa2687..be80332963 100644
--- a/thirdparty/mbedtls/include/mbedtls/check_config.h
+++ b/thirdparty/mbedtls/include/mbedtls/check_config.h
@@ -78,6 +78,10 @@
#error "MBEDTLS_DHM_C defined, but not all prerequisites"
#endif
+#if defined(MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT) && !defined(MBEDTLS_SSL_TRUNCATED_HMAC)
+#error "MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT defined, but not all prerequisites"
+#endif
+
#if defined(MBEDTLS_CMAC_C) && \
!defined(MBEDTLS_AES_C) && !defined(MBEDTLS_DES_C)
#error "MBEDTLS_CMAC_C defined, but not all prerequisites"
diff --git a/thirdparty/mbedtls/include/mbedtls/config.h b/thirdparty/mbedtls/include/mbedtls/config.h
index 79eedffddd..b5905ef9d0 100644
--- a/thirdparty/mbedtls/include/mbedtls/config.h
+++ b/thirdparty/mbedtls/include/mbedtls/config.h
@@ -1049,7 +1049,8 @@
/**
* \def MBEDTLS_RSA_NO_CRT
*
- * Do not use the Chinese Remainder Theorem for the RSA private operation.
+ * Do not use the Chinese Remainder Theorem
+ * for the RSA private operation.
*
* Uncomment this macro to disable the use of CRT in RSA.
*
@@ -1412,6 +1413,30 @@
#define MBEDTLS_SSL_TRUNCATED_HMAC
/**
+ * \def MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT
+ *
+ * Fallback to old (pre-2.7), non-conforming implementation of the truncated
+ * HMAC extension which also truncates the HMAC key. Note that this option is
+ * only meant for a transitory upgrade period and is likely to be removed in
+ * a future version of the library.
+ *
+ * \warning The old implementation is non-compliant and has a security weakness
+ * (2^80 brute force attack on the HMAC key used for a single,
+ * uninterrupted connection). This should only be enabled temporarily
+ * when (1) the use of truncated HMAC is essential in order to save
+ * bandwidth, and (2) the peer is an Mbed TLS stack that doesn't use
+ * the fixed implementation yet (pre-2.7).
+ *
+ * \deprecated This option is deprecated and will likely be removed in a
+ * future version of Mbed TLS.
+ *
+ * Uncomment to fallback to old, non-compliant truncated HMAC implementation.
+ *
+ * Requires: MBEDTLS_SSL_TRUNCATED_HMAC
+ */
+//#define MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT
+
+/**
* \def MBEDTLS_THREADING_ALT
*
* Provide your own alternate threading implementation.
@@ -1517,6 +1542,9 @@
*
* \note Currently compression can't be used with DTLS.
*
+ * \deprecated This feature is deprecated and will be removed
+ * in the next major revision of the library.
+ *
* Used in: library/ssl_tls.c
* library/ssl_cli.c
* library/ssl_srv.c
diff --git a/thirdparty/mbedtls/include/mbedtls/dhm.h b/thirdparty/mbedtls/include/mbedtls/dhm.h
index da2e66b111..00fafd8d16 100644
--- a/thirdparty/mbedtls/include/mbedtls/dhm.h
+++ b/thirdparty/mbedtls/include/mbedtls/dhm.h
@@ -372,7 +372,7 @@ MBEDTLS_DEPRECATED typedef char const * mbedtls_deprecated_constant_t;
* in <em>RFC-5114: Additional Diffie-Hellman Groups for Use with
* IETF Standards</em>.
*/
-#define MBEDTLS_DHM_RFC5114_MODP_P \
+#define MBEDTLS_DHM_RFC5114_MODP_2048_P \
MBEDTLS_DEPRECATED_STRING_CONSTANT( \
"AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1" \
"B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15" \
diff --git a/thirdparty/mbedtls/include/mbedtls/md2.h b/thirdparty/mbedtls/include/mbedtls/md2.h
index 2ff3f171a3..0fd8b5afcc 100644
--- a/thirdparty/mbedtls/include/mbedtls/md2.h
+++ b/thirdparty/mbedtls/include/mbedtls/md2.h
@@ -39,11 +39,6 @@
#define MBEDTLS_ERR_MD2_HW_ACCEL_FAILED -0x002B /**< MD2 hardware accelerator failed */
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
- !defined(inline) && !defined(__cplusplus)
-#define inline __inline
-#endif
-
#if !defined(MBEDTLS_MD2_ALT)
// Regular implementation
//
@@ -187,11 +182,7 @@ int mbedtls_internal_md2_process( mbedtls_md2_context *ctx );
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md2_starts(
- mbedtls_md2_context *ctx )
-{
- mbedtls_md2_starts_ret( ctx );
-}
+MBEDTLS_DEPRECATED void mbedtls_md2_starts( mbedtls_md2_context *ctx );
/**
* \brief MD2 process buffer
@@ -207,13 +198,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md2_starts(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md2_update(
- mbedtls_md2_context *ctx,
- const unsigned char *input,
- size_t ilen )
-{
- mbedtls_md2_update_ret( ctx, input, ilen );
-}
+MBEDTLS_DEPRECATED void mbedtls_md2_update( mbedtls_md2_context *ctx,
+ const unsigned char *input,
+ size_t ilen );
/**
* \brief MD2 final digest
@@ -228,12 +215,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md2_update(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md2_finish(
- mbedtls_md2_context *ctx,
- unsigned char output[16] )
-{
- mbedtls_md2_finish_ret( ctx, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_md2_finish( mbedtls_md2_context *ctx,
+ unsigned char output[16] );
/**
* \brief MD2 process data block (internal use only)
@@ -247,11 +230,7 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md2_finish(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md2_process(
- mbedtls_md2_context *ctx )
-{
- mbedtls_internal_md2_process( ctx );
-}
+MBEDTLS_DEPRECATED void mbedtls_md2_process( mbedtls_md2_context *ctx );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -304,12 +283,9 @@ int mbedtls_md2_ret( const unsigned char *input,
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md2( const unsigned char *input,
- size_t ilen,
- unsigned char output[16] )
-{
- mbedtls_md2_ret( input, ilen, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_md2( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[16] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
diff --git a/thirdparty/mbedtls/include/mbedtls/md4.h b/thirdparty/mbedtls/include/mbedtls/md4.h
index a2ab57f078..23fa95e46a 100644
--- a/thirdparty/mbedtls/include/mbedtls/md4.h
+++ b/thirdparty/mbedtls/include/mbedtls/md4.h
@@ -40,11 +40,6 @@
#define MBEDTLS_ERR_MD4_HW_ACCEL_FAILED -0x002D /**< MD4 hardware accelerator failed */
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
- !defined(inline) && !defined(__cplusplus)
-#define inline __inline
-#endif
-
#if !defined(MBEDTLS_MD4_ALT)
// Regular implementation
//
@@ -188,11 +183,7 @@ int mbedtls_internal_md4_process( mbedtls_md4_context *ctx,
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md4_starts(
- mbedtls_md4_context *ctx )
-{
- mbedtls_md4_starts_ret( ctx );
-}
+MBEDTLS_DEPRECATED void mbedtls_md4_starts( mbedtls_md4_context *ctx );
/**
* \brief MD4 process buffer
@@ -208,13 +199,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md4_starts(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md4_update(
- mbedtls_md4_context *ctx,
- const unsigned char *input,
- size_t ilen )
-{
- mbedtls_md4_update_ret( ctx, input, ilen );
-}
+MBEDTLS_DEPRECATED void mbedtls_md4_update( mbedtls_md4_context *ctx,
+ const unsigned char *input,
+ size_t ilen );
/**
* \brief MD4 final digest
@@ -229,12 +216,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md4_update(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md4_finish(
- mbedtls_md4_context *ctx,
- unsigned char output[16] )
-{
- mbedtls_md4_finish_ret( ctx, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_md4_finish( mbedtls_md4_context *ctx,
+ unsigned char output[16] );
/**
* \brief MD4 process data block (internal use only)
@@ -249,12 +232,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md4_finish(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md4_process(
- mbedtls_md4_context *ctx,
- const unsigned char data[64] )
-{
- mbedtls_internal_md4_process( ctx, data );
-}
+MBEDTLS_DEPRECATED void mbedtls_md4_process( mbedtls_md4_context *ctx,
+ const unsigned char data[64] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -309,12 +288,9 @@ int mbedtls_md4_ret( const unsigned char *input,
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md4( const unsigned char *input,
- size_t ilen,
- unsigned char output[16] )
-{
- mbedtls_md4_ret( input, ilen, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_md4( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[16] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
diff --git a/thirdparty/mbedtls/include/mbedtls/md5.h b/thirdparty/mbedtls/include/mbedtls/md5.h
index d49391f811..06ea4c5d44 100644
--- a/thirdparty/mbedtls/include/mbedtls/md5.h
+++ b/thirdparty/mbedtls/include/mbedtls/md5.h
@@ -43,11 +43,6 @@
// Regular implementation
//
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
- !defined(inline) && !defined(__cplusplus)
-#define inline __inline
-#endif
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -188,11 +183,7 @@ int mbedtls_internal_md5_process( mbedtls_md5_context *ctx,
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md5_starts(
- mbedtls_md5_context *ctx )
-{
- mbedtls_md5_starts_ret( ctx );
-}
+MBEDTLS_DEPRECATED void mbedtls_md5_starts( mbedtls_md5_context *ctx );
/**
* \brief MD5 process buffer
@@ -208,13 +199,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md5_starts(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md5_update(
- mbedtls_md5_context *ctx,
- const unsigned char *input,
- size_t ilen )
-{
- mbedtls_md5_update_ret( ctx, input, ilen );
-}
+MBEDTLS_DEPRECATED void mbedtls_md5_update( mbedtls_md5_context *ctx,
+ const unsigned char *input,
+ size_t ilen );
/**
* \brief MD5 final digest
@@ -229,12 +216,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md5_update(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md5_finish(
- mbedtls_md5_context *ctx,
- unsigned char output[16] )
-{
- mbedtls_md5_finish_ret( ctx, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_md5_finish( mbedtls_md5_context *ctx,
+ unsigned char output[16] );
/**
* \brief MD5 process data block (internal use only)
@@ -249,12 +232,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md5_finish(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md5_process(
- mbedtls_md5_context *ctx,
- const unsigned char data[64] )
-{
- mbedtls_internal_md5_process( ctx, data );
-}
+MBEDTLS_DEPRECATED void mbedtls_md5_process( mbedtls_md5_context *ctx,
+ const unsigned char data[64] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -309,12 +288,9 @@ int mbedtls_md5_ret( const unsigned char *input,
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md5( const unsigned char *input,
- size_t ilen,
- unsigned char output[16] )
-{
- mbedtls_md5_ret( input, ilen, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_md5( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[16] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
diff --git a/thirdparty/mbedtls/include/mbedtls/oid.h b/thirdparty/mbedtls/include/mbedtls/oid.h
index bf2ef5ece4..408645ece7 100644
--- a/thirdparty/mbedtls/include/mbedtls/oid.h
+++ b/thirdparty/mbedtls/include/mbedtls/oid.h
@@ -228,6 +228,14 @@
#define MBEDTLS_OID_HMAC_SHA1 MBEDTLS_OID_RSA_COMPANY "\x02\x07" /**< id-hmacWithSHA1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 7 } */
+#define MBEDTLS_OID_HMAC_SHA224 MBEDTLS_OID_RSA_COMPANY "\x02\x08" /**< id-hmacWithSHA224 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 8 } */
+
+#define MBEDTLS_OID_HMAC_SHA256 MBEDTLS_OID_RSA_COMPANY "\x02\x09" /**< id-hmacWithSHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 9 } */
+
+#define MBEDTLS_OID_HMAC_SHA384 MBEDTLS_OID_RSA_COMPANY "\x02\x0A" /**< id-hmacWithSHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 10 } */
+
+#define MBEDTLS_OID_HMAC_SHA512 MBEDTLS_OID_RSA_COMPANY "\x02\x0B" /**< id-hmacWithSHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 11 } */
+
/*
* Encryption algorithms
*/
@@ -514,6 +522,16 @@ int mbedtls_oid_get_oid_by_sig_alg( mbedtls_pk_type_t pk_alg, mbedtls_md_type_t
* \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
*/
int mbedtls_oid_get_md_alg( const mbedtls_asn1_buf *oid, mbedtls_md_type_t *md_alg );
+
+/**
+ * \brief Translate hmac algorithm OID into md_type
+ *
+ * \param oid OID to use
+ * \param md_hmac place to store message hmac algorithm
+ *
+ * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_md_hmac( const mbedtls_asn1_buf *oid, mbedtls_md_type_t *md_hmac );
#endif /* MBEDTLS_MD_C */
/**
diff --git a/thirdparty/mbedtls/include/mbedtls/ripemd160.h b/thirdparty/mbedtls/include/mbedtls/ripemd160.h
index c21868b185..3a8b50a621 100644
--- a/thirdparty/mbedtls/include/mbedtls/ripemd160.h
+++ b/thirdparty/mbedtls/include/mbedtls/ripemd160.h
@@ -35,11 +35,6 @@
#define MBEDTLS_ERR_RIPEMD160_HW_ACCEL_FAILED -0x0031 /**< RIPEMD160 hardware accelerator failed */
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
- !defined(inline) && !defined(__cplusplus)
-#define inline __inline
-#endif
-
#if !defined(MBEDTLS_RIPEMD160_ALT)
// Regular implementation
//
@@ -139,11 +134,8 @@ int mbedtls_internal_ripemd160_process( mbedtls_ripemd160_context *ctx,
*
* \param ctx context to be initialized
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_starts(
- mbedtls_ripemd160_context *ctx )
-{
- mbedtls_ripemd160_starts_ret( ctx );
-}
+MBEDTLS_DEPRECATED void mbedtls_ripemd160_starts(
+ mbedtls_ripemd160_context *ctx );
/**
* \brief RIPEMD-160 process buffer
@@ -154,13 +146,10 @@ MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_starts(
* \param input buffer holding the data
* \param ilen length of the input data
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_update(
+MBEDTLS_DEPRECATED void mbedtls_ripemd160_update(
mbedtls_ripemd160_context *ctx,
const unsigned char *input,
- size_t ilen )
-{
- mbedtls_ripemd160_update_ret( ctx, input, ilen );
-}
+ size_t ilen );
/**
* \brief RIPEMD-160 final digest
@@ -170,12 +159,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_update(
* \param ctx RIPEMD-160 context
* \param output RIPEMD-160 checksum result
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_finish(
+MBEDTLS_DEPRECATED void mbedtls_ripemd160_finish(
mbedtls_ripemd160_context *ctx,
- unsigned char output[20] )
-{
- mbedtls_ripemd160_finish_ret( ctx, output );
-}
+ unsigned char output[20] );
/**
* \brief RIPEMD-160 process data block (internal use only)
@@ -185,12 +171,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_finish(
* \param ctx RIPEMD-160 context
* \param data buffer holding one block of data
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_process(
+MBEDTLS_DEPRECATED void mbedtls_ripemd160_process(
mbedtls_ripemd160_context *ctx,
- const unsigned char data[64] )
-{
- mbedtls_internal_ripemd160_process( ctx, data );
-}
+ const unsigned char data[64] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -235,13 +218,9 @@ int mbedtls_ripemd160_ret( const unsigned char *input,
* \param ilen length of the input data
* \param output RIPEMD-160 checksum result
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160(
- const unsigned char *input,
- size_t ilen,
- unsigned char output[20] )
-{
- mbedtls_ripemd160_ret( input, ilen, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_ripemd160( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[20] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
diff --git a/thirdparty/mbedtls/include/mbedtls/rsa.h b/thirdparty/mbedtls/include/mbedtls/rsa.h
index fb2f77f94f..5548f3c127 100644
--- a/thirdparty/mbedtls/include/mbedtls/rsa.h
+++ b/thirdparty/mbedtls/include/mbedtls/rsa.h
@@ -518,6 +518,18 @@ int mbedtls_rsa_public( mbedtls_rsa_context *ctx,
*
* \note The input and output buffers must be large
* enough. For example, 128 Bytes if RSA-1024 is used.
+ *
+ * \note Blinding is used if and only if a PRNG is provided.
+ *
+ * \note If blinding is used, both the base of exponentation
+ * and the exponent are blinded, providing protection
+ * against some side-channel attacks.
+ *
+ * \warning It is deprecated and a security risk to not provide
+ * a PRNG here and thereby prevent the use of blinding.
+ * Future versions of the library may enforce the presence
+ * of a PRNG.
+ *
*/
int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
diff --git a/thirdparty/mbedtls/include/mbedtls/sha1.h b/thirdparty/mbedtls/include/mbedtls/sha1.h
index e4f8650216..05540cde12 100644
--- a/thirdparty/mbedtls/include/mbedtls/sha1.h
+++ b/thirdparty/mbedtls/include/mbedtls/sha1.h
@@ -39,11 +39,6 @@
#define MBEDTLS_ERR_SHA1_HW_ACCEL_FAILED -0x0035 /**< SHA-1 hardware accelerator failed */
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
- !defined(inline) && !defined(__cplusplus)
-#define inline __inline
-#endif
-
#if !defined(MBEDTLS_SHA1_ALT)
// Regular implementation
//
@@ -190,11 +185,7 @@ int mbedtls_internal_sha1_process( mbedtls_sha1_context *ctx,
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha1_starts(
- mbedtls_sha1_context *ctx )
-{
- mbedtls_sha1_starts_ret( ctx );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha1_starts( mbedtls_sha1_context *ctx );
/**
* \brief SHA-1 process buffer
@@ -210,13 +201,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha1_starts(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha1_update(
- mbedtls_sha1_context *ctx,
- const unsigned char *input,
- size_t ilen )
-{
- mbedtls_sha1_update_ret( ctx, input, ilen );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha1_update( mbedtls_sha1_context *ctx,
+ const unsigned char *input,
+ size_t ilen );
/**
* \brief SHA-1 final digest
@@ -231,12 +218,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha1_update(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha1_finish(
- mbedtls_sha1_context *ctx,
- unsigned char output[20] )
-{
- mbedtls_sha1_finish_ret( ctx, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha1_finish( mbedtls_sha1_context *ctx,
+ unsigned char output[20] );
/**
* \brief SHA-1 process data block (internal use only)
@@ -251,12 +234,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha1_finish(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha1_process(
- mbedtls_sha1_context *ctx,
- const unsigned char data[64] )
-{
- mbedtls_internal_sha1_process( ctx, data );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha1_process( mbedtls_sha1_context *ctx,
+ const unsigned char data[64] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -317,12 +296,9 @@ int mbedtls_sha1_ret( const unsigned char *input,
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha1( const unsigned char *input,
- size_t ilen,
- unsigned char output[20] )
-{
- mbedtls_sha1_ret( input, ilen, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha1( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[20] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
diff --git a/thirdparty/mbedtls/include/mbedtls/sha256.h b/thirdparty/mbedtls/include/mbedtls/sha256.h
index a2b6e11644..ffb16c277a 100644
--- a/thirdparty/mbedtls/include/mbedtls/sha256.h
+++ b/thirdparty/mbedtls/include/mbedtls/sha256.h
@@ -35,10 +35,6 @@
#define MBEDTLS_ERR_SHA256_HW_ACCEL_FAILED -0x0037 /**< SHA-256 hardware accelerator failed */
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
- !defined(inline) && !defined(__cplusplus)
-#define inline __inline
-#endif
#if !defined(MBEDTLS_SHA256_ALT)
// Regular implementation
//
@@ -156,12 +152,8 @@ int mbedtls_internal_sha256_process( mbedtls_sha256_context *ctx,
* <ul><li>0: Use SHA-256.</li>
* <li>1: Use SHA-224.</li></ul>
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha256_starts(
- mbedtls_sha256_context *ctx,
- int is224 )
-{
- mbedtls_sha256_starts_ret( ctx, is224 );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha256_starts( mbedtls_sha256_context *ctx,
+ int is224 );
/**
* \brief This function feeds an input buffer into an ongoing
@@ -173,13 +165,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha256_starts(
* \param input The buffer holding the data.
* \param ilen The length of the input data.
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha256_update(
- mbedtls_sha256_context *ctx,
- const unsigned char *input,
- size_t ilen )
-{
- mbedtls_sha256_update_ret( ctx, input, ilen );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha256_update( mbedtls_sha256_context *ctx,
+ const unsigned char *input,
+ size_t ilen );
/**
* \brief This function finishes the SHA-256 operation, and writes
@@ -190,12 +178,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha256_update(
* \param ctx The SHA-256 context.
* \param output The SHA-224or SHA-256 checksum result.
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha256_finish(
- mbedtls_sha256_context *ctx,
- unsigned char output[32] )
-{
- mbedtls_sha256_finish_ret( ctx, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha256_finish( mbedtls_sha256_context *ctx,
+ unsigned char output[32] );
/**
* \brief This function processes a single data block within
@@ -207,12 +191,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha256_finish(
* \param ctx The SHA-256 context.
* \param data The buffer holding one block of data.
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha256_process(
- mbedtls_sha256_context *ctx,
- const unsigned char data[64] )
-{
- mbedtls_internal_sha256_process( ctx, data );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha256_process( mbedtls_sha256_context *ctx,
+ const unsigned char data[64] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -276,14 +256,10 @@ int mbedtls_sha256_ret( const unsigned char *input,
* <ul><li>0: Use SHA-256.</li>
* <li>1: Use SHA-224.</li></ul>
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha256(
- const unsigned char *input,
- size_t ilen,
- unsigned char output[32],
- int is224 )
-{
- mbedtls_sha256_ret( input, ilen, output, is224 );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha256( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[32],
+ int is224 );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
diff --git a/thirdparty/mbedtls/include/mbedtls/sha512.h b/thirdparty/mbedtls/include/mbedtls/sha512.h
index 52ae204d44..8404a2d599 100644
--- a/thirdparty/mbedtls/include/mbedtls/sha512.h
+++ b/thirdparty/mbedtls/include/mbedtls/sha512.h
@@ -35,10 +35,6 @@
#define MBEDTLS_ERR_SHA512_HW_ACCEL_FAILED -0x0039 /**< SHA-512 hardware accelerator failed */
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
- !defined(inline) && !defined(__cplusplus)
-#define inline __inline
-#endif
#if !defined(MBEDTLS_SHA512_ALT)
// Regular implementation
//
@@ -156,12 +152,8 @@ int mbedtls_internal_sha512_process( mbedtls_sha512_context *ctx,
* <ul><li>0: Use SHA-512.</li>
* <li>1: Use SHA-384.</li></ul>
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha512_starts(
- mbedtls_sha512_context *ctx,
- int is384 )
-{
- mbedtls_sha512_starts_ret( ctx, is384 );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha512_starts( mbedtls_sha512_context *ctx,
+ int is384 );
/**
* \brief This function feeds an input buffer into an ongoing
@@ -173,13 +165,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha512_starts(
* \param input The buffer holding the data.
* \param ilen The length of the input data.
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha512_update(
- mbedtls_sha512_context *ctx,
- const unsigned char *input,
- size_t ilen )
-{
- mbedtls_sha512_update_ret( ctx, input, ilen );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha512_update( mbedtls_sha512_context *ctx,
+ const unsigned char *input,
+ size_t ilen );
/**
* \brief This function finishes the SHA-512 operation, and writes
@@ -190,12 +178,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha512_update(
* \param ctx The SHA-512 context.
* \param output The SHA-384 or SHA-512 checksum result.
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha512_finish(
- mbedtls_sha512_context *ctx,
- unsigned char output[64] )
-{
- mbedtls_sha512_finish_ret( ctx, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha512_finish( mbedtls_sha512_context *ctx,
+ unsigned char output[64] );
/**
* \brief This function processes a single data block within
@@ -207,12 +191,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha512_finish(
* \param ctx The SHA-512 context.
* \param data The buffer holding one block of data.
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha512_process(
+MBEDTLS_DEPRECATED void mbedtls_sha512_process(
mbedtls_sha512_context *ctx,
- const unsigned char data[128] )
-{
- mbedtls_internal_sha512_process( ctx, data );
-}
+ const unsigned char data[128] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -278,14 +259,10 @@ int mbedtls_sha512_ret( const unsigned char *input,
* <ul><li>0: Use SHA-512.</li>
* <li>1: Use SHA-384.</li></ul>
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha512(
- const unsigned char *input,
- size_t ilen,
- unsigned char output[64],
- int is384 )
-{
- mbedtls_sha512_ret( input, ilen, output, is384 );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha512( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[64],
+ int is384 );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
diff --git a/thirdparty/mbedtls/include/mbedtls/ssl.h b/thirdparty/mbedtls/include/mbedtls/ssl.h
index 51e843ae24..dffc162191 100644
--- a/thirdparty/mbedtls/include/mbedtls/ssl.h
+++ b/thirdparty/mbedtls/include/mbedtls/ssl.h
@@ -49,6 +49,15 @@
#endif
#if defined(MBEDTLS_ZLIB_SUPPORT)
+
+#if defined(MBEDTLS_DEPRECATED_WARNING)
+#warning "Record compression support via MBEDTLS_ZLIB_SUPPORT is deprecated and will be removed in the next major revision of the library"
+#endif
+
+#if defined(MBEDTLS_DEPRECATED_REMOVED)
+#error "Record compression support via MBEDTLS_ZLIB_SUPPORT is deprecated and cannot be used if MBEDTLS_DEPRECATED_REMOVED is set"
+#endif
+
#include "zlib.h"
#endif
@@ -971,8 +980,13 @@ void mbedtls_ssl_init( mbedtls_ssl_context *ssl );
* \note No copy of the configuration context is made, it can be
* shared by many mbedtls_ssl_context structures.
*
- * \warning Modifying the conf structure after it has been used in this
- * function is unsupported!
+ * \warning The conf structure will be accessed during the session.
+ * It must not be modified or freed as long as the session
+ * is active.
+ *
+ * \warning This function must be called exactly once per context.
+ * Calling mbedtls_ssl_setup again is not supported, even
+ * if no session is active.
*
* \param ssl SSL context
* \param conf SSL configuration to use
diff --git a/thirdparty/mbedtls/include/mbedtls/ssl_internal.h b/thirdparty/mbedtls/include/mbedtls/ssl_internal.h
index 9f583a8777..60b431a0f4 100644
--- a/thirdparty/mbedtls/include/mbedtls/ssl_internal.h
+++ b/thirdparty/mbedtls/include/mbedtls/ssl_internal.h
@@ -71,6 +71,9 @@
#endif /* MBEDTLS_SSL_PROTO_TLS1 */
#endif /* MBEDTLS_SSL_PROTO_SSL3 */
+#define MBEDTLS_SSL_MIN_VALID_MINOR_VERSION MBEDTLS_SSL_MINOR_VERSION_1
+#define MBEDTLS_SSL_MIN_VALID_MAJOR_VERSION MBEDTLS_SSL_MAJOR_VERSION_3
+
/* Determine maximum supported version */
#define MBEDTLS_SSL_MAX_MAJOR_VERSION MBEDTLS_SSL_MAJOR_VERSION_3
diff --git a/thirdparty/mbedtls/include/mbedtls/version.h b/thirdparty/mbedtls/include/mbedtls/version.h
index 961be59c35..c3ee649f5c 100644
--- a/thirdparty/mbedtls/include/mbedtls/version.h
+++ b/thirdparty/mbedtls/include/mbedtls/version.h
@@ -39,7 +39,7 @@
* Major, Minor, Patchlevel
*/
#define MBEDTLS_VERSION_MAJOR 2
-#define MBEDTLS_VERSION_MINOR 7
+#define MBEDTLS_VERSION_MINOR 8
#define MBEDTLS_VERSION_PATCH 0
/**
@@ -47,9 +47,9 @@
* MMNNPP00
* Major version | Minor version | Patch version
*/
-#define MBEDTLS_VERSION_NUMBER 0x02070000
-#define MBEDTLS_VERSION_STRING "2.7.0"
-#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.7.0"
+#define MBEDTLS_VERSION_NUMBER 0x02080000
+#define MBEDTLS_VERSION_STRING "2.8.0"
+#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.8.0"
#if defined(MBEDTLS_VERSION_C)
diff --git a/thirdparty/mbedtls/library/aes.c b/thirdparty/mbedtls/library/aes.c
index dba4a5f578..3d2eac82dd 100644
--- a/thirdparty/mbedtls/library/aes.c
+++ b/thirdparty/mbedtls/library/aes.c
@@ -765,12 +765,14 @@ int mbedtls_internal_aes_encrypt( mbedtls_aes_context *ctx,
}
#endif /* !MBEDTLS_AES_ENCRYPT_ALT */
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
void mbedtls_aes_encrypt( mbedtls_aes_context *ctx,
const unsigned char input[16],
unsigned char output[16] )
{
mbedtls_internal_aes_encrypt( ctx, input, output );
}
+#endif /* !MBEDTLS_DEPRECATED_REMOVED */
/*
* AES-ECB block decryption
@@ -831,12 +833,14 @@ int mbedtls_internal_aes_decrypt( mbedtls_aes_context *ctx,
}
#endif /* !MBEDTLS_AES_DECRYPT_ALT */
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
void mbedtls_aes_decrypt( mbedtls_aes_context *ctx,
const unsigned char input[16],
unsigned char output[16] )
{
mbedtls_internal_aes_decrypt( ctx, input, output );
}
+#endif /* !MBEDTLS_DEPRECATED_REMOVED */
/*
* AES-ECB block encryption/decryption
diff --git a/thirdparty/mbedtls/library/bignum.c b/thirdparty/mbedtls/library/bignum.c
index d27c130bcb..9f13da4421 100644
--- a/thirdparty/mbedtls/library/bignum.c
+++ b/thirdparty/mbedtls/library/bignum.c
@@ -1623,7 +1623,7 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi
mbedtls_mpi RR, T, W[ 2 << MBEDTLS_MPI_WINDOW_SIZE ], Apos;
int neg;
- if( mbedtls_mpi_cmp_int( N, 0 ) < 0 || ( N->p[0] & 1 ) == 0 )
+ if( mbedtls_mpi_cmp_int( N, 0 ) <= 0 || ( N->p[0] & 1 ) == 0 )
return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
if( mbedtls_mpi_cmp_int( E, 0 ) < 0 )
diff --git a/thirdparty/mbedtls/library/ctr_drbg.c b/thirdparty/mbedtls/library/ctr_drbg.c
index c2310cb579..ff532a0134 100644
--- a/thirdparty/mbedtls/library/ctr_drbg.c
+++ b/thirdparty/mbedtls/library/ctr_drbg.c
@@ -19,7 +19,7 @@
* This file is part of mbed TLS (https://tls.mbed.org)
*/
/*
- * The NIST SP 800-90 DRBGs are described in the following publucation.
+ * The NIST SP 800-90 DRBGs are described in the following publication.
*
* http://csrc.nist.gov/publications/nistpubs/800-90/SP800-90revised_March2007.pdf
*/
diff --git a/thirdparty/mbedtls/library/debug.c b/thirdparty/mbedtls/library/debug.c
index f9229b3606..db3924ac54 100644
--- a/thirdparty/mbedtls/library/debug.c
+++ b/thirdparty/mbedtls/library/debug.c
@@ -91,7 +91,7 @@ void mbedtls_debug_print_msg( const mbedtls_ssl_context *ssl, int level,
va_start( argp, format );
#if defined(_WIN32)
-#if defined(_TRUNCATE)
+#if defined(_TRUNCATE) && !defined(__MINGW32__)
ret = _vsnprintf_s( str, DEBUG_BUF_SIZE, _TRUNCATE, format, argp );
#else
ret = _vsnprintf( str, DEBUG_BUF_SIZE, format, argp );
diff --git a/thirdparty/mbedtls/library/entropy_poll.c b/thirdparty/mbedtls/library/entropy_poll.c
index 5e8a090b37..ed350735d0 100644
--- a/thirdparty/mbedtls/library/entropy_poll.c
+++ b/thirdparty/mbedtls/library/entropy_poll.c
@@ -55,16 +55,17 @@
#endif
#include <windows.h>
#include <bcrypt.h>
-#if _MSC_VER <= 1600
-/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and <intsafe.h> are included, as they
- * redefine a number of <TYPE>_MAX constants. These constants are guaranteed to be the same, though, so
- * we suppress the warning when including intsafe.h.
+#if defined(_MSC_VER) && _MSC_VER <= 1600
+/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and
+ * <intsafe.h> are included, as they redefine a number of <TYPE>_MAX constants.
+ * These constants are guaranteed to be the same, though, so we suppress the
+ * warning when including intsafe.h.
*/
#pragma warning( push )
#pragma warning( disable : 4005 )
#endif
#include <intsafe.h>
-#if _MSC_VER <= 1600
+#if defined(_MSC_VER) && _MSC_VER <= 1600
#pragma warning( pop )
#endif
@@ -76,8 +77,9 @@ int mbedtls_platform_entropy_poll( void *data, unsigned char *output, size_t len
*olen = 0;
/*
- * BCryptGenRandom takes ULONG for size, which is smaller than size_t on 64-bit platforms.
- * Ensure len's value can be safely converted into a ULONG.
+ * BCryptGenRandom takes ULONG for size, which is smaller than size_t on
+ * 64-bit Windows platforms. Ensure len's value can be safely converted into
+ * a ULONG.
*/
if ( FAILED( SizeTToULong( len, &len_as_ulong ) ) )
{
diff --git a/thirdparty/mbedtls/library/md2.c b/thirdparty/mbedtls/library/md2.c
index 5028e8c586..b88aa406af 100644
--- a/thirdparty/mbedtls/library/md2.c
+++ b/thirdparty/mbedtls/library/md2.c
@@ -115,6 +115,13 @@ int mbedtls_md2_starts_ret( mbedtls_md2_context *ctx )
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md2_starts( mbedtls_md2_context *ctx )
+{
+ mbedtls_md2_starts_ret( ctx );
+}
+#endif
+
#if !defined(MBEDTLS_MD2_PROCESS_ALT)
int mbedtls_internal_md2_process( mbedtls_md2_context *ctx )
{
@@ -151,6 +158,13 @@ int mbedtls_internal_md2_process( mbedtls_md2_context *ctx )
return( 0 );
}
+
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md2_process( mbedtls_md2_context *ctx )
+{
+ mbedtls_internal_md2_process( ctx );
+}
+#endif
#endif /* !MBEDTLS_MD2_PROCESS_ALT */
/*
@@ -187,6 +201,15 @@ int mbedtls_md2_update_ret( mbedtls_md2_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md2_update( mbedtls_md2_context *ctx,
+ const unsigned char *input,
+ size_t ilen )
+{
+ mbedtls_md2_update_ret( ctx, input, ilen );
+}
+#endif
+
/*
* MD2 final digest
*/
@@ -214,6 +237,14 @@ int mbedtls_md2_finish_ret( mbedtls_md2_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md2_finish( mbedtls_md2_context *ctx,
+ unsigned char output[16] )
+{
+ mbedtls_md2_finish_ret( ctx, output );
+}
+#endif
+
#endif /* !MBEDTLS_MD2_ALT */
/*
@@ -243,6 +274,15 @@ exit:
return( ret );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md2( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[16] )
+{
+ mbedtls_md2_ret( input, ilen, output );
+}
+#endif
+
#if defined(MBEDTLS_SELF_TEST)
/*
diff --git a/thirdparty/mbedtls/library/md4.c b/thirdparty/mbedtls/library/md4.c
index 34a4b0e24e..ba704f58e8 100644
--- a/thirdparty/mbedtls/library/md4.c
+++ b/thirdparty/mbedtls/library/md4.c
@@ -111,6 +111,13 @@ int mbedtls_md4_starts_ret( mbedtls_md4_context *ctx )
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md4_starts( mbedtls_md4_context *ctx )
+{
+ mbedtls_md4_starts_ret( ctx );
+}
+#endif
+
#if !defined(MBEDTLS_MD4_PROCESS_ALT)
int mbedtls_internal_md4_process( mbedtls_md4_context *ctx,
const unsigned char data[64] )
@@ -217,6 +224,14 @@ int mbedtls_internal_md4_process( mbedtls_md4_context *ctx,
return( 0 );
}
+
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md4_process( mbedtls_md4_context *ctx,
+ const unsigned char data[64] )
+{
+ mbedtls_internal_md4_process( ctx, data );
+}
+#endif
#endif /* !MBEDTLS_MD4_PROCESS_ALT */
/*
@@ -273,6 +288,15 @@ int mbedtls_md4_update_ret( mbedtls_md4_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md4_update( mbedtls_md4_context *ctx,
+ const unsigned char *input,
+ size_t ilen )
+{
+ mbedtls_md4_update_ret( ctx, input, ilen );
+}
+#endif
+
static const unsigned char md4_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -318,6 +342,14 @@ int mbedtls_md4_finish_ret( mbedtls_md4_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md4_finish( mbedtls_md4_context *ctx,
+ unsigned char output[16] )
+{
+ mbedtls_md4_finish_ret( ctx, output );
+}
+#endif
+
#endif /* !MBEDTLS_MD4_ALT */
/*
@@ -347,6 +379,15 @@ exit:
return( ret );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md4( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[16] )
+{
+ mbedtls_md4_ret( input, ilen, output );
+}
+#endif
+
#if defined(MBEDTLS_SELF_TEST)
/*
diff --git a/thirdparty/mbedtls/library/md5.c b/thirdparty/mbedtls/library/md5.c
index 8872dc467d..8440ebffcf 100644
--- a/thirdparty/mbedtls/library/md5.c
+++ b/thirdparty/mbedtls/library/md5.c
@@ -110,6 +110,13 @@ int mbedtls_md5_starts_ret( mbedtls_md5_context *ctx )
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md5_starts( mbedtls_md5_context *ctx )
+{
+ mbedtls_md5_starts_ret( ctx );
+}
+#endif
+
#if !defined(MBEDTLS_MD5_PROCESS_ALT)
int mbedtls_internal_md5_process( mbedtls_md5_context *ctx,
const unsigned char data[64] )
@@ -236,6 +243,14 @@ int mbedtls_internal_md5_process( mbedtls_md5_context *ctx,
return( 0 );
}
+
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md5_process( mbedtls_md5_context *ctx,
+ const unsigned char data[64] )
+{
+ mbedtls_internal_md5_process( ctx, data );
+}
+#endif
#endif /* !MBEDTLS_MD5_PROCESS_ALT */
/*
@@ -289,6 +304,15 @@ int mbedtls_md5_update_ret( mbedtls_md5_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md5_update( mbedtls_md5_context *ctx,
+ const unsigned char *input,
+ size_t ilen )
+{
+ mbedtls_md5_update_ret( ctx, input, ilen );
+}
+#endif
+
static const unsigned char md5_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -332,6 +356,14 @@ int mbedtls_md5_finish_ret( mbedtls_md5_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md5_finish( mbedtls_md5_context *ctx,
+ unsigned char output[16] )
+{
+ mbedtls_md5_finish_ret( ctx, output );
+}
+#endif
+
#endif /* !MBEDTLS_MD5_ALT */
/*
@@ -361,6 +393,15 @@ exit:
return( ret );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md5( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[16] )
+{
+ mbedtls_md5_ret( input, ilen, output );
+}
+#endif
+
#if defined(MBEDTLS_SELF_TEST)
/*
* RFC 1321 test vectors
diff --git a/thirdparty/mbedtls/library/memory_buffer_alloc.c b/thirdparty/mbedtls/library/memory_buffer_alloc.c
index 545d5a2c32..821ae2c708 100644
--- a/thirdparty/mbedtls/library/memory_buffer_alloc.c
+++ b/thirdparty/mbedtls/library/memory_buffer_alloc.c
@@ -113,7 +113,7 @@ static void debug_header( memory_header *hdr )
#endif
}
-static void debug_chain()
+static void debug_chain( void )
{
memory_header *cur = heap.first;
@@ -180,11 +180,11 @@ static int verify_header( memory_header *hdr )
return( 0 );
}
-static int verify_chain()
+static int verify_chain( void )
{
- memory_header *prv = heap.first, *cur = heap.first->next;
+ memory_header *prv = heap.first, *cur;
- if( verify_header( heap.first ) != 0 )
+ if( prv == NULL || verify_header( prv ) != 0 )
{
#if defined(MBEDTLS_MEMORY_DEBUG)
mbedtls_fprintf( stderr, "FATAL: verification of first header "
@@ -202,6 +202,8 @@ static int verify_chain()
return( 1 );
}
+ cur = heap.first->next;
+
while( cur != NULL )
{
if( verify_header( cur ) != 0 )
@@ -245,7 +247,9 @@ static void *buffer_alloc_calloc( size_t n, size_t size )
original_len = len = n * size;
- if( n != 0 && len / n != size )
+ if( n == 0 || size == 0 || len / n != size )
+ return( NULL );
+ else if( len > (size_t)-MBEDTLS_MEMORY_ALIGN_MULTIPLE )
return( NULL );
if( len % MBEDTLS_MEMORY_ALIGN_MULTIPLE )
@@ -386,7 +390,7 @@ static void buffer_alloc_free( void *ptr )
if( ptr == NULL || heap.buf == NULL || heap.first == NULL )
return;
- if( p < heap.buf || p > heap.buf + heap.len )
+ if( p < heap.buf || p >= heap.buf + heap.len )
{
#if defined(MBEDTLS_MEMORY_DEBUG)
mbedtls_fprintf( stderr, "FATAL: mbedtls_free() outside of managed "
@@ -500,13 +504,13 @@ void mbedtls_memory_buffer_set_verify( int verify )
heap.verify = verify;
}
-int mbedtls_memory_buffer_alloc_verify()
+int mbedtls_memory_buffer_alloc_verify( void )
{
return verify_chain();
}
#if defined(MBEDTLS_MEMORY_DEBUG)
-void mbedtls_memory_buffer_alloc_status()
+void mbedtls_memory_buffer_alloc_status( void )
{
mbedtls_fprintf( stderr,
"Current use: %zu blocks / %zu bytes, max: %zu blocks / "
@@ -570,8 +574,7 @@ static void buffer_alloc_free_mutexed( void *ptr )
void mbedtls_memory_buffer_alloc_init( unsigned char *buf, size_t len )
{
- memset( &heap, 0, sizeof(buffer_alloc_ctx) );
- memset( buf, 0, len );
+ memset( &heap, 0, sizeof( buffer_alloc_ctx ) );
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_init( &heap.mutex );
@@ -581,26 +584,30 @@ void mbedtls_memory_buffer_alloc_init( unsigned char *buf, size_t len )
mbedtls_platform_set_calloc_free( buffer_alloc_calloc, buffer_alloc_free );
#endif
- if( (size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE )
+ if( len < sizeof( memory_header ) + MBEDTLS_MEMORY_ALIGN_MULTIPLE )
+ return;
+ else if( (size_t)buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE )
{
/* Adjust len first since buf is used in the computation */
len -= MBEDTLS_MEMORY_ALIGN_MULTIPLE
- - (size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE;
+ - (size_t)buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE;
buf += MBEDTLS_MEMORY_ALIGN_MULTIPLE
- - (size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE;
+ - (size_t)buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE;
}
+ memset( buf, 0, len );
+
heap.buf = buf;
heap.len = len;
- heap.first = (memory_header *) buf;
- heap.first->size = len - sizeof(memory_header);
+ heap.first = (memory_header *)buf;
+ heap.first->size = len - sizeof( memory_header );
heap.first->magic1 = MAGIC1;
heap.first->magic2 = MAGIC2;
heap.first_free = heap.first;
}
-void mbedtls_memory_buffer_alloc_free()
+void mbedtls_memory_buffer_alloc_free( void )
{
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_free( &heap.mutex );
@@ -620,7 +627,7 @@ static int check_pointer( void *p )
return( 0 );
}
-static int check_all_free( )
+static int check_all_free( void )
{
if(
#if defined(MBEDTLS_MEMORY_DEBUG)
diff --git a/thirdparty/mbedtls/library/net_sockets.c b/thirdparty/mbedtls/library/net_sockets.c
index 754049005d..2fb548caa9 100644
--- a/thirdparty/mbedtls/library/net_sockets.c
+++ b/thirdparty/mbedtls/library/net_sockets.c
@@ -45,11 +45,14 @@
#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \
!defined(EFI32)
-#ifdef _WIN32_WINNT
+/* GODOT ADDITION */
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)
#undef _WIN32_WINNT
-#endif
/* Enables getaddrinfo() & Co */
-#define _WIN32_WINNT 0x0601
+#define _WIN32_WINNT 0x0501
+#endif
+/* END GODOT ADDITION */
+
#include <ws2tcpip.h>
#include <winsock2.h>
diff --git a/thirdparty/mbedtls/library/oid.c b/thirdparty/mbedtls/library/oid.c
index f13826ed74..edea950f8f 100644
--- a/thirdparty/mbedtls/library/oid.c
+++ b/thirdparty/mbedtls/library/oid.c
@@ -625,6 +625,51 @@ static const oid_md_alg_t oid_md_alg[] =
FN_OID_TYPED_FROM_ASN1(oid_md_alg_t, md_alg, oid_md_alg)
FN_OID_GET_ATTR1(mbedtls_oid_get_md_alg, oid_md_alg_t, md_alg, mbedtls_md_type_t, md_alg)
FN_OID_GET_OID_BY_ATTR1(mbedtls_oid_get_oid_by_md, oid_md_alg_t, oid_md_alg, mbedtls_md_type_t, md_alg)
+
+/*
+ * For HMAC digestAlgorithm
+ */
+typedef struct {
+ mbedtls_oid_descriptor_t descriptor;
+ mbedtls_md_type_t md_hmac;
+} oid_md_hmac_t;
+
+static const oid_md_hmac_t oid_md_hmac[] =
+{
+#if defined(MBEDTLS_SHA1_C)
+ {
+ { ADD_LEN( MBEDTLS_OID_HMAC_SHA1 ), "hmacSHA1", "HMAC-SHA-1" },
+ MBEDTLS_MD_SHA1,
+ },
+#endif /* MBEDTLS_SHA1_C */
+#if defined(MBEDTLS_SHA256_C)
+ {
+ { ADD_LEN( MBEDTLS_OID_HMAC_SHA224 ), "hmacSHA224", "HMAC-SHA-224" },
+ MBEDTLS_MD_SHA224,
+ },
+ {
+ { ADD_LEN( MBEDTLS_OID_HMAC_SHA256 ), "hmacSHA256", "HMAC-SHA-256" },
+ MBEDTLS_MD_SHA256,
+ },
+#endif /* MBEDTLS_SHA256_C */
+#if defined(MBEDTLS_SHA512_C)
+ {
+ { ADD_LEN( MBEDTLS_OID_HMAC_SHA384 ), "hmacSHA384", "HMAC-SHA-384" },
+ MBEDTLS_MD_SHA384,
+ },
+ {
+ { ADD_LEN( MBEDTLS_OID_HMAC_SHA512 ), "hmacSHA512", "HMAC-SHA-512" },
+ MBEDTLS_MD_SHA512,
+ },
+#endif /* MBEDTLS_SHA512_C */
+ {
+ { NULL, 0, NULL, NULL },
+ MBEDTLS_MD_NONE,
+ },
+};
+
+FN_OID_TYPED_FROM_ASN1(oid_md_hmac_t, md_hmac, oid_md_hmac)
+FN_OID_GET_ATTR1(mbedtls_oid_get_md_hmac, oid_md_hmac_t, md_hmac, mbedtls_md_type_t, md_hmac)
#endif /* MBEDTLS_MD_C */
#if defined(MBEDTLS_PKCS12_C)
diff --git a/thirdparty/mbedtls/library/pem.c b/thirdparty/mbedtls/library/pem.c
index c09651f4a2..ac86d7e479 100644
--- a/thirdparty/mbedtls/library/pem.c
+++ b/thirdparty/mbedtls/library/pem.c
@@ -442,7 +442,7 @@ int mbedtls_pem_write_buffer( const char *header, const char *footer,
unsigned char *buf, size_t buf_len, size_t *olen )
{
int ret;
- unsigned char *encode_buf, *c, *p = buf;
+ unsigned char *encode_buf = NULL, *c, *p = buf;
size_t len = 0, use_len, add_len = 0;
mbedtls_base64_encode( NULL, 0, &use_len, der_data, der_len );
@@ -454,7 +454,8 @@ int mbedtls_pem_write_buffer( const char *header, const char *footer,
return( MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL );
}
- if( ( encode_buf = mbedtls_calloc( 1, use_len ) ) == NULL )
+ if( use_len != 0 &&
+ ( ( encode_buf = mbedtls_calloc( 1, use_len ) ) == NULL ) )
return( MBEDTLS_ERR_PEM_ALLOC_FAILED );
if( ( ret = mbedtls_base64_encode( encode_buf, use_len, &use_len, der_data,
diff --git a/thirdparty/mbedtls/library/pkcs5.c b/thirdparty/mbedtls/library/pkcs5.c
index e28d5a8473..95f44fa98b 100644
--- a/thirdparty/mbedtls/library/pkcs5.c
+++ b/thirdparty/mbedtls/library/pkcs5.c
@@ -96,11 +96,9 @@ static int pkcs5_parse_pbkdf2_params( const mbedtls_asn1_buf *params,
if( ( ret = mbedtls_asn1_get_alg_null( &p, end, &prf_alg_oid ) ) != 0 )
return( MBEDTLS_ERR_PKCS5_INVALID_FORMAT + ret );
- if( MBEDTLS_OID_CMP( MBEDTLS_OID_HMAC_SHA1, &prf_alg_oid ) != 0 )
+ if( mbedtls_oid_get_md_hmac( &prf_alg_oid, md_type ) != 0 )
return( MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE );
- *md_type = MBEDTLS_MD_SHA1;
-
if( p != end )
return( MBEDTLS_ERR_PKCS5_INVALID_FORMAT +
MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
diff --git a/thirdparty/mbedtls/library/pkparse.c b/thirdparty/mbedtls/library/pkparse.c
index b4def4f914..9022db2f93 100644
--- a/thirdparty/mbedtls/library/pkparse.c
+++ b/thirdparty/mbedtls/library/pkparse.c
@@ -181,6 +181,10 @@ static int pk_get_ecparams( unsigned char **p, const unsigned char *end,
{
int ret;
+ if ( end - *p < 1 )
+ return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT +
+ MBEDTLS_ERR_ASN1_OUT_OF_DATA );
+
/* Tag may be either OID or SEQUENCE */
params->tag = **p;
if( params->tag != MBEDTLS_ASN1_OID
@@ -1277,6 +1281,9 @@ int mbedtls_pk_parse_key( mbedtls_pk_context *pk,
{
unsigned char *key_copy;
+ if( keylen == 0 )
+ return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
+
if( ( key_copy = mbedtls_calloc( 1, keylen ) ) == NULL )
return( MBEDTLS_ERR_PK_ALLOC_FAILED );
@@ -1348,11 +1355,45 @@ int mbedtls_pk_parse_public_key( mbedtls_pk_context *ctx,
{
int ret;
unsigned char *p;
+#if defined(MBEDTLS_RSA_C)
+ const mbedtls_pk_info_t *pk_info;
+#endif
#if defined(MBEDTLS_PEM_PARSE_C)
size_t len;
mbedtls_pem_context pem;
mbedtls_pem_init( &pem );
+#if defined(MBEDTLS_RSA_C)
+ /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */
+ if( keylen == 0 || key[keylen - 1] != '\0' )
+ ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT;
+ else
+ ret = mbedtls_pem_read_buffer( &pem,
+ "-----BEGIN RSA PUBLIC KEY-----",
+ "-----END RSA PUBLIC KEY-----",
+ key, NULL, 0, &len );
+
+ if( ret == 0 )
+ {
+ p = pem.buf;
+ if( ( pk_info = mbedtls_pk_info_from_type( MBEDTLS_PK_RSA ) ) == NULL )
+ return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
+
+ if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 )
+ return( ret );
+
+ if ( ( ret = pk_get_rsapubkey( &p, p + pem.buflen, mbedtls_pk_rsa( *ctx ) ) ) != 0 )
+ mbedtls_pk_free( ctx );
+
+ mbedtls_pem_free( &pem );
+ return( ret );
+ }
+ else if( ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT )
+ {
+ mbedtls_pem_free( &pem );
+ return( ret );
+ }
+#endif /* MBEDTLS_RSA_C */
/* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */
if( keylen == 0 || key[keylen - 1] != '\0' )
@@ -1368,23 +1409,43 @@ int mbedtls_pk_parse_public_key( mbedtls_pk_context *ctx,
/*
* Was PEM encoded
*/
- key = pem.buf;
- keylen = pem.buflen;
+ p = pem.buf;
+
+ ret = mbedtls_pk_parse_subpubkey( &p, p + pem.buflen, ctx );
+ mbedtls_pem_free( &pem );
+ return( ret );
}
else if( ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT )
{
mbedtls_pem_free( &pem );
return( ret );
}
+ mbedtls_pem_free( &pem );
#endif /* MBEDTLS_PEM_PARSE_C */
+
+#if defined(MBEDTLS_RSA_C)
+ if( ( pk_info = mbedtls_pk_info_from_type( MBEDTLS_PK_RSA ) ) == NULL )
+ return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
+
+ if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 )
+ return( ret );
+
+ p = (unsigned char *)key;
+ ret = pk_get_rsapubkey( &p, p + keylen, mbedtls_pk_rsa( *ctx ) );
+ if( ret == 0 )
+ {
+ return( ret );
+ }
+ mbedtls_pk_free( ctx );
+ if( ret != ( MBEDTLS_ERR_PK_INVALID_PUBKEY + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ) )
+ {
+ return( ret );
+ }
+#endif /* MBEDTLS_RSA_C */
p = (unsigned char *) key;
ret = mbedtls_pk_parse_subpubkey( &p, p + keylen, ctx );
-#if defined(MBEDTLS_PEM_PARSE_C)
- mbedtls_pem_free( &pem );
-#endif
-
return( ret );
}
diff --git a/thirdparty/mbedtls/library/platform.c b/thirdparty/mbedtls/library/platform.c
index 76df7fac18..a295f9b9af 100644
--- a/thirdparty/mbedtls/library/platform.c
+++ b/thirdparty/mbedtls/library/platform.c
@@ -82,7 +82,7 @@ int mbedtls_platform_win32_snprintf( char *s, size_t n, const char *fmt, ... )
return( -1 );
va_start( argp, fmt );
-#if defined(_TRUNCATE)
+#if defined(_TRUNCATE) && !defined(__MINGW32__)
ret = _vsnprintf_s( s, n, _TRUNCATE, fmt, argp );
#else
ret = _vsnprintf( s, n, fmt, argp );
diff --git a/thirdparty/mbedtls/library/ripemd160.c b/thirdparty/mbedtls/library/ripemd160.c
index b85b117c6a..2ba48b7fdb 100644
--- a/thirdparty/mbedtls/library/ripemd160.c
+++ b/thirdparty/mbedtls/library/ripemd160.c
@@ -112,6 +112,13 @@ int mbedtls_ripemd160_starts_ret( mbedtls_ripemd160_context *ctx )
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_ripemd160_starts( mbedtls_ripemd160_context *ctx )
+{
+ mbedtls_ripemd160_starts_ret( ctx );
+}
+#endif
+
#if !defined(MBEDTLS_RIPEMD160_PROCESS_ALT)
/*
* Process one block
@@ -295,6 +302,14 @@ int mbedtls_internal_ripemd160_process( mbedtls_ripemd160_context *ctx,
return( 0 );
}
+
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_ripemd160_process( mbedtls_ripemd160_context *ctx,
+ const unsigned char data[64] )
+{
+ mbedtls_internal_ripemd160_process( ctx, data );
+}
+#endif
#endif /* !MBEDTLS_RIPEMD160_PROCESS_ALT */
/*
@@ -349,6 +364,15 @@ int mbedtls_ripemd160_update_ret( mbedtls_ripemd160_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_ripemd160_update( mbedtls_ripemd160_context *ctx,
+ const unsigned char *input,
+ size_t ilen )
+{
+ mbedtls_ripemd160_update_ret( ctx, input, ilen );
+}
+#endif
+
static const unsigned char ripemd160_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -395,6 +419,14 @@ int mbedtls_ripemd160_finish_ret( mbedtls_ripemd160_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_ripemd160_finish( mbedtls_ripemd160_context *ctx,
+ unsigned char output[20] )
+{
+ mbedtls_ripemd160_finish_ret( ctx, output );
+}
+#endif
+
#endif /* ! MBEDTLS_RIPEMD160_ALT */
/*
@@ -424,6 +456,15 @@ exit:
return( ret );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_ripemd160( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[20] )
+{
+ mbedtls_ripemd160_ret( input, ilen, output );
+}
+#endif
+
#if defined(MBEDTLS_SELF_TEST)
/*
* Test vectors from the RIPEMD-160 paper and
diff --git a/thirdparty/mbedtls/library/rsa.c b/thirdparty/mbedtls/library/rsa.c
index 6526978e26..c9f7ba91b6 100644
--- a/thirdparty/mbedtls/library/rsa.c
+++ b/thirdparty/mbedtls/library/rsa.c
@@ -773,16 +773,38 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
{
int ret;
size_t olen;
- mbedtls_mpi T, T1, T2;
+
+ /* Temporary holding the result */
+ mbedtls_mpi T;
+
+ /* Temporaries holding P-1, Q-1 and the
+ * exponent blinding factor, respectively. */
mbedtls_mpi P1, Q1, R;
-#if defined(MBEDTLS_RSA_NO_CRT)
- mbedtls_mpi D_blind;
- mbedtls_mpi *D = &ctx->D;
-#else
+
+#if !defined(MBEDTLS_RSA_NO_CRT)
+ /* Temporaries holding the results mod p resp. mod q. */
+ mbedtls_mpi TP, TQ;
+
+ /* Temporaries holding the blinded exponents for
+ * the mod p resp. mod q computation (if used). */
mbedtls_mpi DP_blind, DQ_blind;
+
+ /* Pointers to actual exponents to be used - either the unblinded
+ * or the blinded ones, depending on the presence of a PRNG. */
mbedtls_mpi *DP = &ctx->DP;
mbedtls_mpi *DQ = &ctx->DQ;
-#endif
+#else
+ /* Temporary holding the blinded exponent (if used). */
+ mbedtls_mpi D_blind;
+
+ /* Pointer to actual exponent to be used - either the unblinded
+ * or the blinded one, depending on the presence of a PRNG. */
+ mbedtls_mpi *D = &ctx->D;
+#endif /* MBEDTLS_RSA_NO_CRT */
+
+ /* Temporaries holding the initial input and the double
+ * checked result; should be the same in the end. */
+ mbedtls_mpi I, C;
if( rsa_check_context( ctx, 1 /* private key checks */,
f_rng != NULL /* blinding y/n */ ) != 0 )
@@ -790,8 +812,17 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
return( MBEDTLS_ERR_RSA_BAD_INPUT_DATA );
}
- mbedtls_mpi_init( &T ); mbedtls_mpi_init( &T1 ); mbedtls_mpi_init( &T2 );
- mbedtls_mpi_init( &P1 ); mbedtls_mpi_init( &Q1 ); mbedtls_mpi_init( &R );
+#if defined(MBEDTLS_THREADING_C)
+ if( ( ret = mbedtls_mutex_lock( &ctx->mutex ) ) != 0 )
+ return( ret );
+#endif
+
+ /* MPI Initialization */
+ mbedtls_mpi_init( &T );
+
+ mbedtls_mpi_init( &P1 );
+ mbedtls_mpi_init( &Q1 );
+ mbedtls_mpi_init( &R );
if( f_rng != NULL )
{
@@ -803,12 +834,15 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
#endif
}
-
-#if defined(MBEDTLS_THREADING_C)
- if( ( ret = mbedtls_mutex_lock( &ctx->mutex ) ) != 0 )
- return( ret );
+#if !defined(MBEDTLS_RSA_NO_CRT)
+ mbedtls_mpi_init( &TP ); mbedtls_mpi_init( &TQ );
#endif
+ mbedtls_mpi_init( &I );
+ mbedtls_mpi_init( &C );
+
+ /* End of MPI initialization */
+
MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( &T, input, ctx->len ) );
if( mbedtls_mpi_cmp_mpi( &T, &ctx->N ) >= 0 )
{
@@ -816,6 +850,8 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
goto cleanup;
}
+ MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &I, &T ) );
+
if( f_rng != NULL )
{
/*
@@ -874,24 +910,25 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
/*
* Faster decryption using the CRT
*
- * T1 = input ^ dP mod P
- * T2 = input ^ dQ mod Q
+ * TP = input ^ dP mod P
+ * TQ = input ^ dQ mod Q
*/
- MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &T1, &T, DP, &ctx->P, &ctx->RP ) );
- MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &T2, &T, DQ, &ctx->Q, &ctx->RQ ) );
+
+ MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &TP, &T, DP, &ctx->P, &ctx->RP ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &TQ, &T, DQ, &ctx->Q, &ctx->RQ ) );
/*
- * T = (T1 - T2) * (Q^-1 mod P) mod P
+ * T = (TP - TQ) * (Q^-1 mod P) mod P
*/
- MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &T, &T1, &T2 ) );
- MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &T1, &T, &ctx->QP ) );
- MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &T, &T1, &ctx->P ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &T, &TP, &TQ ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &TP, &T, &ctx->QP ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &T, &TP, &ctx->P ) );
/*
- * T = T2 + T * Q
+ * T = TQ + T * Q
*/
- MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &T1, &T, &ctx->Q ) );
- MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &T, &T2, &T1 ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &TP, &T, &ctx->Q ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &T, &TQ, &TP ) );
#endif /* MBEDTLS_RSA_NO_CRT */
if( f_rng != NULL )
@@ -904,6 +941,15 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &T, &T, &ctx->N ) );
}
+ /* Verify the result to prevent glitching attacks. */
+ MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &C, &T, &ctx->E,
+ &ctx->N, &ctx->RN ) );
+ if( mbedtls_mpi_cmp_mpi( &C, &I ) != 0 )
+ {
+ ret = MBEDTLS_ERR_RSA_VERIFY_FAILED;
+ goto cleanup;
+ }
+
olen = ctx->len;
MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary( &T, output, olen ) );
@@ -913,8 +959,9 @@ cleanup:
return( MBEDTLS_ERR_THREADING_MUTEX_ERROR );
#endif
- mbedtls_mpi_free( &T ); mbedtls_mpi_free( &T1 ); mbedtls_mpi_free( &T2 );
- mbedtls_mpi_free( &P1 ); mbedtls_mpi_free( &Q1 ); mbedtls_mpi_free( &R );
+ mbedtls_mpi_free( &P1 );
+ mbedtls_mpi_free( &Q1 );
+ mbedtls_mpi_free( &R );
if( f_rng != NULL )
{
@@ -926,6 +973,15 @@ cleanup:
#endif
}
+ mbedtls_mpi_free( &T );
+
+#if !defined(MBEDTLS_RSA_NO_CRT)
+ mbedtls_mpi_free( &TP ); mbedtls_mpi_free( &TQ );
+#endif
+
+ mbedtls_mpi_free( &C );
+ mbedtls_mpi_free( &I );
+
if( ret != 0 )
return( MBEDTLS_ERR_RSA_PRIVATE_FAILED + ret );
@@ -2222,7 +2278,8 @@ int mbedtls_rsa_self_test( int verbose )
if( verbose != 0 )
mbedtls_printf( "failed\n" );
- return( 1 );
+ ret = 1;
+ goto cleanup;
}
if( verbose != 0 )
@@ -2237,7 +2294,8 @@ int mbedtls_rsa_self_test( int verbose )
if( verbose != 0 )
mbedtls_printf( "failed\n" );
- return( 1 );
+ ret = 1;
+ goto cleanup;
}
if( verbose != 0 )
@@ -2250,7 +2308,8 @@ int mbedtls_rsa_self_test( int verbose )
if( verbose != 0 )
mbedtls_printf( "failed\n" );
- return( 1 );
+ ret = 1;
+ goto cleanup;
}
if( memcmp( rsa_decrypted, rsa_plaintext, len ) != 0 )
@@ -2258,7 +2317,8 @@ int mbedtls_rsa_self_test( int verbose )
if( verbose != 0 )
mbedtls_printf( "failed\n" );
- return( 1 );
+ ret = 1;
+ goto cleanup;
}
if( verbose != 0 )
@@ -2283,7 +2343,8 @@ int mbedtls_rsa_self_test( int verbose )
if( verbose != 0 )
mbedtls_printf( "failed\n" );
- return( 1 );
+ ret = 1;
+ goto cleanup;
}
if( verbose != 0 )
@@ -2296,7 +2357,8 @@ int mbedtls_rsa_self_test( int verbose )
if( verbose != 0 )
mbedtls_printf( "failed\n" );
- return( 1 );
+ ret = 1;
+ goto cleanup;
}
if( verbose != 0 )
diff --git a/thirdparty/mbedtls/library/sha1.c b/thirdparty/mbedtls/library/sha1.c
index 8432eba8bd..1f29a0fbf8 100644
--- a/thirdparty/mbedtls/library/sha1.c
+++ b/thirdparty/mbedtls/library/sha1.c
@@ -111,6 +111,13 @@ int mbedtls_sha1_starts_ret( mbedtls_sha1_context *ctx )
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha1_starts( mbedtls_sha1_context *ctx )
+{
+ mbedtls_sha1_starts_ret( ctx );
+}
+#endif
+
#if !defined(MBEDTLS_SHA1_PROCESS_ALT)
int mbedtls_internal_sha1_process( mbedtls_sha1_context *ctx,
const unsigned char data[64] )
@@ -270,6 +277,14 @@ int mbedtls_internal_sha1_process( mbedtls_sha1_context *ctx,
return( 0 );
}
+
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha1_process( mbedtls_sha1_context *ctx,
+ const unsigned char data[64] )
+{
+ mbedtls_internal_sha1_process( ctx, data );
+}
+#endif
#endif /* !MBEDTLS_SHA1_PROCESS_ALT */
/*
@@ -322,6 +337,15 @@ int mbedtls_sha1_update_ret( mbedtls_sha1_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha1_update( mbedtls_sha1_context *ctx,
+ const unsigned char *input,
+ size_t ilen )
+{
+ mbedtls_sha1_update_ret( ctx, input, ilen );
+}
+#endif
+
static const unsigned char sha1_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -365,6 +389,14 @@ int mbedtls_sha1_finish_ret( mbedtls_sha1_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha1_finish( mbedtls_sha1_context *ctx,
+ unsigned char output[20] )
+{
+ mbedtls_sha1_finish_ret( ctx, output );
+}
+#endif
+
#endif /* !MBEDTLS_SHA1_ALT */
/*
@@ -394,6 +426,15 @@ exit:
return( ret );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha1( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[20] )
+{
+ mbedtls_sha1_ret( input, ilen, output );
+}
+#endif
+
#if defined(MBEDTLS_SELF_TEST)
/*
* FIPS-180-1 test vectors
diff --git a/thirdparty/mbedtls/library/sha256.c b/thirdparty/mbedtls/library/sha256.c
index abcd64d134..f39bcbab6c 100644
--- a/thirdparty/mbedtls/library/sha256.c
+++ b/thirdparty/mbedtls/library/sha256.c
@@ -135,6 +135,14 @@ int mbedtls_sha256_starts_ret( mbedtls_sha256_context *ctx, int is224 )
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha256_starts( mbedtls_sha256_context *ctx,
+ int is224 )
+{
+ mbedtls_sha256_starts_ret( ctx, is224 );
+}
+#endif
+
#if !defined(MBEDTLS_SHA256_PROCESS_ALT)
static const uint32_t K[] =
{
@@ -238,6 +246,14 @@ int mbedtls_internal_sha256_process( mbedtls_sha256_context *ctx,
return( 0 );
}
+
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha256_process( mbedtls_sha256_context *ctx,
+ const unsigned char data[64] )
+{
+ mbedtls_internal_sha256_process( ctx, data );
+}
+#endif
#endif /* !MBEDTLS_SHA256_PROCESS_ALT */
/*
@@ -290,6 +306,15 @@ int mbedtls_sha256_update_ret( mbedtls_sha256_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha256_update( mbedtls_sha256_context *ctx,
+ const unsigned char *input,
+ size_t ilen )
+{
+ mbedtls_sha256_update_ret( ctx, input, ilen );
+}
+#endif
+
static const unsigned char sha256_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -339,6 +364,14 @@ int mbedtls_sha256_finish_ret( mbedtls_sha256_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha256_finish( mbedtls_sha256_context *ctx,
+ unsigned char output[32] )
+{
+ mbedtls_sha256_finish_ret( ctx, output );
+}
+#endif
+
#endif /* !MBEDTLS_SHA256_ALT */
/*
@@ -369,6 +402,16 @@ exit:
return( ret );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha256( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[32],
+ int is224 )
+{
+ mbedtls_sha256_ret( input, ilen, output, is224 );
+}
+#endif
+
#if defined(MBEDTLS_SELF_TEST)
/*
* FIPS-180-2 test vectors
diff --git a/thirdparty/mbedtls/library/sha512.c b/thirdparty/mbedtls/library/sha512.c
index c99b6da950..97cee07c56 100644
--- a/thirdparty/mbedtls/library/sha512.c
+++ b/thirdparty/mbedtls/library/sha512.c
@@ -149,6 +149,14 @@ int mbedtls_sha512_starts_ret( mbedtls_sha512_context *ctx, int is384 )
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha512_starts( mbedtls_sha512_context *ctx,
+ int is384 )
+{
+ mbedtls_sha512_starts_ret( ctx, is384 );
+}
+#endif
+
#if !defined(MBEDTLS_SHA512_PROCESS_ALT)
/*
@@ -269,6 +277,14 @@ int mbedtls_internal_sha512_process( mbedtls_sha512_context *ctx,
return( 0 );
}
+
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha512_process( mbedtls_sha512_context *ctx,
+ const unsigned char data[128] )
+{
+ mbedtls_internal_sha512_process( ctx, data );
+}
+#endif
#endif /* !MBEDTLS_SHA512_PROCESS_ALT */
/*
@@ -320,6 +336,15 @@ int mbedtls_sha512_update_ret( mbedtls_sha512_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha512_update( mbedtls_sha512_context *ctx,
+ const unsigned char *input,
+ size_t ilen )
+{
+ mbedtls_sha512_update_ret( ctx, input, ilen );
+}
+#endif
+
static const unsigned char sha512_padding[128] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -375,6 +400,14 @@ int mbedtls_sha512_finish_ret( mbedtls_sha512_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha512_finish( mbedtls_sha512_context *ctx,
+ unsigned char output[64] )
+{
+ mbedtls_sha512_finish_ret( ctx, output );
+}
+#endif
+
#endif /* !MBEDTLS_SHA512_ALT */
/*
@@ -405,6 +438,16 @@ exit:
return( ret );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha512( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[64],
+ int is384 )
+{
+ mbedtls_sha512_ret( input, ilen, output, is384 );
+}
+#endif
+
#if defined(MBEDTLS_SELF_TEST)
/*
diff --git a/thirdparty/mbedtls/library/ssl_cli.c b/thirdparty/mbedtls/library/ssl_cli.c
index 2534346a49..88864b8136 100644
--- a/thirdparty/mbedtls/library/ssl_cli.c
+++ b/thirdparty/mbedtls/library/ssl_cli.c
@@ -902,6 +902,8 @@ static int ssl_write_client_hello( mbedtls_ssl_context *ssl )
*p++ = (unsigned char)( ciphersuites[i] );
}
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "client hello, got %d ciphersuites (excluding SCSVs)", n ) );
+
/*
* Add TLS_EMPTY_RENEGOTIATION_INFO_SCSV
*/
@@ -909,6 +911,7 @@ static int ssl_write_client_hello( mbedtls_ssl_context *ssl )
if( ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE )
#endif
{
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "adding EMPTY_RENEGOTIATION_INFO_SCSV" ) );
*p++ = (unsigned char)( MBEDTLS_SSL_EMPTY_RENEGOTIATION_INFO >> 8 );
*p++ = (unsigned char)( MBEDTLS_SSL_EMPTY_RENEGOTIATION_INFO );
n++;
@@ -928,8 +931,6 @@ static int ssl_write_client_hello( mbedtls_ssl_context *ssl )
*q++ = (unsigned char)( n >> 7 );
*q++ = (unsigned char)( n << 1 );
- MBEDTLS_SSL_DEBUG_MSG( 3, ( "client hello, got %d ciphersuites", n ) );
-
#if defined(MBEDTLS_ZLIB_SUPPORT)
offer_compress = 1;
#else
@@ -2057,10 +2058,16 @@ static int ssl_parse_server_psk_hint( mbedtls_ssl_context *ssl,
*
* opaque psk_identity_hint<0..2^16-1>;
*/
+ if( (*p) > end - 2 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server key exchange message "
+ "(psk_identity_hint length)" ) );
+ return( MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE );
+ }
len = (*p)[0] << 8 | (*p)[1];
*p += 2;
- if( (*p) + len > end )
+ if( (*p) > end - len )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server key exchange message "
"(psk_identity_hint length)" ) );
@@ -2478,10 +2485,18 @@ static int ssl_parse_server_key_exchange( mbedtls_ssl_context *ssl )
/*
* Read signature
*/
+
+ if( p > end - 2 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) );
+ mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
+ MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR );
+ return( MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE );
+ }
sig_len = ( p[0] << 8 ) | p[1];
p += 2;
- if( end != p + sig_len )
+ if( p != end - sig_len )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) );
mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
diff --git a/thirdparty/mbedtls/library/ssl_tls.c b/thirdparty/mbedtls/library/ssl_tls.c
index 617dedb1b0..236e52d767 100644
--- a/thirdparty/mbedtls/library/ssl_tls.c
+++ b/thirdparty/mbedtls/library/ssl_tls.c
@@ -501,6 +501,7 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
unsigned char *key2;
unsigned char *mac_enc;
unsigned char *mac_dec;
+ size_t mac_key_len;
size_t iv_copy_len;
const mbedtls_cipher_info_t *cipher_info;
const mbedtls_md_info_t *md_info;
@@ -692,6 +693,7 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
cipher_info->mode == MBEDTLS_MODE_CCM )
{
transform->maclen = 0;
+ mac_key_len = 0;
transform->ivlen = 12;
transform->fixed_ivlen = 4;
@@ -712,7 +714,8 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
}
/* Get MAC length */
- transform->maclen = mbedtls_md_get_size( md_info );
+ mac_key_len = mbedtls_md_get_size( md_info );
+ transform->maclen = mac_key_len;
#if defined(MBEDTLS_SSL_TRUNCATED_HMAC)
/*
@@ -721,7 +724,16 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
* so we only need to adjust the length here.
*/
if( session->trunc_hmac == MBEDTLS_SSL_TRUNC_HMAC_ENABLED )
+ {
transform->maclen = MBEDTLS_SSL_TRUNCATED_HMAC_LEN;
+
+#if defined(MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT)
+ /* Fall back to old, non-compliant version of the truncated
+ * HMAC implementation which also truncates the key
+ * (Mbed TLS versions from 1.3 to 2.6.0) */
+ mac_key_len = transform->maclen;
+#endif
+ }
#endif /* MBEDTLS_SSL_TRUNCATED_HMAC */
/* IV length */
@@ -783,11 +795,11 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
#if defined(MBEDTLS_SSL_CLI_C)
if( ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT )
{
- key1 = keyblk + transform->maclen * 2;
- key2 = keyblk + transform->maclen * 2 + transform->keylen;
+ key1 = keyblk + mac_key_len * 2;
+ key2 = keyblk + mac_key_len * 2 + transform->keylen;
mac_enc = keyblk;
- mac_dec = keyblk + transform->maclen;
+ mac_dec = keyblk + mac_key_len;
/*
* This is not used in TLS v1.1.
@@ -803,10 +815,10 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
#if defined(MBEDTLS_SSL_SRV_C)
if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER )
{
- key1 = keyblk + transform->maclen * 2 + transform->keylen;
- key2 = keyblk + transform->maclen * 2;
+ key1 = keyblk + mac_key_len * 2 + transform->keylen;
+ key2 = keyblk + mac_key_len * 2;
- mac_enc = keyblk + transform->maclen;
+ mac_enc = keyblk + mac_key_len;
mac_dec = keyblk;
/*
@@ -828,14 +840,14 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
#if defined(MBEDTLS_SSL_PROTO_SSL3)
if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 )
{
- if( transform->maclen > sizeof transform->mac_enc )
+ if( mac_key_len > sizeof transform->mac_enc )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
- memcpy( transform->mac_enc, mac_enc, transform->maclen );
- memcpy( transform->mac_dec, mac_dec, transform->maclen );
+ memcpy( transform->mac_enc, mac_enc, mac_key_len );
+ memcpy( transform->mac_dec, mac_dec, mac_key_len );
}
else
#endif /* MBEDTLS_SSL_PROTO_SSL3 */
@@ -843,8 +855,8 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
defined(MBEDTLS_SSL_PROTO_TLS1_2)
if( ssl->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_1 )
{
- mbedtls_md_hmac_starts( &transform->md_ctx_enc, mac_enc, transform->maclen );
- mbedtls_md_hmac_starts( &transform->md_ctx_dec, mac_dec, transform->maclen );
+ mbedtls_md_hmac_starts( &transform->md_ctx_enc, mac_enc, mac_key_len );
+ mbedtls_md_hmac_starts( &transform->md_ctx_dec, mac_dec, mac_key_len );
}
else
#endif
@@ -864,7 +876,7 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
transform->iv_enc, transform->iv_dec,
iv_copy_len,
mac_enc, mac_dec,
- transform->maclen ) ) != 0 )
+ mac_key_len ) ) != 0 )
{
MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_hw_record_init", ret );
return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
@@ -877,7 +889,7 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
{
ssl->conf->f_export_keys( ssl->conf->p_export_keys,
session->master, keyblk,
- transform->maclen, transform->keylen,
+ mac_key_len, transform->keylen,
iv_copy_len );
}
#endif
@@ -7673,8 +7685,14 @@ int mbedtls_ssl_config_defaults( mbedtls_ssl_config *conf,
* Default
*/
default:
- conf->min_major_ver = MBEDTLS_SSL_MAJOR_VERSION_3;
- conf->min_minor_ver = MBEDTLS_SSL_MINOR_VERSION_1; /* TLS 1.0 */
+ conf->min_major_ver = ( MBEDTLS_SSL_MIN_MAJOR_VERSION >
+ MBEDTLS_SSL_MIN_VALID_MAJOR_VERSION ) ?
+ MBEDTLS_SSL_MIN_MAJOR_VERSION :
+ MBEDTLS_SSL_MIN_VALID_MAJOR_VERSION;
+ conf->min_minor_ver = ( MBEDTLS_SSL_MIN_MINOR_VERSION >
+ MBEDTLS_SSL_MIN_VALID_MINOR_VERSION ) ?
+ MBEDTLS_SSL_MIN_MINOR_VERSION :
+ MBEDTLS_SSL_MIN_VALID_MINOR_VERSION;
conf->max_major_ver = MBEDTLS_SSL_MAX_MAJOR_VERSION;
conf->max_minor_ver = MBEDTLS_SSL_MAX_MINOR_VERSION;
diff --git a/thirdparty/mbedtls/library/version.c b/thirdparty/mbedtls/library/version.c
index 6ca80d4695..fd96750885 100644
--- a/thirdparty/mbedtls/library/version.c
+++ b/thirdparty/mbedtls/library/version.c
@@ -30,7 +30,7 @@
#include "mbedtls/version.h"
#include <string.h>
-unsigned int mbedtls_version_get_number()
+unsigned int mbedtls_version_get_number( void )
{
return( MBEDTLS_VERSION_NUMBER );
}
diff --git a/thirdparty/mbedtls/library/version_features.c b/thirdparty/mbedtls/library/version_features.c
index 72afec2da0..da47e3d753 100644
--- a/thirdparty/mbedtls/library/version_features.c
+++ b/thirdparty/mbedtls/library/version_features.c
@@ -468,6 +468,9 @@ static const char *features[] = {
#if defined(MBEDTLS_SSL_TRUNCATED_HMAC)
"MBEDTLS_SSL_TRUNCATED_HMAC",
#endif /* MBEDTLS_SSL_TRUNCATED_HMAC */
+#if defined(MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT)
+ "MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT",
+#endif /* MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT */
#if defined(MBEDTLS_THREADING_ALT)
"MBEDTLS_THREADING_ALT",
#endif /* MBEDTLS_THREADING_ALT */
diff --git a/thirdparty/mbedtls/library/x509_crl.c b/thirdparty/mbedtls/library/x509_crl.c
index 55d12acd03..b0f39d428b 100644
--- a/thirdparty/mbedtls/library/x509_crl.c
+++ b/thirdparty/mbedtls/library/x509_crl.c
@@ -95,17 +95,23 @@ static int x509_crl_get_version( unsigned char **p,
}
/*
- * X.509 CRL v2 extensions (no extensions parsed yet.)
+ * X.509 CRL v2 extensions
+ *
+ * We currently don't parse any extension's content, but we do check that the
+ * list of extensions is well-formed and abort on critical extensions (that
+ * are unsupported as we don't support any extension so far)
*/
static int x509_get_crl_ext( unsigned char **p,
const unsigned char *end,
mbedtls_x509_buf *ext )
{
int ret;
- size_t len = 0;
- /* Get explicit tag */
- if( ( ret = mbedtls_x509_get_ext( p, end, ext, 0) ) != 0 )
+ /*
+ * crlExtensions [0] EXPLICIT Extensions OPTIONAL
+ * -- if present, version MUST be v2
+ */
+ if( ( ret = mbedtls_x509_get_ext( p, end, ext, 0 ) ) != 0 )
{
if( ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG )
return( 0 );
@@ -115,11 +121,54 @@ static int x509_get_crl_ext( unsigned char **p,
while( *p < end )
{
+ /*
+ * Extension ::= SEQUENCE {
+ * extnID OBJECT IDENTIFIER,
+ * critical BOOLEAN DEFAULT FALSE,
+ * extnValue OCTET STRING }
+ */
+ int is_critical = 0;
+ const unsigned char *end_ext_data;
+ size_t len;
+
+ /* Get enclosing sequence tag */
if( ( ret = mbedtls_asn1_get_tag( p, end, &len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
+ end_ext_data = *p + len;
+
+ /* Get OID (currently ignored) */
+ if( ( ret = mbedtls_asn1_get_tag( p, end_ext_data, &len,
+ MBEDTLS_ASN1_OID ) ) != 0 )
+ {
+ return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
+ }
+ *p += len;
+
+ /* Get optional critical */
+ if( ( ret = mbedtls_asn1_get_bool( p, end_ext_data,
+ &is_critical ) ) != 0 &&
+ ( ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ) )
+ {
+ return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
+ }
+
+ /* Data should be octet string type */
+ if( ( ret = mbedtls_asn1_get_tag( p, end_ext_data, &len,
+ MBEDTLS_ASN1_OCTET_STRING ) ) != 0 )
+ return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
+
+ /* Ignore data so far and just check its length */
*p += len;
+ if( *p != end_ext_data )
+ return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
+ MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
+
+ /* Abort on (unsupported) critical extensions */
+ if( is_critical )
+ return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
+ MBEDTLS_ERR_ASN1_UNEXPECTED_TAG );
}
if( *p != end )
@@ -257,7 +306,7 @@ int mbedtls_x509_crl_parse_der( mbedtls_x509_crl *chain,
{
int ret;
size_t len;
- unsigned char *p, *end;
+ unsigned char *p = NULL, *end = NULL;
mbedtls_x509_buf sig_params1, sig_params2, sig_oid2;
mbedtls_x509_crl *crl = chain;
@@ -294,7 +343,11 @@ int mbedtls_x509_crl_parse_der( mbedtls_x509_crl *chain,
/*
* Copy raw DER-encoded CRL
*/
- if( ( p = mbedtls_calloc( 1, buflen ) ) == NULL )
+ if( buflen == 0 )
+ return( MBEDTLS_ERR_X509_INVALID_FORMAT );
+
+ p = mbedtls_calloc( 1, buflen );
+ if( p == NULL )
return( MBEDTLS_ERR_X509_ALLOC_FAILED );
memcpy( p, buf, buflen );
diff --git a/thirdparty/mbedtls/library/x509_crt.c b/thirdparty/mbedtls/library/x509_crt.c
index 6c592fdb5a..2a5dbb8783 100644
--- a/thirdparty/mbedtls/library/x509_crt.c
+++ b/thirdparty/mbedtls/library/x509_crt.c
@@ -62,16 +62,17 @@
#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
#include <windows.h>
-#if _MSC_VER <= 1600
-/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and <intsafe.h> are included, as they
- * redefine a number of <TYPE>_MAX constants. These constants are guaranteed to be the same, though, so
- * we suppress the warning when including intsafe.h.
+#if defined(_MSC_VER) && _MSC_VER <= 1600
+/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and
+ * <intsafe.h> are included, as they redefine a number of <TYPE>_MAX constants.
+ * These constants are guaranteed to be the same, though, so we suppress the
+ * warning when including intsafe.h.
*/
#pragma warning( push )
#pragma warning( disable : 4005 )
#endif
#include <intsafe.h>
-#if _MSC_VER <= 1600
+#if defined(_MSC_VER) && _MSC_VER <= 1600
#pragma warning( pop )
#endif
#else
@@ -145,7 +146,8 @@ const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_suiteb =
MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) |
MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
/* Only ECDSA */
- MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ),
+ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
+ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
#if defined(MBEDTLS_ECP_C)
/* Only NIST P-256 and P-384 */
MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP256R1 ) |
@@ -484,9 +486,12 @@ static int x509_get_subject_alt_name( unsigned char **p,
if( ( ret = mbedtls_asn1_get_len( p, end, &tag_len ) ) != 0 )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
- if( ( tag & MBEDTLS_ASN1_CONTEXT_SPECIFIC ) != MBEDTLS_ASN1_CONTEXT_SPECIFIC )
+ if( ( tag & MBEDTLS_ASN1_TAG_CLASS_MASK ) !=
+ MBEDTLS_ASN1_CONTEXT_SPECIFIC )
+ {
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
MBEDTLS_ERR_ASN1_UNEXPECTED_TAG );
+ }
/* Skip everything but DNS name */
if( tag != ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | 2 ) )
@@ -1140,6 +1145,14 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
if ( FAILED ( SizeTToInt( len, &lengthAsInt ) ) )
return( MBEDTLS_ERR_X509_FILE_IO_ERROR );
+ /*
+ * Note this function uses the code page CP_ACP, and assumes the incoming
+ * string is encoded in ANSI, before translating it into Unicode. If the
+ * incoming string were changed to be UTF-8, then the length check needs to
+ * change to check the number of characters, not the number of bytes, in the
+ * incoming string are less than MAX_PATH to avoid a buffer overrun with
+ * MultiByteToWideChar().
+ */
w_ret = MultiByteToWideChar( CP_ACP, 0, filename, lengthAsInt, szDir,
MAX_PATH - 3 );
if( w_ret == 0 )