summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.mailmap2
-rw-r--r--AUTHORS.md5
-rw-r--r--DONORS.md122
-rw-r--r--SConstruct304
-rw-r--r--core/config/engine.cpp16
-rw-r--r--core/config/engine.h7
-rw-r--r--core/config/project_settings.cpp1
-rw-r--r--core/core_bind.cpp5
-rw-r--r--core/core_bind.h1
-rw-r--r--core/io/dir_access.cpp2
-rw-r--r--core/io/marshalls.cpp49
-rw-r--r--core/io/stream_peer_tcp.cpp1
-rw-r--r--core/object/worker_thread_pool.cpp156
-rw-r--r--core/object/worker_thread_pool.h19
-rw-r--r--core/os/os.h1
-rw-r--r--core/templates/command_queue_mt.cpp8
-rw-r--r--core/templates/command_queue_mt.h43
-rw-r--r--core/variant/array.h2
-rw-r--r--doc/classes/BoneAttachment3D.xml5
-rw-r--r--doc/classes/Control.xml2
-rw-r--r--doc/classes/EditorSettings.xml13
-rw-r--r--doc/classes/FileAccess.xml2
-rw-r--r--doc/classes/NavigationRegion2D.xml21
-rw-r--r--doc/classes/Node.xml4
-rw-r--r--doc/classes/OS.xml13
-rw-r--r--doc/classes/PhysicsServer2DExtension.xml4
-rw-r--r--doc/classes/Popup.xml5
-rw-r--r--doc/classes/PopupMenu.xml3
-rw-r--r--doc/classes/PopupPanel.xml5
-rw-r--r--doc/classes/ProjectSettings.xml10
-rw-r--r--doc/classes/ScriptEditor.xml29
-rw-r--r--doc/classes/Skeleton3D.xml20
-rw-r--r--drivers/egl/egl_manager.cpp15
-rw-r--r--drivers/egl/egl_manager.h1
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp58
-rw-r--r--drivers/gles3/storage/particles_storage.cpp6
-rw-r--r--drivers/unix/dir_access_unix.cpp4
-rw-r--r--drivers/unix/os_unix.cpp43
-rw-r--r--drivers/unix/os_unix.h8
-rw-r--r--editor/code_editor.cpp32
-rw-r--r--editor/code_editor.h4
-rw-r--r--editor/create_dialog.cpp9
-rw-r--r--editor/editor_file_system.cpp12
-rw-r--r--editor/editor_help.cpp3
-rw-r--r--editor/editor_inspector.cpp22
-rw-r--r--editor/editor_inspector.h7
-rw-r--r--editor/editor_node.cpp73
-rw-r--r--editor/editor_node.h3
-rw-r--r--editor/editor_settings.cpp12
-rw-r--r--editor/engine_update_label.cpp344
-rw-r--r--editor/engine_update_label.h107
-rw-r--r--editor/export/editor_export.cpp8
-rw-r--r--editor/filesystem_dock.cpp5
-rw-r--r--editor/filesystem_dock.h1
-rw-r--r--editor/gui/editor_bottom_panel.cpp1
-rw-r--r--editor/gui/editor_file_dialog.cpp6
-rw-r--r--editor/gui/editor_scene_tabs.cpp12
-rw-r--r--editor/gui/editor_scene_tabs.h1
-rw-r--r--editor/import/3d/resource_importer_scene.cpp3
-rw-r--r--editor/import/3d/scene_import_settings.cpp22
-rw-r--r--editor/import/3d/scene_import_settings.h4
-rw-r--r--editor/multi_node_edit.cpp2
-rw-r--r--editor/multi_node_edit.h15
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp4
-rw-r--r--editor/plugins/animation_player_editor_plugin.h2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp65
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h2
-rw-r--r--editor/plugins/script_editor_plugin.cpp2
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp12
-rw-r--r--editor/plugins/tiles/tile_map_layer_editor.cpp8
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp3
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp12
-rw-r--r--editor/project_manager.cpp10
-rw-r--r--editor/scene_tree_dock.cpp36
-rw-r--r--editor/scene_tree_dock.h1
-rw-r--r--main/main.cpp27
-rw-r--r--main/performance.cpp4
-rw-r--r--methods.py4
-rw-r--r--misc/extension_api_validation/4.2-stable.expected24
-rwxr-xr-xmisc/scripts/copyright_headers.py2
-rw-r--r--modules/fbx/fbx_document.cpp22
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp14
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp3
-rw-r--r--modules/gdscript/gdscript_tokenizer_buffer.cpp6
-rw-r--r--modules/gdscript/gdscript_warning.cpp4
-rw-r--r--modules/gdscript/gdscript_warning.h2
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp23
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.out7
-rw-r--r--modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.gd12
-rw-r--r--modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/member_info.gd1
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs41
-rw-r--r--modules/navigation/2d/nav_mesh_generator_2d.cpp4
-rw-r--r--modules/navigation/3d/nav_mesh_generator_3d.cpp4
-rw-r--r--modules/navigation/3d/navigation_mesh_generator.cpp2
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.cpp1
-rw-r--r--modules/openxr/doc_classes/OpenXRHand.xml7
-rw-r--r--modules/openxr/scene/openxr_hand.cpp37
-rw-r--r--modules/openxr/scene/openxr_hand.h14
-rw-r--r--modules/svg/image_loader_svg.cpp12
-rw-r--r--platform/android/export/export_plugin.cpp24
-rw-r--r--platform/android/export/export_plugin.h2
-rw-r--r--platform/ios/os_ios.mm8
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.cpp8
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.h1
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp15
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h1
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11.cpp14
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11.h1
-rw-r--r--platform/macos/display_server_macos.h1
-rw-r--r--platform/macos/display_server_macos.mm18
-rw-r--r--platform/macos/gl_manager_macos_legacy.h1
-rw-r--r--platform/macos/gl_manager_macos_legacy.mm13
-rw-r--r--platform/macos/godot_open_save_delegate.mm8
-rw-r--r--platform/web/os_web.cpp4
-rw-r--r--platform/web/os_web.h1
-rwxr-xr-xplatform/web/serve.py13
-rw-r--r--platform/windows/detect.py11
-rw-r--r--platform/windows/display_server_windows.cpp17
-rw-r--r--platform/windows/display_server_windows.h1
-rw-r--r--platform/windows/gl_manager_windows_native.cpp24
-rw-r--r--platform/windows/gl_manager_windows_native.h4
-rw-r--r--platform/windows/os_windows.cpp41
-rw-r--r--platform/windows/os_windows.h4
-rw-r--r--scene/2d/navigation_region_2d.cpp160
-rw-r--r--scene/2d/navigation_region_2d.h17
-rw-r--r--scene/2d/parallax_2d.cpp7
-rw-r--r--scene/2d/physics/character_body_2d.cpp10
-rw-r--r--scene/2d/physics/character_body_2d.h1
-rw-r--r--scene/2d/physics/kinematic_collision_2d.cpp1
-rw-r--r--scene/2d/physics/kinematic_collision_2d.h2
-rw-r--r--scene/2d/physics/physics_body_2d.cpp10
-rw-r--r--scene/2d/physics/physics_body_2d.h2
-rw-r--r--scene/2d/tile_map.cpp5
-rw-r--r--scene/3d/bone_attachment_3d.compat.inc43
-rw-r--r--scene/3d/bone_attachment_3d.cpp13
-rw-r--r--scene/3d/bone_attachment_3d.h6
-rw-r--r--scene/3d/cpu_particles_3d.cpp4
-rw-r--r--scene/3d/navigation_region_3d.cpp2
-rw-r--r--scene/3d/physics/character_body_3d.cpp10
-rw-r--r--scene/3d/physics/character_body_3d.h1
-rw-r--r--scene/3d/physics/kinematic_collision_3d.cpp1
-rw-r--r--scene/3d/physics/kinematic_collision_3d.h2
-rw-r--r--scene/3d/physics/physics_body_3d.cpp10
-rw-r--r--scene/3d/physics/physics_body_3d.h2
-rw-r--r--scene/3d/physics/ray_cast_3d.cpp6
-rw-r--r--scene/3d/skeleton_3d.cpp6
-rw-r--r--scene/animation/animation_mixer.cpp14
-rw-r--r--scene/gui/control.cpp2
-rw-r--r--scene/gui/popup.cpp2
-rw-r--r--scene/gui/popup.h4
-rw-r--r--scene/gui/popup_menu.cpp6
-rw-r--r--scene/gui/rich_text_label.cpp4
-rw-r--r--scene/main/scene_tree.cpp11
-rw-r--r--scene/main/scene_tree.h3
-rw-r--r--scene/main/window.cpp5
-rw-r--r--scene/register_scene_types.cpp151
-rw-r--r--scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp (renamed from scene/resources/navigation_mesh_source_geometry_data_2d.cpp)0
-rw-r--r--scene/resources/2d/navigation_mesh_source_geometry_data_2d.h (renamed from scene/resources/navigation_mesh_source_geometry_data_2d.h)2
-rw-r--r--scene/resources/2d/navigation_polygon.cpp (renamed from scene/resources/navigation_polygon.cpp)0
-rw-r--r--scene/resources/2d/navigation_polygon.h (renamed from scene/resources/navigation_polygon.h)0
-rw-r--r--scene/resources/2d/tile_set.h2
-rw-r--r--scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp (renamed from scene/resources/navigation_mesh_source_geometry_data_3d.cpp)0
-rw-r--r--scene/resources/3d/navigation_mesh_source_geometry_data_3d.h (renamed from scene/resources/navigation_mesh_source_geometry_data_3d.h)0
-rw-r--r--scene/resources/particle_process_material.cpp4
-rw-r--r--scene/resources/resource_format_text.cpp2
-rw-r--r--scene/resources/world_2d.cpp8
-rw-r--r--scene/scene_string_names.cpp2
-rw-r--r--scene/scene_string_names.h2
-rw-r--r--servers/display_server.cpp6
-rw-r--r--servers/display_server.h1
-rw-r--r--servers/navigation_server_2d.h4
-rw-r--r--servers/navigation_server_3d.cpp2
-rw-r--r--servers/navigation_server_3d.h4
-rw-r--r--servers/navigation_server_3d_dummy.h2
-rw-r--r--servers/physics_2d/godot_shape_2d.cpp2
-rw-r--r--servers/physics_3d/godot_shape_3d.cpp2
-rw-r--r--servers/physics_server_2d_wrap_mt.cpp55
-rw-r--r--servers/physics_server_2d_wrap_mt.h38
-rw-r--r--servers/physics_server_3d_wrap_mt.cpp49
-rw-r--r--servers/physics_server_3d_wrap_mt.h38
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp2
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp1
-rw-r--r--servers/rendering/renderer_rd/storage_rd/particles_storage.cpp6
-rw-r--r--servers/rendering/rendering_server_default.cpp65
-rw-r--r--servers/rendering/rendering_server_default.h21
-rw-r--r--servers/rendering/shader_language.cpp22
-rw-r--r--servers/rendering/shader_warnings.cpp4
-rw-r--r--servers/rendering/shader_warnings.h2
-rw-r--r--servers/rendering_server.cpp9
-rw-r--r--servers/server_wrap_mt_common.h45
-rw-r--r--tests/core/templates/test_command_queue.h44
-rw-r--r--tests/core/threads/test_worker_thread_pool.h67
-rw-r--r--tests/core/variant/test_array.h4
-rw-r--r--tests/scene/test_navigation_region_2d.h2
-rw-r--r--tests/scene/test_navigation_region_3d.h11
-rw-r--r--tests/servers/test_navigation_server_3d.h3
-rw-r--r--tests/test_main.cpp2
-rw-r--r--thirdparty/mbedtls/include/godot_core_mbedtls_config.h7
202 files changed, 2204 insertions, 1275 deletions
diff --git a/.mailmap b/.mailmap
index 731f224bb6..9e1ddb95e1 100644
--- a/.mailmap
+++ b/.mailmap
@@ -70,6 +70,8 @@ J08nY <johny@neuromancer.sk> <jancar.jj@gmail.com>
J08nY <johny@neuromancer.sk> <J08nY@users.noreply.github.com>
Jake Young <young9003@gmail.com>
Jakub Grzesik <kubecz3k@gmail.com>
+Jakub Marcowski <chubercikbattle@gmail.com> <01158831@pw.edu.pl>
+Jakub Marcowski <chubercikbattle@gmail.com> <37378746+Chubercik@users.noreply.github.com>
janglee <merupatel123@gmail.com>
Jason Knight <00jknight@gmail.com> <jason@winterpixel.com>
Jean-Michel Bernard <jmb462@gmail.com>
diff --git a/AUTHORS.md b/AUTHORS.md
index 768eb4f904..f2fc58c1be 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -34,8 +34,10 @@ name is available.
Alex Drozd (brno32)
Alexey Khoroshavin (allkhor)
Alfred Reinold Baudisch (alfredbaudisch)
+ Alistair Leslie-Hughes (alesliehughes)
Alket Rexhepi (alketii)
Andrea Catania (AndreaCatania)
+ Andreia Gaita (shana)
Andrii Doroshenko (Xrayez)
Andy Maloney (asmaloney)
Andy Moss (MillionOstrich)
@@ -132,6 +134,7 @@ name is available.
J08nY
Jake Young (Duroxxigar)
Jakub Grzesik (kubecz3k)
+ Jakub Marcowski (Chubercik)
James Buck (jbuck3)
Jan Haller (Bromeon)
Jason Knight (jasonwinterpixel)
@@ -287,7 +290,9 @@ name is available.
Will Nations (willnationsdev)
Wilson E. Alvarez (Rubonnek)
Xavier Cho (mysticfall)
+ Yaohua Xiong (xiongyaohua)
yg2f (SuperUserNameMan)
+ Yordan Dolchinkov (Jordyfel)
Yuri Rubinsky (Chaosus)
Yuri Sizov (YuriSizov)
Zae Chao (zaevi)
diff --git a/DONORS.md b/DONORS.md
index b74232c461..f12f6f4b3a 100644
--- a/DONORS.md
+++ b/DONORS.md
@@ -12,40 +12,40 @@ generous deed immortalized in the next stable release of Godot Engine.
## Patrons
- OSS Capital <https://oss.capital>
- Re-Logic <https://re-logic.com>
+ OSS Capital <https://oss.capital/>
+ Re-Logic <https://re-logic.com/>
## Platinum sponsors
- Google Play <https://play.google.com>
- Heroic Labs <https://heroiclabs.com>
- Ramatak <https://ramatak.com>
+ Google Play <https://play.google.com/>
+ Heroic Labs <https://heroiclabs.com/>
+ Ramatak <https://ramatak.com/>
V-Sekai <https://github.com/V-Sekai>
- W4 Games <https://w4games.com>
+ W4 Games <https://w4games.com/>
## Gold sponsors
- Mega Crit <https://www.megacrit.com>
- Prehensile Tales <https://prehensile-tales.com>
- Robot Gentleman <http://robotgentleman.com>
+ Mega Crit <https://www.megacrit.com/>
+ Pirate Software <https://gopiratesoftware.com/>
+ Prehensile Tales <https://prehensile-tales.com/>
+ Robot Gentleman <http://robotgentleman.com/>
## Silver sponsors
- Affray Interactive <https://scp.games/pandemic>
- Broken Rules <https://brokenrul.es>
- Chasing Carrots <https://www.chasing-carrots.com>
- Delton Ding
- Gamblify <https://www.gamblify.com>
- Indoor Astronaut <https://indoorastronaut.ch>
- Null <https://null.com>
- Orbital Knight <https://www.orbitalknight.com>
- Playful Studios <https://playfulstudios.com>
+ Affray Interactive <https://scp.games/pandemic/>
+ Broken Rules <https://brokenrul.es/>
+ Chasing Carrots <https://www.chasing-carrots.com/>
+ Gamblify <https://www.gamblify.com/>
+ Indoor Astronaut <https://indoorastronaut.ch/>
+ Null <https://null.com/>
+ Orbital Knight <https://www.orbitalknight.com/>
+ Playful Studios <https://playfulstudios.com/>
## Diamond members
Sealow
- Sylv <https://rankith.itch.io/unnamed-space-idle-prototype>
- And 3 anonymous donors
+ Sylv <https://rankith.itch.io/unnamed-space-idle-prototype/>
+ And 4 anonymous donors
## Titanium members
@@ -55,43 +55,43 @@ generous deed immortalized in the next stable release of Godot Engine.
Game Dev Artisan <https://gamedevartisan.com/>
Garry Newman
Isaiah Smith <https://www.isaiahsmith.dev/>
- katnamag <https://katnamag.com/>
Kenney <https://kenney.nl/>
Life Art Studios <https://lifeartstudios.net/>
Lucid Silence Games
Matthew Campbell
Maxim Karsten
- Midjiwan AB <https://polytopia.io/>
PolyMars <https://polymars.dev/>
+ RPG in a Box <https://www.rpginabox.com/>
Razenpok <https://www.youtube.com/watch?v=-QxI-RP6-HM>
Smirk Software <https://smirk.gg/>
Sterling Long <https://www.sterlinglong.me/>
Sunshower <https://github.com/Phanterm>
TrampolineTales <https://trampolinetales.com/>
- Wilfred James <https://twitter.com/0430agi>
粟二华 (Su Erhua)
- And 5 anonymous donors
+ And 4 anonymous donors
## Platinum members
- AD Ford
+ Andy Touch
BlockImperiumGames (BIG)
+ Christian Baune
Christoph Woinke
Christopher Shifflett
Darrin Massena
Druvsaft
Edward Flick
+ getIntoGameDev
HP van Braam
iCommitGames
Jonah Stich
Justin McGettigan
Justo Delgado Baudí
+ katnamag
Marek Belski
Matthew Ekenstedt
Memories in 8Bit
Mike King
Nassor Paulino da Silva
- nate-wilkins
Neal Gompa (Conan Kudo)
Ronnie Cheng
Ryan Heath
@@ -103,60 +103,56 @@ generous deed immortalized in the next stable release of Godot Engine.
Stephan Lanfermann
TigerJ
Tim Yuen
- Tobias Holewa
Violin Iliev
Vladimír Chvátil
- And 17 anonymous donors
+ And 16 anonymous donors
## Gold members
@reilaos
- albinaask
alMoo Games
Alva Majo
Antti Vesanen
Artur Ilkaev
+ Asher Glick
Ben Burbank
Ben Rog-Wilhelm
Benito
Benjamin Sarsgard
Bernd Barsuhn
- Brian Ernst
+ Blake Farnsworth
Brian Levinsen
Brut
Carlo del Mundo
- Chen-Pang He (jdh8)
ClarkThyLord
Cosmin Munteanu
Coy Humphrey
+ David Chen Zhen
+ David Coles
David Hubber
David Snopek
- Dehyvis Coronel
+ Delton Ding
Dustuu
- Ed Morley
ElektroFox
endaye
Ends
Eric Phy
Faisal Al-Kubaisi (QatariGameDev)
- getIntoGameDev
+ FeralBytes
GlassBrick
Grau
- HTML5onMobilePLZ
- Hammster
+ Guangzhou Lingchan
Here's my 20 cents
hiulit
Illyan
Jacob (HACKhalo2 Studios)
Jam
- James Green
Jason Cawood
Javier Roman
Joel Martinez
John Gabriel
Jon Woodward
José Canepa
- João Pedro Braz
KAR Games
Karasu Studio
korinVR
@@ -166,20 +162,21 @@ generous deed immortalized in the next stable release of Godot Engine.
Luca Vazzano
LyaaaaaGames
MHDante
+ Malcolm Nixon
+ Manuel Requena
Mara Huldra
- Mark Schramm
- Martin Agnar Dahl
Martin Šenkeřík
- Matthew Hillier
Megabit Interactive
Modus Ponens
nezticle
+ Niklas Wahrman
NotNet
- Officine Pixel S.n.c.
+ Obelisk Island Studios
ohanaya3
Oleksii Nosov
Pav Soor
RAWRLAB Games
+ RadenTheFolf
Rafa Laguna
Request
re:thinc
@@ -187,19 +184,21 @@ generous deed immortalized in the next stable release of Godot Engine.
Saltlight Studio
Samuel Judd
Silverclad Studios
+ Sofox
Space Kraken Studios
Spoony Panda
- Sympa City
ThatGamer
ThePolyglotProgrammer
+ Tim Nedvyga
Tom Langwaldt
Trevor Slocum
tukon
+ Vincent Foulon
Weasel Games
+ WuotanStudios.com
Zhu Li
zikes
- Alexander Erlemann
Alex Khayrullin
Algebrute
Andriy
@@ -222,8 +221,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Fransiska
Harry Tumber
Harvey Fong
- Horváth-Lázár Péter
- illuxxium
James Couzens
Jared White
Jesús Chicharro
@@ -237,11 +234,8 @@ generous deed immortalized in the next stable release of Godot Engine.
kickmaniac
Liam Smyth
LoparPanda
- Marcus Dobler
Martin Gulliksson
Martin Soucek
- Matt Greene
- Matthew Dana
Michael Dürwald
Michael Policastro
n00sh
@@ -257,7 +251,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Rob
Rob McInroy
RodZilla
- Ronnie Ashlock
Ruzgud
Ryan Breaker
"Sage Automatic Systems, LLC"
@@ -269,6 +262,7 @@ generous deed immortalized in the next stable release of Godot Engine.
toto bibi
Valryia
VoidPointer
+ wpbirney420
Yifan Lai
zkip lan
@@ -286,14 +280,12 @@ generous deed immortalized in the next stable release of Godot Engine.
Aki Mimoto
Alan Beauchamp
Alejandro Saucedo
- AleMax
+ Alexander Erlemann
Alex Clavelle
- Alex de la Mare
alex raeside
Andre Altmueller
Andreas Østergaard Nielsen
Andrew
- andrew james morris
Ano Nim
Arch Toasty
Arda Erol
@@ -301,19 +293,16 @@ generous deed immortalized in the next stable release of Godot Engine.
Ash K
Aubrey Falconer
Austin Miller
- Azar Gurbanov
AzulCrescent
Balázs Batári
Beau Seymour
Benedikt
Ben Visness
Bill Thibault
- Bjarne Voigtländer
Bread
Brian Ford
Caleb Makela
Cameron Meyer
- Carlos Rios
Carl van der Geest
Cerno_b
ChainZ
@@ -366,7 +355,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Gary Thomas
gebba
Green Fox
- Greg Burkland
Greyson Richey
Grominet
Guldoman
@@ -389,7 +377,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Jeffrey Berube
Jennifer Graves
Joakim Askenbäck
- John Bruce
Jonas
Jonas Arndt
Jonas Yamazaki
@@ -400,12 +387,9 @@ generous deed immortalized in the next stable release of Godot Engine.
Joshua Heidrich
Juanfran
Juan Maggi
- Juan Uys
- Jueast
Julian le Roux
Justin Spedding
Kalydi Balázs
- Keedong Park
Keegan Scott
Keith Bradner
Kent Jofur
@@ -417,6 +401,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Kyle Jacobs
Leland Vakarian
Levi Berciu
+ liberodark
Linus Lind Lundgren
Ludovic DELVAL
Luigi Renna
@@ -430,7 +415,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Markie Music
Mark Tyler
Markus Michael Egger
- Markus Strompen
Martin Holas
Martin Liška
Martin Trbola
@@ -464,7 +448,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Nicolas Rosset
Nik Rudenko
Noel Billig
- Olexa Tourko
Oscar Domingo
ozrk
Parth Patel
@@ -482,7 +465,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Point08
Portponky
PsycHead
- Puntigames
Quincy Quincy
Quinn Morrison
Raghava Kovvali
@@ -493,12 +475,12 @@ generous deed immortalized in the next stable release of Godot Engine.
Richard Hayes
Riley
RobotCritter
- Rob Ruana
Roka
Roland Rząsa
Russ
Ryan Groom
Sammy Fischer
+ Satnam Singh
Sebastian Michailidis
SeongWan Kim
Sessamekesh
@@ -515,11 +497,10 @@ generous deed immortalized in the next stable release of Godot Engine.
smbe19
smo1704
SpicyCactuar
- Squidgy
+ SquidgySapphic
Squirrel
Stephen Rice
Stephen Schlie
- Sung soo Choi
Sven Walter
SxP
tadashi endo
@@ -536,23 +517,20 @@ generous deed immortalized in the next stable release of Godot Engine.
Tim Riley
Tom Webster
Trent Skinner
- Turgut Temucin
Tyler Stafos
Tyler Stepke
- Ukko K.
Uther
Vaughan Ling
vgmoose
- Vulinux
+ vlnx
Wapiti .
Wiley Thompson
Xatonym
- Yan Shi
Zekim
ケルベロス
貴宏 小松
- And 196 anonymous donors
+ And 208 anonymous donors
## Silver and bronze donors
diff --git a/SConstruct b/SConstruct
index 2cce05a20b..dbcd9dfd68 100644
--- a/SConstruct
+++ b/SConstruct
@@ -121,38 +121,38 @@ elif os.name == "nt" and methods.get_cmdline_bool("use_mingw", False):
# We let SCons build its default ENV as it includes OS-specific things which we don't
# want to have to pull in manually.
# Then we prepend PATH to make it take precedence, while preserving SCons' own entries.
-env_base = Environment(tools=custom_tools)
-env_base.PrependENVPath("PATH", os.getenv("PATH"))
-env_base.PrependENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
+env = Environment(tools=custom_tools)
+env.PrependENVPath("PATH", os.getenv("PATH"))
+env.PrependENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
if "TERM" in os.environ: # Used for colored output.
- env_base["ENV"]["TERM"] = os.environ["TERM"]
+ env["ENV"]["TERM"] = os.environ["TERM"]
-env_base.disabled_modules = []
-env_base.module_version_string = ""
-env_base.msvc = False
+env.disabled_modules = []
+env.module_version_string = ""
+env.msvc = False
-env_base.__class__.disable_module = methods.disable_module
+env.__class__.disable_module = methods.disable_module
-env_base.__class__.add_module_version_string = methods.add_module_version_string
+env.__class__.add_module_version_string = methods.add_module_version_string
-env_base.__class__.add_source_files = methods.add_source_files
-env_base.__class__.use_windows_spawn_fix = methods.use_windows_spawn_fix
+env.__class__.add_source_files = methods.add_source_files
+env.__class__.use_windows_spawn_fix = methods.use_windows_spawn_fix
-env_base.__class__.add_shared_library = methods.add_shared_library
-env_base.__class__.add_library = methods.add_library
-env_base.__class__.add_program = methods.add_program
-env_base.__class__.CommandNoCache = methods.CommandNoCache
-env_base.__class__.Run = methods.Run
-env_base.__class__.disable_warnings = methods.disable_warnings
-env_base.__class__.force_optimization_on_debug = methods.force_optimization_on_debug
-env_base.__class__.module_add_dependencies = methods.module_add_dependencies
-env_base.__class__.module_check_dependencies = methods.module_check_dependencies
+env.__class__.add_shared_library = methods.add_shared_library
+env.__class__.add_library = methods.add_library
+env.__class__.add_program = methods.add_program
+env.__class__.CommandNoCache = methods.CommandNoCache
+env.__class__.Run = methods.Run
+env.__class__.disable_warnings = methods.disable_warnings
+env.__class__.force_optimization_on_debug = methods.force_optimization_on_debug
+env.__class__.module_add_dependencies = methods.module_add_dependencies
+env.__class__.module_check_dependencies = methods.module_check_dependencies
-env_base["x86_libtheora_opt_gcc"] = False
-env_base["x86_libtheora_opt_vc"] = False
+env["x86_libtheora_opt_gcc"] = False
+env["x86_libtheora_opt_vc"] = False
# avoid issues when building with different versions of python out of the same directory
-env_base.SConsignFile(File("#.sconsign{0}.dblite".format(pickle.HIGHEST_PROTOCOL)).abspath)
+env.SConsignFile(File("#.sconsign{0}.dblite".format(pickle.HIGHEST_PROTOCOL)).abspath)
# Build options
@@ -180,6 +180,7 @@ opts.Add(
)
opts.Add(BoolVariable("debug_symbols", "Build with debugging symbols", False))
opts.Add(BoolVariable("separate_debug_symbols", "Extract debugging symbols to a separate file", False))
+opts.Add(BoolVariable("debug_paths_relative", "Make file paths in debug symbols relative (if supported)", False))
opts.Add(EnumVariable("lto", "Link-time optimization (production builds)", "none", ("none", "auto", "thin", "full")))
opts.Add(BoolVariable("production", "Set defaults to build Godot for use in production", False))
opts.Add(BoolVariable("threads", "Enable threading support", True))
@@ -227,6 +228,7 @@ opts.Add(
opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False))
opts.Add(BoolVariable("scu_build", "Use single compilation unit build", False))
opts.Add("scu_limit", "Max includes per SCU file when using scu_build (determines RAM use)", "0")
+opts.Add(BoolVariable("engine_update_check", "Enable engine update checks in the Project Manager", True))
# Thirdparty libraries
opts.Add(BoolVariable("builtin_brotli", "Use the built-in Brotli library", True))
@@ -273,22 +275,22 @@ opts.Add("linkflags", "Custom flags for the linker")
# Update the environment to have all above options defined
# in following code (especially platform and custom_modules).
-opts.Update(env_base)
+opts.Update(env)
# Copy custom environment variables if set.
-if env_base["import_env_vars"]:
- for env_var in str(env_base["import_env_vars"]).split(","):
+if env["import_env_vars"]:
+ for env_var in str(env["import_env_vars"]).split(","):
if env_var in os.environ:
- env_base["ENV"][env_var] = os.environ[env_var]
+ env["ENV"][env_var] = os.environ[env_var]
# Platform selection: validate input, and add options.
selected_platform = ""
-if env_base["platform"] != "":
- selected_platform = env_base["platform"]
-elif env_base["p"] != "":
- selected_platform = env_base["p"]
+if env["platform"] != "":
+ selected_platform = env["platform"]
+elif env["p"] != "":
+ selected_platform = env["p"]
else:
# Missing `platform` argument, try to detect platform automatically
if (
@@ -341,7 +343,7 @@ if selected_platform not in platform_list:
# Make sure to update this to the found, valid platform as it's used through the buildsystem as the reference.
# It should always be re-set after calling `opts.Update()` otherwise it uses the original input value.
-env_base["platform"] = selected_platform
+env["platform"] = selected_platform
# Add platform-specific options.
if selected_platform in platform_opts:
@@ -349,15 +351,15 @@ if selected_platform in platform_opts:
opts.Add(opt)
# Update the environment to take platform-specific options into account.
-opts.Update(env_base)
-env_base["platform"] = selected_platform # Must always be re-set after calling opts.Update().
+opts.Update(env)
+env["platform"] = selected_platform # Must always be re-set after calling opts.Update().
# Detect modules.
modules_detected = OrderedDict()
module_search_paths = ["modules"] # Built-in path.
-if env_base["custom_modules"]:
- paths = env_base["custom_modules"].split(",")
+if env["custom_modules"]:
+ paths = env["custom_modules"].split(",")
for p in paths:
try:
module_search_paths.append(methods.convert_custom_modules_path(p))
@@ -371,13 +373,13 @@ for path in module_search_paths:
# so save the time it takes to parse directories.
modules = methods.detect_modules(path, recursive=False)
else: # Custom.
- modules = methods.detect_modules(path, env_base["custom_modules_recursive"])
+ modules = methods.detect_modules(path, env["custom_modules_recursive"])
# Provide default include path for both the custom module search `path`
# and the base directory containing custom modules, as it may be different
# from the built-in "modules" name (e.g. "custom_modules/summator/summator.h"),
# so it can be referenced simply as `#include "summator/summator.h"`
# independently of where a module is located on user's filesystem.
- env_base.Prepend(CPPPATH=[path, os.path.dirname(path)])
+ env.Prepend(CPPPATH=[path, os.path.dirname(path)])
# Note: custom modules can override built-in ones.
modules_detected.update(modules)
@@ -386,7 +388,7 @@ for name, path in modules_detected.items():
sys.path.insert(0, path)
import config
- if env_base["modules_enabled_by_default"]:
+ if env["modules_enabled_by_default"]:
enabled = True
try:
enabled = config.is_enabled()
@@ -410,17 +412,17 @@ for name, path in modules_detected.items():
methods.write_modules(modules_detected)
# Update the environment again after all the module options are added.
-opts.Update(env_base)
-env_base["platform"] = selected_platform # Must always be re-set after calling opts.Update().
-Help(opts.GenerateHelpText(env_base))
+opts.Update(env)
+env["platform"] = selected_platform # Must always be re-set after calling opts.Update().
+Help(opts.GenerateHelpText(env))
# add default include paths
-env_base.Prepend(CPPPATH=["#"])
+env.Prepend(CPPPATH=["#"])
# configure ENV for platform
-env_base.platform_exporters = platform_exporters
-env_base.platform_apis = platform_apis
+env.platform_exporters = platform_exporters
+env.platform_apis = platform_apis
# Configuration of build targets:
# - Editor or template
@@ -429,68 +431,69 @@ env_base.platform_apis = platform_apis
# - Optimization level
# - Debug symbols for crash traces / debuggers
-env_base.editor_build = env_base["target"] == "editor"
-env_base.dev_build = env_base["dev_build"]
-env_base.debug_features = env_base["target"] in ["editor", "template_debug"]
+env.editor_build = env["target"] == "editor"
+env.dev_build = env["dev_build"]
+env.debug_features = env["target"] in ["editor", "template_debug"]
-if env_base.dev_build:
+if env.dev_build:
opt_level = "none"
-elif env_base.debug_features:
+elif env.debug_features:
opt_level = "speed_trace"
else: # Release
opt_level = "speed"
-env_base["optimize"] = ARGUMENTS.get("optimize", opt_level)
-env_base["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", env_base.dev_build)
+env["optimize"] = ARGUMENTS.get("optimize", opt_level)
+env["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", env.dev_build)
-if env_base.editor_build:
- env_base.Append(CPPDEFINES=["TOOLS_ENABLED"])
+if env.editor_build:
+ env.Append(CPPDEFINES=["TOOLS_ENABLED"])
-if env_base.debug_features:
+if env.debug_features:
# DEBUG_ENABLED enables debugging *features* and debug-only code, which is intended
# to give *users* extra debugging information for their game development.
- env_base.Append(CPPDEFINES=["DEBUG_ENABLED"])
+ env.Append(CPPDEFINES=["DEBUG_ENABLED"])
-if env_base.dev_build:
+if env.dev_build:
# DEV_ENABLED enables *engine developer* code which should only be compiled for those
# working on the engine itself.
- env_base.Append(CPPDEFINES=["DEV_ENABLED"])
+ env.Append(CPPDEFINES=["DEV_ENABLED"])
else:
# Disable assert() for production targets (only used in thirdparty code).
- env_base.Append(CPPDEFINES=["NDEBUG"])
+ env.Append(CPPDEFINES=["NDEBUG"])
# SCons speed optimization controlled by the `fast_unsafe` option, which provide
# more than 10 s speed up for incremental rebuilds.
# Unsafe as they reduce the certainty of rebuilding all changed files, so it's
# enabled by default for `debug` builds, and can be overridden from command line.
# Ref: https://github.com/SCons/scons/wiki/GoFastButton
-if methods.get_cmdline_bool("fast_unsafe", env_base.dev_build):
+if methods.get_cmdline_bool("fast_unsafe", env.dev_build):
# Renamed to `content-timestamp` in SCons >= 4.2, keeping MD5 for compat.
- env_base.Decider("MD5-timestamp")
- env_base.SetOption("implicit_cache", 1)
- env_base.SetOption("max_drift", 60)
+ env.Decider("MD5-timestamp")
+ env.SetOption("implicit_cache", 1)
+ env.SetOption("max_drift", 60)
-if env_base["use_precise_math_checks"]:
- env_base.Append(CPPDEFINES=["PRECISE_MATH_CHECKS"])
+if env["use_precise_math_checks"]:
+ env.Append(CPPDEFINES=["PRECISE_MATH_CHECKS"])
-if not env_base.File("#main/splash_editor.png").exists():
+if env.editor_build and env["engine_update_check"]:
+ env.Append(CPPDEFINES=["ENGINE_UPDATE_CHECK_ENABLED"])
+
+if not env.File("#main/splash_editor.png").exists():
# Force disabling editor splash if missing.
- env_base["no_editor_splash"] = True
-if env_base["no_editor_splash"]:
- env_base.Append(CPPDEFINES=["NO_EDITOR_SPLASH"])
+ env["no_editor_splash"] = True
+if env["no_editor_splash"]:
+ env.Append(CPPDEFINES=["NO_EDITOR_SPLASH"])
-if not env_base["deprecated"]:
- env_base.Append(CPPDEFINES=["DISABLE_DEPRECATED"])
+if not env["deprecated"]:
+ env.Append(CPPDEFINES=["DISABLE_DEPRECATED"])
-if env_base["precision"] == "double":
- env_base.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"])
+if env["precision"] == "double":
+ env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"])
tmppath = "./platform/" + selected_platform
sys.path.insert(0, tmppath)
import detect
-env = env_base.Clone()
-
# Default num_jobs to local cpu count if not user specified.
# SCons has a peculiarity where user-specified options won't be overridden
# by SetOption, so we can rely on this to know if we should use our default.
@@ -565,7 +568,7 @@ if env["production"]:
# Run SCU file generation script if in a SCU build.
if env["scu_build"]:
max_includes_per_scu = 8
- if env_base.dev_build == True:
+ if env.dev_build == True:
max_includes_per_scu = 1024
read_scu_limit = int(env["scu_limit"])
@@ -583,73 +586,6 @@ print(f'Building for platform "{selected_platform}", architecture "{env["arch"]}
if env.dev_build:
print("NOTE: Developer build, with debug optimization level and debug symbols (unless overridden).")
-# Set optimize and debug_symbols flags.
-# "custom" means do nothing and let users set their own optimization flags.
-# Needs to happen after configure to have `env.msvc` defined.
-if env.msvc:
- if env["debug_symbols"]:
- env.Append(CCFLAGS=["/Zi", "/FS"])
- env.Append(LINKFLAGS=["/DEBUG:FULL"])
- else:
- env.Append(LINKFLAGS=["/DEBUG:NONE"])
-
- if env["optimize"] == "speed":
- env.Append(CCFLAGS=["/O2"])
- env.Append(LINKFLAGS=["/OPT:REF"])
- elif env["optimize"] == "speed_trace":
- env.Append(CCFLAGS=["/O2"])
- env.Append(LINKFLAGS=["/OPT:REF", "/OPT:NOICF"])
- elif env["optimize"] == "size":
- env.Append(CCFLAGS=["/O1"])
- env.Append(LINKFLAGS=["/OPT:REF"])
- elif env["optimize"] == "debug" or env["optimize"] == "none":
- env.Append(CCFLAGS=["/Od"])
-else:
- if env["debug_symbols"]:
- # Adding dwarf-4 explicitly makes stacktraces work with clang builds,
- # otherwise addr2line doesn't understand them
- env.Append(CCFLAGS=["-gdwarf-4"])
- if env.dev_build:
- env.Append(CCFLAGS=["-g3"])
- else:
- env.Append(CCFLAGS=["-g2"])
- else:
- if methods.using_clang(env) and not methods.is_vanilla_clang(env):
- # Apple Clang, its linker doesn't like -s.
- env.Append(LINKFLAGS=["-Wl,-S", "-Wl,-x", "-Wl,-dead_strip"])
- else:
- env.Append(LINKFLAGS=["-s"])
-
- if env["optimize"] == "speed":
- env.Append(CCFLAGS=["-O3"])
- # `-O2` is friendlier to debuggers than `-O3`, leading to better crash backtraces.
- elif env["optimize"] == "speed_trace":
- env.Append(CCFLAGS=["-O2"])
- elif env["optimize"] == "size":
- env.Append(CCFLAGS=["-Os"])
- elif env["optimize"] == "debug":
- env.Append(CCFLAGS=["-Og"])
- elif env["optimize"] == "none":
- env.Append(CCFLAGS=["-O0"])
-
-# Needs to happen after configure to handle "auto".
-if env["lto"] != "none":
- print("Using LTO: " + env["lto"])
-
-# Set our C and C++ standard requirements.
-# C++17 is required as we need guaranteed copy elision as per GH-36436.
-# Prepending to make it possible to override.
-# This needs to come after `configure`, otherwise we don't have env.msvc.
-if not env.msvc:
- # Specifying GNU extensions support explicitly, which are supported by
- # both GCC and Clang. Both currently default to gnu11 and gnu++14.
- env.Prepend(CFLAGS=["-std=gnu11"])
- env.Prepend(CXXFLAGS=["-std=gnu++17"])
-else:
- # MSVC doesn't have clear C standard support, /std only covers C++.
- # We apply it to CCFLAGS (both C and C++ code) in case it impacts C features.
- env.Prepend(CCFLAGS=["/std:c++17"])
-
# Enforce our minimal compiler version requirements
cc_version = methods.get_compiler_version(env) or {
"major": None,
@@ -695,6 +631,9 @@ if methods.using_gcc(env):
"to switch to posix threads."
)
Exit(255)
+ if env["debug_paths_relative"] and cc_version_major < 8:
+ print("GCC < 8 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
+ env["debug_paths_relative"] = False
elif methods.using_clang(env):
if cc_version_major == -1:
print(
@@ -717,12 +656,89 @@ elif methods.using_clang(env):
"support C++17. Supported versions are Apple Clang 10 and later."
)
Exit(255)
+ if env["debug_paths_relative"] and not vanilla and cc_version_major < 12:
+ print("Apple Clang < 12 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
+ env["debug_paths_relative"] = False
elif cc_version_major < 6:
print(
"Detected Clang version older than 6, which does not fully support "
"C++17. Supported versions are Clang 6 and later."
)
Exit(255)
+ if env["debug_paths_relative"] and cc_version_major < 10:
+ print("Clang < 10 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
+ env["debug_paths_relative"] = False
+
+# Set optimize and debug_symbols flags.
+# "custom" means do nothing and let users set their own optimization flags.
+# Needs to happen after configure to have `env.msvc` defined.
+if env.msvc:
+ if env["debug_symbols"]:
+ env.Append(CCFLAGS=["/Zi", "/FS"])
+ env.Append(LINKFLAGS=["/DEBUG:FULL"])
+ else:
+ env.Append(LINKFLAGS=["/DEBUG:NONE"])
+
+ if env["optimize"] == "speed":
+ env.Append(CCFLAGS=["/O2"])
+ env.Append(LINKFLAGS=["/OPT:REF"])
+ elif env["optimize"] == "speed_trace":
+ env.Append(CCFLAGS=["/O2"])
+ env.Append(LINKFLAGS=["/OPT:REF", "/OPT:NOICF"])
+ elif env["optimize"] == "size":
+ env.Append(CCFLAGS=["/O1"])
+ env.Append(LINKFLAGS=["/OPT:REF"])
+ elif env["optimize"] == "debug" or env["optimize"] == "none":
+ env.Append(CCFLAGS=["/Od"])
+else:
+ if env["debug_symbols"]:
+ # Adding dwarf-4 explicitly makes stacktraces work with clang builds,
+ # otherwise addr2line doesn't understand them
+ env.Append(CCFLAGS=["-gdwarf-4"])
+ if env.dev_build:
+ env.Append(CCFLAGS=["-g3"])
+ else:
+ env.Append(CCFLAGS=["-g2"])
+ if env["debug_paths_relative"]:
+ # Remap absolute paths to relative paths for debug symbols.
+ project_path = Dir("#").abspath
+ env.Append(CCFLAGS=[f"-ffile-prefix-map={project_path}=."])
+ else:
+ if methods.using_clang(env) and not methods.is_vanilla_clang(env):
+ # Apple Clang, its linker doesn't like -s.
+ env.Append(LINKFLAGS=["-Wl,-S", "-Wl,-x", "-Wl,-dead_strip"])
+ else:
+ env.Append(LINKFLAGS=["-s"])
+
+ if env["optimize"] == "speed":
+ env.Append(CCFLAGS=["-O3"])
+ # `-O2` is friendlier to debuggers than `-O3`, leading to better crash backtraces.
+ elif env["optimize"] == "speed_trace":
+ env.Append(CCFLAGS=["-O2"])
+ elif env["optimize"] == "size":
+ env.Append(CCFLAGS=["-Os"])
+ elif env["optimize"] == "debug":
+ env.Append(CCFLAGS=["-Og"])
+ elif env["optimize"] == "none":
+ env.Append(CCFLAGS=["-O0"])
+
+# Needs to happen after configure to handle "auto".
+if env["lto"] != "none":
+ print("Using LTO: " + env["lto"])
+
+# Set our C and C++ standard requirements.
+# C++17 is required as we need guaranteed copy elision as per GH-36436.
+# Prepending to make it possible to override.
+# This needs to come after `configure`, otherwise we don't have env.msvc.
+if not env.msvc:
+ # Specifying GNU extensions support explicitly, which are supported by
+ # both GCC and Clang. Both currently default to gnu11 and gnu++14.
+ env.Prepend(CFLAGS=["-std=gnu11"])
+ env.Prepend(CXXFLAGS=["-std=gnu++17"])
+else:
+ # MSVC doesn't have clear C standard support, /std only covers C++.
+ # We apply it to CCFLAGS (both C and C++ code) in case it impacts C features.
+ env.Prepend(CCFLAGS=["/std:c++17"])
# Disable exception handling. Godot doesn't use exceptions anywhere, and this
# saves around 20% of binary size and very significant build time (GH-80513).
@@ -826,7 +842,7 @@ suffix += "." + env["target"]
if env.dev_build:
suffix += ".dev"
-if env_base["precision"] == "double":
+if env["precision"] == "double":
suffix += ".double"
suffix += "." + env["arch"]
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 9f4bff3779..f2f8aebe8b 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -82,6 +82,17 @@ int Engine::get_audio_output_latency() const {
return _audio_output_latency;
}
+void Engine::increment_frames_drawn() {
+ if (frame_server_synced) {
+ server_syncs++;
+ } else {
+ server_syncs = 0;
+ }
+ frame_server_synced = false;
+
+ frames_drawn++;
+}
+
uint64_t Engine::get_frames_drawn() {
return frames_drawn;
}
@@ -364,6 +375,11 @@ Engine *Engine::get_singleton() {
return singleton;
}
+bool Engine::notify_frame_server_synced() {
+ frame_server_synced = true;
+ return server_syncs > SERVER_SYNC_FRAME_COUNT_WARNING;
+}
+
Engine::Engine() {
singleton = this;
}
diff --git a/core/config/engine.h b/core/config/engine.h
index d1495b36c2..8dece803e3 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -91,6 +91,10 @@ private:
String write_movie_path;
String shader_cache_path;
+ static constexpr int SERVER_SYNC_FRAME_COUNT_WARNING = 5;
+ int server_syncs = 0;
+ bool frame_server_synced = false;
+
public:
static Engine *get_singleton();
@@ -179,6 +183,9 @@ public:
bool is_generate_spirv_debug_info_enabled() const;
int32_t get_gpu_index() const;
+ void increment_frames_drawn();
+ bool notify_frame_server_synced();
+
Engine();
virtual ~Engine() {}
};
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 75eea2ef50..104b17961d 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1528,6 +1528,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF_RST("internationalization/rendering/force_right_to_left_layout_direction", false);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_ENUM, "Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale"), 0);
+ GLOBAL_DEF_BASIC("internationalization/rendering/root_node_auto_translate", true);
GLOBAL_DEF(PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater"), 2000);
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 8c85030783..03c31bee28 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -338,6 +338,10 @@ bool OS::is_process_running(int p_pid) const {
return ::OS::get_singleton()->is_process_running(p_pid);
}
+int OS::get_process_exit_code(int p_pid) const {
+ return ::OS::get_singleton()->get_process_exit_code(p_pid);
+}
+
int OS::get_process_id() const {
return ::OS::get_singleton()->get_process_id();
}
@@ -602,6 +606,7 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("shell_open", "uri"), &OS::shell_open);
ClassDB::bind_method(D_METHOD("shell_show_in_file_manager", "file_or_dir_path", "open_folder"), &OS::shell_show_in_file_manager, DEFVAL(true));
ClassDB::bind_method(D_METHOD("is_process_running", "pid"), &OS::is_process_running);
+ ClassDB::bind_method(D_METHOD("get_process_exit_code", "pid"), &OS::get_process_exit_code);
ClassDB::bind_method(D_METHOD("get_process_id"), &OS::get_process_id);
ClassDB::bind_method(D_METHOD("has_environment", "variable"), &OS::has_environment);
diff --git a/core/core_bind.h b/core/core_bind.h
index d46321cf5c..3c0cdc25ce 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -164,6 +164,7 @@ public:
Error shell_show_in_file_manager(const String &p_path, bool p_open_folder = true);
bool is_process_running(int p_pid) const;
+ int get_process_exit_code(int p_pid) const;
int get_process_id() const;
void set_restart_on_exit(bool p_restart, const Vector<String> &p_restart_arguments = Vector<String>());
diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp
index 680a653dfc..e99885befa 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -84,7 +84,7 @@ static Error _erase_recursive(DirAccess *da) {
String n = da->get_next();
while (!n.is_empty()) {
if (n != "." && n != "..") {
- if (da->current_is_dir()) {
+ if (da->current_is_dir() && !da->is_link(n)) {
dirs.push_back(n);
} else {
files.push_back(n);
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index 4487b8e472..18dbac991c 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -656,10 +656,19 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
ERR_FAIL_COND_V(!ClassDB::can_instantiate(str), ERR_INVALID_DATA);
Object *obj = ClassDB::instantiate(str);
-
ERR_FAIL_NULL_V(obj, ERR_UNAVAILABLE);
- ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+ // Avoid premature free `RefCounted`. This must be done before properties are initialized,
+ // since script functions (setters, implicit initializer) may be called. See GH-68666.
+ Variant variant;
+ if (Object::cast_to<RefCounted>(obj)) {
+ Ref<RefCounted> ref = Ref<RefCounted>(Object::cast_to<RefCounted>(obj));
+ variant = ref;
+ } else {
+ variant = obj;
+ }
+
+ ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
int32_t count = decode_uint32(buf);
buf += 4;
len -= 4;
@@ -699,12 +708,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
}
}
- if (Object::cast_to<RefCounted>(obj)) {
- Ref<RefCounted> ref = Ref<RefCounted>(Object::cast_to<RefCounted>(obj));
- r_variant = ref;
- } else {
- r_variant = obj;
- }
+ r_variant = variant;
}
}
@@ -790,11 +794,11 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
ERR_FAIL_INDEX_V(bt, Variant::VARIANT_MAX, ERR_INVALID_DATA);
builtin_type = (Variant::Type)bt;
- ERR_FAIL_COND_V(!p_allow_objects && builtin_type == Variant::OBJECT, ERR_UNAUTHORIZED);
+ if (!p_allow_objects && builtin_type == Variant::OBJECT) {
+ class_name = EncodedObjectAsID::get_class_static();
+ }
} break;
case HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME: {
- ERR_FAIL_COND_V(!p_allow_objects, ERR_UNAUTHORIZED);
-
String str;
Error err = _decode_string(buf, len, r_len, str);
if (err) {
@@ -802,22 +806,28 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
}
builtin_type = Variant::OBJECT;
- class_name = str;
+ if (p_allow_objects) {
+ class_name = str;
+ } else {
+ class_name = EncodedObjectAsID::get_class_static();
+ }
} break;
case HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT: {
- ERR_FAIL_COND_V(!p_allow_objects, ERR_UNAUTHORIZED);
-
String path;
Error err = _decode_string(buf, len, r_len, path);
if (err) {
return err;
}
- ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'.");
- script = ResourceLoader::load(path, "Script");
- ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'.");
builtin_type = Variant::OBJECT;
- class_name = script->get_instance_base_type();
+ if (p_allow_objects) {
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'.");
+ script = ResourceLoader::load(path, "Script");
+ ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'.");
+ class_name = script->get_instance_base_type();
+ } else {
+ class_name = EncodedObjectAsID::get_class_static();
+ }
} break;
default:
ERR_FAIL_V(ERR_INVALID_DATA); // Future proofing.
@@ -1239,13 +1249,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
if (array.is_typed()) {
Ref<Script> script = array.get_typed_script();
if (script.is_valid()) {
- ERR_FAIL_COND_V(!p_full_objects, ERR_UNAVAILABLE);
header |= HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT;
} else if (array.get_typed_class_name() != StringName()) {
- ERR_FAIL_COND_V(!p_full_objects, ERR_UNAVAILABLE);
header |= HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME;
} else {
- ERR_FAIL_COND_V(!p_full_objects && array.get_typed_builtin() == Variant::OBJECT, ERR_UNAVAILABLE);
header |= HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN;
}
}
diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp
index 2b9487b9e1..90a8f49a75 100644
--- a/core/io/stream_peer_tcp.cpp
+++ b/core/io/stream_peer_tcp.cpp
@@ -51,6 +51,7 @@ Error StreamPeerTCP::poll() {
status = STATUS_ERROR;
return err;
}
+ return OK;
} else if (status != STATUS_CONNECTING) {
return OK;
}
diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp
index ef3d315e4b..e0b8730a67 100644
--- a/core/object/worker_thread_pool.cpp
+++ b/core/object/worker_thread_pool.cpp
@@ -35,6 +35,8 @@
#include "core/os/thread_safe.h"
#include "core/templates/command_queue_mt.h"
+WorkerThreadPool::Task *const WorkerThreadPool::ThreadData::YIELDING = (Task *)1;
+
void WorkerThreadPool::Task::free_template_userdata() {
ERR_FAIL_NULL(template_userdata);
ERR_FAIL_NULL(native_func_userdata);
@@ -60,11 +62,13 @@ void WorkerThreadPool::_process_task(Task *p_task) {
// its pre-created threads can't have ScriptServer::thread_enter() called on them early.
// Therefore, we do it late at the first opportunity, so in case the task
// about to be run uses scripting, guarantees are held.
+ task_mutex.lock();
if (!curr_thread.ready_for_scripting && ScriptServer::are_languages_initialized()) {
+ task_mutex.unlock();
ScriptServer::thread_enter();
+ task_mutex.lock();
curr_thread.ready_for_scripting = true;
}
- task_mutex.lock();
p_task->pool_thread_index = pool_thread_index;
prev_task = curr_thread.current_task;
curr_thread.current_task = p_task;
@@ -389,83 +393,115 @@ Error WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) {
task_mutex.unlock();
if (caller_pool_thread) {
- while (true) {
- Task *task_to_process = nullptr;
- {
- MutexLock lock(task_mutex);
- bool was_signaled = caller_pool_thread->signaled;
- caller_pool_thread->signaled = false;
-
- if (task->completed) {
- // This thread was awaken also for some reason, but it's about to exit.
- // Let's find out what may be pending and forward the requests.
- if (!exit_threads && was_signaled) {
- uint32_t to_process = task_queue.first() ? 1 : 0;
- uint32_t to_promote = caller_pool_thread->current_task->low_priority && low_priority_task_queue.first() ? 1 : 0;
- if (to_process || to_promote) {
- // This thread must be left alone since it won't loop again.
- caller_pool_thread->signaled = true;
- _notify_threads(caller_pool_thread, to_process, to_promote);
- }
- }
+ _wait_collaboratively(caller_pool_thread, task);
+ task->waiting_pool--;
+ if (task->waiting_pool == 0 && task->waiting_user == 0) {
+ tasks.erase(p_task_id);
+ task_allocator.free(task);
+ }
+ } else {
+ task->done_semaphore.wait();
+ task_mutex.lock();
+ task->waiting_user--;
+ if (task->waiting_pool == 0 && task->waiting_user == 0) {
+ tasks.erase(p_task_id);
+ task_allocator.free(task);
+ }
+ task_mutex.unlock();
+ }
- task->waiting_pool--;
- if (task->waiting_pool == 0 && task->waiting_user == 0) {
- tasks.erase(p_task_id);
- task_allocator.free(task);
- }
+ return OK;
+}
- break;
- }
+void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task) {
+ // Keep processing tasks until the condition to stop waiting is met.
- if (!exit_threads) {
- // This is a thread from the pool. It shouldn't just idle.
- // Let's try to process other tasks while we wait.
+#define IS_WAIT_OVER (unlikely(p_task == ThreadData::YIELDING) ? p_caller_pool_thread->yield_is_over : p_task->completed)
- if (caller_pool_thread->current_task->low_priority && low_priority_task_queue.first()) {
- if (_try_promote_low_priority_task()) {
- _notify_threads(caller_pool_thread, 1, 0);
- }
+ while (true) {
+ Task *task_to_process = nullptr;
+ {
+ MutexLock lock(task_mutex);
+ bool was_signaled = p_caller_pool_thread->signaled;
+ p_caller_pool_thread->signaled = false;
+
+ if (IS_WAIT_OVER) {
+ p_caller_pool_thread->yield_is_over = false;
+ if (!exit_threads && was_signaled) {
+ // This thread was awaken for some additional reason, but it's about to exit.
+ // Let's find out what may be pending and forward the requests.
+ uint32_t to_process = task_queue.first() ? 1 : 0;
+ uint32_t to_promote = p_caller_pool_thread->current_task->low_priority && low_priority_task_queue.first() ? 1 : 0;
+ if (to_process || to_promote) {
+ // This thread must be left alone since it won't loop again.
+ p_caller_pool_thread->signaled = true;
+ _notify_threads(p_caller_pool_thread, to_process, to_promote);
}
+ }
- if (singleton->task_queue.first()) {
- task_to_process = task_queue.first()->self();
- task_queue.remove(task_queue.first());
+ break;
+ }
+
+ if (!exit_threads) {
+ if (p_caller_pool_thread->current_task->low_priority && low_priority_task_queue.first()) {
+ if (_try_promote_low_priority_task()) {
+ _notify_threads(p_caller_pool_thread, 1, 0);
}
+ }
- if (!task_to_process) {
- caller_pool_thread->awaited_task = task;
+ if (singleton->task_queue.first()) {
+ task_to_process = task_queue.first()->self();
+ task_queue.remove(task_queue.first());
+ }
- if (flushing_cmd_queue) {
- flushing_cmd_queue->unlock();
- }
- caller_pool_thread->cond_var.wait(lock);
- if (flushing_cmd_queue) {
- flushing_cmd_queue->lock();
- }
+ if (!task_to_process) {
+ p_caller_pool_thread->awaited_task = p_task;
- DEV_ASSERT(exit_threads || caller_pool_thread->signaled || task->completed);
- caller_pool_thread->awaited_task = nullptr;
+ if (flushing_cmd_queue) {
+ flushing_cmd_queue->unlock();
+ }
+ p_caller_pool_thread->cond_var.wait(lock);
+ if (flushing_cmd_queue) {
+ flushing_cmd_queue->lock();
}
- }
- }
- if (task_to_process) {
- _process_task(task_to_process);
+ DEV_ASSERT(exit_threads || p_caller_pool_thread->signaled || IS_WAIT_OVER);
+ p_caller_pool_thread->awaited_task = nullptr;
+ }
}
}
- } else {
- task->done_semaphore.wait();
- task_mutex.lock();
- task->waiting_user--;
- if (task->waiting_pool == 0 && task->waiting_user == 0) {
- tasks.erase(p_task_id);
- task_allocator.free(task);
+
+ if (task_to_process) {
+ _process_task(task_to_process);
}
+ }
+}
+
+void WorkerThreadPool::yield() {
+ int th_index = get_thread_index();
+ ERR_FAIL_COND_MSG(th_index == -1, "This function can only be called from a worker thread.");
+ _wait_collaboratively(&threads[th_index], ThreadData::YIELDING);
+}
+
+void WorkerThreadPool::notify_yield_over(TaskID p_task_id) {
+ task_mutex.lock();
+ Task **taskp = tasks.getptr(p_task_id);
+ if (!taskp) {
task_mutex.unlock();
+ ERR_FAIL_MSG("Invalid Task ID.");
+ }
+ Task *task = *taskp;
+ if (task->completed) {
+ task_mutex.unlock();
+ return;
}
- return OK;
+ ThreadData &td = threads[task->pool_thread_index];
+ td.yield_is_over = true;
+ td.signaled = true;
+ td.cond_var.notify_one();
+
+ task_mutex.unlock();
}
WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) {
diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h
index fdddc9a647..64f24df79f 100644
--- a/core/object/worker_thread_pool.h
+++ b/core/object/worker_thread_pool.h
@@ -107,13 +107,21 @@ private:
BinaryMutex task_mutex;
struct ThreadData {
+ static Task *const YIELDING; // Too bad constexpr doesn't work here.
+
uint32_t index = 0;
Thread thread;
- bool ready_for_scripting = false;
- bool signaled = false;
+ bool ready_for_scripting : 1;
+ bool signaled : 1;
+ bool yield_is_over : 1;
Task *current_task = nullptr;
- Task *awaited_task = nullptr; // Null if not awaiting the condition variable. Special value for idle-waiting.
+ Task *awaited_task = nullptr; // Null if not awaiting the condition variable, or special value (YIELDING).
ConditionVariable cond_var;
+
+ ThreadData() :
+ ready_for_scripting(false),
+ signaled(false),
+ yield_is_over(false) {}
};
TightLocalVector<ThreadData> threads;
@@ -177,6 +185,8 @@ private:
}
};
+ void _wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task);
+
protected:
static void _bind_methods();
@@ -196,6 +206,9 @@ public:
bool is_task_completed(TaskID p_task_id) const;
Error wait_for_task_completion(TaskID p_task_id);
+ void yield();
+ void notify_yield_over(TaskID p_task_id);
+
template <typename C, typename M, typename U>
GroupID add_template_group_task(C *p_instance, M p_method, U p_userdata, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String()) {
typedef GroupUserData<C, M, U> GroupUD;
diff --git a/core/os/os.h b/core/os/os.h
index 3827bb273a..06be0e2b41 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -176,6 +176,7 @@ public:
virtual Error kill(const ProcessID &p_pid) = 0;
virtual int get_process_id() const;
virtual bool is_process_running(const ProcessID &p_pid) const = 0;
+ virtual int get_process_exit_code(const ProcessID &p_pid) const = 0;
virtual void vibrate_handheld(int p_duration_ms = 500) {}
virtual Error shell_open(const String &p_uri);
diff --git a/core/templates/command_queue_mt.cpp b/core/templates/command_queue_mt.cpp
index 6ecd75ebc1..0c5c6394a1 100644
--- a/core/templates/command_queue_mt.cpp
+++ b/core/templates/command_queue_mt.cpp
@@ -70,14 +70,8 @@ CommandQueueMT::SyncSemaphore *CommandQueueMT::_alloc_sync_sem() {
return &sync_sems[idx];
}
-CommandQueueMT::CommandQueueMT(bool p_sync) {
- if (p_sync) {
- sync = memnew(Semaphore);
- }
+CommandQueueMT::CommandQueueMT() {
}
CommandQueueMT::~CommandQueueMT() {
- if (sync) {
- memdelete(sync);
- }
}
diff --git a/core/templates/command_queue_mt.h b/core/templates/command_queue_mt.h
index e26f11d28a..a4ac338bed 100644
--- a/core/templates/command_queue_mt.h
+++ b/core/templates/command_queue_mt.h
@@ -248,16 +248,17 @@
#define CMD_TYPE(N) Command##N<T, M COMMA(N) COMMA_SEP_LIST(TYPE_ARG, N)>
#define CMD_ASSIGN_PARAM(N) cmd->p##N = p##N
-#define DECL_PUSH(N) \
- template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \
- void push(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \
- CMD_TYPE(N) *cmd = allocate_and_lock<CMD_TYPE(N)>(); \
- cmd->instance = p_instance; \
- cmd->method = p_method; \
- SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
- unlock(); \
- if (sync) \
- sync->post(); \
+#define DECL_PUSH(N) \
+ template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \
+ void push(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \
+ CMD_TYPE(N) *cmd = allocate_and_lock<CMD_TYPE(N)>(); \
+ cmd->instance = p_instance; \
+ cmd->method = p_method; \
+ SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
+ if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \
+ WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \
+ } \
+ unlock(); \
}
#define CMD_RET_TYPE(N) CommandRet##N<T, M, COMMA_SEP_LIST(TYPE_ARG, N) COMMA(N) R>
@@ -272,9 +273,10 @@
SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
cmd->ret = r_ret; \
cmd->sync_sem = ss; \
+ if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \
+ WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \
+ } \
unlock(); \
- if (sync) \
- sync->post(); \
ss->sem.wait(); \
ss->in_use = false; \
}
@@ -290,9 +292,10 @@
cmd->method = p_method; \
SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
cmd->sync_sem = ss; \
+ if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \
+ WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \
+ } \
unlock(); \
- if (sync) \
- sync->post(); \
ss->sem.wait(); \
ss->in_use = false; \
}
@@ -340,7 +343,7 @@ class CommandQueueMT {
LocalVector<uint8_t> command_mem;
SyncSemaphore sync_sems[SYNC_SEMAPHORES];
Mutex mutex;
- Semaphore *sync = nullptr;
+ WorkerThreadPool::TaskID pump_task_id = WorkerThreadPool::INVALID_TASK_ID;
uint64_t flush_read_ptr = 0;
template <typename T>
@@ -420,12 +423,16 @@ public:
}
void wait_and_flush() {
- ERR_FAIL_NULL(sync);
- sync->wait();
+ ERR_FAIL_COND(pump_task_id == WorkerThreadPool::INVALID_TASK_ID);
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(pump_task_id);
_flush();
}
- CommandQueueMT(bool p_sync);
+ void set_pump_task_id(WorkerThreadPool::TaskID p_task_id) {
+ pump_task_id = p_task_id;
+ }
+
+ CommandQueueMT();
~CommandQueueMT();
};
diff --git a/core/variant/array.h b/core/variant/array.h
index d45a6887e2..3aa957b312 100644
--- a/core/variant/array.h
+++ b/core/variant/array.h
@@ -54,7 +54,7 @@ public:
_FORCE_INLINE_ ConstIterator &operator--();
_FORCE_INLINE_ bool operator==(const ConstIterator &p_other) const { return element_ptr == p_other.element_ptr; }
- _FORCE_INLINE_ bool operator!=(const ConstIterator &p_other) const { return element_ptr == p_other.element_ptr; }
+ _FORCE_INLINE_ bool operator!=(const ConstIterator &p_other) const { return element_ptr != p_other.element_ptr; }
_FORCE_INLINE_ ConstIterator(const Variant *p_element_ptr, Variant *p_read_only = nullptr) :
element_ptr(p_element_ptr), read_only(p_read_only) {}
diff --git a/doc/classes/BoneAttachment3D.xml b/doc/classes/BoneAttachment3D.xml
index bafa463f82..a51b2d9a94 100644
--- a/doc/classes/BoneAttachment3D.xml
+++ b/doc/classes/BoneAttachment3D.xml
@@ -21,11 +21,10 @@
Returns whether the BoneAttachment3D node is using an external [Skeleton3D] rather than attempting to use its parent node as the [Skeleton3D].
</description>
</method>
- <method name="on_bone_pose_update">
+ <method name="on_skeleton_update">
<return type="void" />
- <param index="0" name="bone_index" type="int" />
<description>
- A function that is called automatically when the [Skeleton3D] the BoneAttachment3D node is using has a bone that has changed its pose. This function is where the BoneAttachment3D node updates its position so it is correctly bound when it is [i]not[/i] set to override the bone pose.
+ A function that is called automatically when the [Skeleton3D] is updated. This function is where the [BoneAttachment3D] node updates its position so it is correctly bound when it is [i]not[/i] set to override the bone pose.
</description>
</method>
<method name="set_external_skeleton">
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index 09bc4b3412..a0c76a3ad6 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -937,7 +937,7 @@
<member name="anchor_top" type="float" setter="_set_anchor" getter="get_anchor" default="0.0">
Anchors the top edge of the node to the origin, the center or the end of its parent control. It changes how the top offset updates when the node moves or changes size. You can use one of the [enum Anchor] constants for convenience.
</member>
- <member name="auto_translate" type="bool" setter="set_auto_translate" getter="is_auto_translating" default="true" deprecated="Use [member Node.auto_translate_mode] instead.">
+ <member name="auto_translate" type="bool" setter="set_auto_translate" getter="is_auto_translating" deprecated="Use [member Node.auto_translate_mode] instead.">
Toggles if any text should automatically change to its translated version depending on the current locale.
</member>
<member name="clip_contents" type="bool" setter="set_clip_contents" getter="is_clipping_contents" default="false">
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 87ca0536b8..20ee65403c 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -656,6 +656,9 @@
<member name="interface/editor/font_subpixel_positioning" type="int" setter="" getter="">
The subpixel positioning mode to use when rendering editor font glyphs. This affects both the main and code fonts. [b]Disabled[/b] is the fastest to render and uses the least memory. [b]Auto[/b] only uses subpixel positioning for small font sizes (where the benefit is the most noticeable). [b]One Half of a Pixel[/b] and [b]One Quarter of a Pixel[/b] force the same subpixel positioning mode for all editor fonts, regardless of their size (with [b]One Quarter of a Pixel[/b] being the highest-quality option).
</member>
+ <member name="interface/editor/import_resources_when_unfocused" type="bool" setter="" getter="">
+ If [code]true[/code], (re)imports resources even if the editor window is unfocused or minimized. If [code]false[/code], resources are only (re)imported when the editor window is focused. This can be set to [code]true[/code] to speed up iteration by starting the import process earlier when saving files in the project folder. This also allows getting visual feedback on changes without having to click the editor window, which is useful with multi-monitor setups. The downside of setting this to [code]true[/code] is that it increases idle CPU usage and may steal CPU time from other applications when importing resources.
+ </member>
<member name="interface/editor/localize_settings" type="bool" setter="" getter="">
If [code]true[/code], setting names in the editor are localized when possible.
[b]Note:[/b] This setting affects most [EditorInspector]s in the editor UI, primarily Project Settings and Editor Settings. To control names displayed in the Inspector dock, use [member interface/inspector/default_property_name_style] instead.
@@ -866,8 +869,16 @@
Specify the multiplier to apply to the scale for the editor gizmo handles to improve usability on touchscreen devices.
[b]Note:[/b] Defaults to [code]1[/code] on non-touchscreen devices.
</member>
+ <member name="network/connection/engine_version_update_mode" type="int" setter="" getter="">
+ Specifies how the engine should check for updates.
+ - [b]Disable Update Checks[/b] will block the engine from checking updates (see also [member network/connection/network_mode]).
+ - [b]Check Newest Preview[/b] (default for preview versions) will check for the newest available development snapshot.
+ - [b]Check Newest Stable[/b] (default for stable versions) will check for the newest available stable version.
+ - [b]Check Newest Patch[/b] will check for the latest available stable version, but only within the same minor version. E.g. if your version is [code]4.3.stable[/code], you will be notified about [code]4.3.1.stable[/code], but not [code]4.4.stable[/code].
+ All update modes will ignore builds with different major versions (e.g. Godot 4 -&gt; Godot 5).
+ </member>
<member name="network/connection/network_mode" type="int" setter="" getter="">
- Determines whether online features are enabled in the editor, such as the Asset Library. Setting this property to "Offline" is recommended to limit editor's internet activity, especially if privacy is a concern.
+ Determines whether online features are enabled in the editor, such as the Asset Library or update checks. Disabling these online features helps alleviate privacy concerns by preventing the editor from making HTTP requests to the Godot website, GitHub, or third-party platforms hosting assets from the Asset Library.
</member>
<member name="network/debug/remote_host" type="String" setter="" getter="">
The address to listen to when starting the remote debugger. This can be set to [code]0.0.0.0[/code] to allow external clients to connect to the remote debugger (instead of restricting the remote debugger to connections from [code]localhost[/code]).
diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml
index 6b9c0bcc76..4052e48400 100644
--- a/doc/classes/FileAccess.xml
+++ b/doc/classes/FileAccess.xml
@@ -190,7 +190,7 @@
<method name="get_line" qualifiers="const">
<return type="String" />
<description>
- Returns the next line of the file as a [String].
+ Returns the next line of the file as a [String]. The returned string doesn't include newline ([code]\n[/code]) or carriage return ([code]\r[/code]) characters, but does include any other leading or trailing whitespace.
Text is interpreted as being UTF-8 encoded.
</description>
</method>
diff --git a/doc/classes/NavigationRegion2D.xml b/doc/classes/NavigationRegion2D.xml
index c490620fc0..31ce1dba4a 100644
--- a/doc/classes/NavigationRegion2D.xml
+++ b/doc/classes/NavigationRegion2D.xml
@@ -23,13 +23,6 @@
Bakes the [NavigationPolygon]. If [param on_thread] is set to [code]true[/code] (default), the baking is done on a separate thread.
</description>
</method>
- <method name="get_avoidance_layer_value" qualifiers="const">
- <return type="bool" />
- <param index="0" name="layer_number" type="int" />
- <description>
- Returns whether or not the specified layer of the [member avoidance_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
- </description>
- </method>
<method name="get_navigation_layer_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
@@ -61,14 +54,6 @@
Returns [code]true[/code] when the [NavigationPolygon] is being baked on a background thread.
</description>
</method>
- <method name="set_avoidance_layer_value">
- <return type="void" />
- <param index="0" name="layer_number" type="int" />
- <param index="1" name="value" type="bool" />
- <description>
- Based on [param value], enables or disables the specified layer in the [member avoidance_layers] bitmask, given a [param layer_number] between 1 and 32.
- </description>
- </method>
<method name="set_navigation_layer_value">
<return type="void" />
<param index="0" name="layer_number" type="int" />
@@ -86,12 +71,6 @@
</method>
</methods>
<members>
- <member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
- A bitfield determining all avoidance layers for the avoidance constrain.
- </member>
- <member name="constrain_avoidance" type="bool" setter="set_constrain_avoidance" getter="get_constrain_avoidance" default="false" experimental="When enabled, agents are known to get stuck on the navigation polygon corners and edges, especially at a high frame rate. Not recommended for use in production at this stage.">
- If [code]true[/code] constraints avoidance agent's with an avoidance mask bit that matches with a bit of the [member avoidance_layers] to the navigation polygon. Due to each navigation polygon outline creating an obstacle and each polygon edge creating an avoidance line constrain keep the navigation polygon shape as simple as possible for performance.
- </member>
<member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true">
Determines if the [NavigationRegion2D] is enabled or disabled.
</member>
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index aea4082dbe..37e64da8c8 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -970,8 +970,8 @@
</methods>
<members>
<member name="auto_translate_mode" type="int" setter="set_auto_translate_mode" getter="get_auto_translate_mode" enum="Node.AutoTranslateMode" default="0">
- Defines if any text should automatically change to its translated version depending on the current locale (for nodes such as [Label], [RichTextLabel], [Window], etc.). See [enum AutoTranslateMode].
- Also decides if the node's strings should be parsed for POT generation.
+ Defines if any text should automatically change to its translated version depending on the current locale (for nodes such as [Label], [RichTextLabel], [Window], etc.). Also decides if the node's strings should be parsed for POT generation.
+ [b]Note:[/b] For the root node, auto translate mode can also be set via [member ProjectSettings.internationalization/rendering/root_node_auto_translate].
</member>
<member name="editor_description" type="String" setter="set_editor_description" getter="get_editor_description" default="&quot;&quot;">
An optional description to the node. It will be displayed as a tooltip when hovering over the node in the editor's Scene dock.
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index de39901133..a130b06dba 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -408,11 +408,20 @@
[b]Note:[/b] On Web platforms, it is still possible to determine the host platform's OS with feature tags. See [method has_feature].
</description>
</method>
+ <method name="get_process_exit_code" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="pid" type="int" />
+ <description>
+ Returns the exit code of a spawned process once it has finished running (see [method is_process_running]).
+ Returns [code]-1[/code] if the [param pid] is not a PID of a spawned child process, the process is still running, or the method is not implemented for the current platform.
+ [b]Note:[/b] This method is implemented on Android, Linux, macOS and Windows.
+ </description>
+ </method>
<method name="get_process_id" qualifiers="const">
<return type="int" />
<description>
Returns the number used by the host machine to uniquely identify this application.
- [b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows.
+ [b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS, and Windows.
</description>
</method>
<method name="get_processor_count" qualifiers="const">
@@ -592,7 +601,7 @@
<param index="0" name="pid" type="int" />
<description>
Returns [code]true[/code] if the child process ID ([param pid]) is still running or [code]false[/code] if it has terminated. [param pid] must be a valid ID generated from [method create_process].
- [b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows.
+ [b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS, and Windows.
</description>
</method>
<method name="is_restart_on_exit_set" qualifiers="const">
diff --git a/doc/classes/PhysicsServer2DExtension.xml b/doc/classes/PhysicsServer2DExtension.xml
index 815fc742d1..41826ea7c2 100644
--- a/doc/classes/PhysicsServer2DExtension.xml
+++ b/doc/classes/PhysicsServer2DExtension.xml
@@ -185,7 +185,7 @@
<param index="1" name="pickable" type="bool" />
<description>
If set to [code]true[/code], allows the area with the given [RID] to detect mouse inputs when the mouse cursor is hovering on it.
- Overridable version of [PhysicsServer2D]'s internal [code]area_set_pickable[/code] method. Corresponds to [member PhysicsBody2D.input_pickable].
+ Overridable version of [PhysicsServer2D]'s internal [code]area_set_pickable[/code] method. Corresponds to [member CollisionObject2D.input_pickable].
</description>
</method>
<method name="_area_set_shape" qualifiers="virtual">
@@ -650,7 +650,7 @@
<param index="1" name="pickable" type="bool" />
<description>
If set to [code]true[/code], allows the body with the given [RID] to detect mouse inputs when the mouse cursor is hovering on it.
- Overridable version of [PhysicsServer2D]'s internal [code]body_set_pickable[/code] method. Corresponds to [member PhysicsBody2D.input_pickable].
+ Overridable version of [PhysicsServer2D]'s internal [code]body_set_pickable[/code] method. Corresponds to [member CollisionObject2D.input_pickable].
</description>
</method>
<method name="_body_set_shape" qualifiers="virtual">
diff --git a/doc/classes/Popup.xml b/doc/classes/Popup.xml
index c435f3d291..29b44a98f2 100644
--- a/doc/classes/Popup.xml
+++ b/doc/classes/Popup.xml
@@ -23,9 +23,4 @@
</description>
</signal>
</signals>
- <theme_items>
- <theme_item name="panel" data_type="style" type="StyleBox">
- Default [StyleBox] for the [Popup].
- </theme_item>
- </theme_items>
</class>
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index 7831ebd1b9..0f5687f091 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -768,6 +768,9 @@
<theme_item name="labeled_separator_right" data_type="style" type="StyleBox">
[StyleBox] for the right side of labeled separator. See [method add_separator].
</theme_item>
+ <theme_item name="panel" data_type="style" type="StyleBox">
+ [StyleBox] for the the background panel.
+ </theme_item>
<theme_item name="separator" data_type="style" type="StyleBox">
[StyleBox] used for the separators. See [method add_separator].
</theme_item>
diff --git a/doc/classes/PopupPanel.xml b/doc/classes/PopupPanel.xml
index b86972e8af..399e285402 100644
--- a/doc/classes/PopupPanel.xml
+++ b/doc/classes/PopupPanel.xml
@@ -8,4 +8,9 @@
</description>
<tutorials>
</tutorials>
+ <theme_items>
+ <theme_item name="panel" data_type="style" type="StyleBox">
+ [StyleBox] for the the background panel.
+ </theme_item>
+ </theme_items>
</class>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index a5aeee5bc4..1daa1b04e4 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -488,6 +488,9 @@
<member name="debug/gdscript/warnings/enable" type="bool" setter="" getter="" default="true">
If [code]true[/code], enables specific GDScript warnings (see [code]debug/gdscript/warnings/*[/code] settings). If [code]false[/code], disables all GDScript warnings.
</member>
+ <member name="debug/gdscript/warnings/enum_variable_without_default" type="int" setter="" getter="" default="1">
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a variable has an enum type but no explicit default value, but only if the enum does not contain [code]0[/code] as a valid value.
+ </member>
<member name="debug/gdscript/warnings/exclude_addons" type="bool" setter="" getter="" default="true">
If [code]true[/code], scripts in the [code]res://addons[/code] folder will not generate warnings.
</member>
@@ -637,6 +640,9 @@
<member name="debug/shader_language/warnings/formatting_error" type="bool" setter="" getter="" default="true">
When set to [code]true[/code], produces a warning upon encountering certain formatting errors. Currently this only checks for empty statements. More formatting errors may be added over time.
</member>
+ <member name="debug/shader_language/warnings/magic_position_write" type="bool" setter="" getter="" default="true">
+ When set to [code]true[/code], produces a warning when the shader contains [code]POSITION = vec4(vertex,[/code] as this was very common code written in Godot 4.2 and earlier that was paired with a QuadMesh to produce a full screen post processes pass. With the switch to reversed z in 4.3, this trick no longer works, as it implicitly relied on the [code]VERTEX.z[/code] being 0.
+ </member>
<member name="debug/shader_language/warnings/treat_warnings_as_errors" type="bool" setter="" getter="" default="false">
When set to [code]true[/code], warnings are treated as errors.
</member>
@@ -1436,6 +1442,10 @@
<member name="internationalization/rendering/force_right_to_left_layout_direction" type="bool" setter="" getter="" default="false">
Force layout direction and text writing direction to RTL for all controls.
</member>
+ <member name="internationalization/rendering/root_node_auto_translate" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], root node will use [constant Node.AUTO_TRANSLATE_MODE_ALWAYS], otherwise [constant Node.AUTO_TRANSLATE_MODE_DISABLED] will be used.
+ [b]Note:[/b] This property is only read when the project starts. To change the auto translate mode at runtime, set [member Node.auto_translate_mode] of [member SceneTree.root] instead.
+ </member>
<member name="internationalization/rendering/root_node_layout_direction" type="int" setter="" getter="" default="0">
Root node default layout direction.
</member>
diff --git a/doc/classes/ScriptEditor.xml b/doc/classes/ScriptEditor.xml
index 2c88ecd675..43ee4dda60 100644
--- a/doc/classes/ScriptEditor.xml
+++ b/doc/classes/ScriptEditor.xml
@@ -34,6 +34,35 @@
Returns an array with all [Script] objects which are currently open in editor.
</description>
</method>
+ <method name="goto_help">
+ <return type="void" />
+ <param index="0" name="topic" type="String" />
+ <description>
+ Opens help for the given topic. The [param topic] is an encoded string that controls which class, method, constant, signal, annotation, property, or theme item should be focused.
+ The supported [param topic] formats include [code]class_name:class[/code], [code]class_method:class:method[/code], [code]class_constant:class:constant[/code], [code]class_signal:class:signal[/code], [code]class_annotation:class:@annotation[/code], [code]class_property:class:property[/code], and [code]class_theme_item:class:item[/code], where [code]class[/code] is the class name, [code]method[/code] is the method name, [code]constant[/code] is the constant name, [code]signal[/code] is the signal name, [code]annotation[/code] is the annotation name, [code]property[/code] is the property name, and [code]item[/code] is the theme item.
+ [b]Examples:[/b]
+ [codeblock]
+ # Shows help for the Node class.
+ class_name:Node
+ # Shows help for the global min function.
+ # Global objects are accessible in the `@GlobalScope` namespace, shown here.
+ class_method:@GlobalScope:min
+ # Shows help for get_viewport in the Node class.
+ class_method:Node:get_viewport
+ # Shows help for the Input constant MOUSE_BUTTON_MIDDLE.
+ class_constant:Input:MOUSE_BUTTON_MIDDLE
+ # Shows help for the BaseButton signal pressed.
+ class_signal:BaseButton:pressed
+ # Shows help for the CanvasItem property visible.
+ class_property:CanvasItem:visible
+ # Shows help for the GDScript annotation export.
+ # Annotations should be prefixed with the `@` symbol in the descriptor, as shown here.
+ class_annotation:@GDScript:@export
+ # Shows help for the GraphNode theme item named panel_selected.
+ class_theme_item:GraphNode:panel_selected
+ [/codeblock]
+ </description>
+ </method>
<method name="goto_line">
<return type="void" />
<param index="0" name="line_number" type="int" />
diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml
index caa3097a6b..14d9c568d7 100644
--- a/doc/classes/Skeleton3D.xml
+++ b/doc/classes/Skeleton3D.xml
@@ -252,7 +252,7 @@
<param index="1" name="pose" type="Transform3D" />
<description>
Sets the global pose transform, [param pose], for the bone at [param bone_idx].
- [b]Note:[/b] If other bone poses have been changed, this method executes an update process and will cause performance to deteriorate. If you know that multiple global poses will be applied, consider using [method set_bone_pose] with precalculation.
+ [b]Note:[/b] If other bone poses have been changed, this method executes a dirty poses recalculation and will cause performance to deteriorate. If you know that multiple global poses will be applied, consider using [method set_bone_pose] with precalculation.
</description>
</method>
<method name="set_bone_global_pose_override" deprecated="">
@@ -355,15 +355,10 @@
<description>
</description>
</signal>
- <signal name="bone_pose_changed">
- <param index="0" name="bone_idx" type="int" />
- <description>
- Emitted when the bone at [param bone_idx] changes its transform/pose. This can be used to update other nodes that rely on bone positions.
- </description>
- </signal>
<signal name="pose_updated">
<description>
- Emitted when the pose is updated, after [constant NOTIFICATION_UPDATE_SKELETON] is received.
+ Emitted when the pose is updated.
+ [b]Note:[/b] During the update process, this signal is not fired, so modification by [SkeletonModifier3D] is not detected.
</description>
</signal>
<signal name="show_rest_only_changed">
@@ -371,11 +366,16 @@
Emitted when the value of [member show_rest_only] changes.
</description>
</signal>
+ <signal name="skeleton_updated">
+ <description>
+ Emitted when the final pose has been calculated will be applied to the skin in the update process.
+ This means that all [SkeletonModifier3D] processing is complete. In order to detect the completion of the processing of each [SkeletonModifier3D], use [signal SkeletonModifier3D.modification_processed].
+ </description>
+ </signal>
</signals>
<constants>
<constant name="NOTIFICATION_UPDATE_SKELETON" value="50">
- Notification received when this skeleton's pose needs to be updated.
- This notification is received [i]before[/i] the related [signal pose_updated] signal.
+ Notification received when this skeleton's pose needs to be updated. In that case, this is called only once per frame in a deferred process.
</constant>
<constant name="MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS" value="0" enum="ModifierCallbackModeProcess">
Set a flag to process modification during physics frames (see [constant Node.NOTIFICATION_INTERNAL_PHYSICS_PROCESS]).
diff --git a/drivers/egl/egl_manager.cpp b/drivers/egl/egl_manager.cpp
index 10c2119260..4eddfd027b 100644
--- a/drivers/egl/egl_manager.cpp
+++ b/drivers/egl/egl_manager.cpp
@@ -260,21 +260,6 @@ void EGLManager::release_current() {
eglMakeCurrent(current_display.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
-void EGLManager::make_current() {
- if (!current_window) {
- return;
- }
-
- if (!current_window->initialized) {
- WARN_PRINT("Current OpenGL window is uninitialized!");
- return;
- }
-
- GLDisplay &current_display = displays[current_window->gldisplay_id];
-
- eglMakeCurrent(current_display.egl_display, current_window->egl_surface, current_window->egl_surface, current_display.egl_context);
-}
-
void EGLManager::swap_buffers() {
if (!current_window) {
return;
diff --git a/drivers/egl/egl_manager.h b/drivers/egl/egl_manager.h
index 61d3289b2d..83779349f0 100644
--- a/drivers/egl/egl_manager.h
+++ b/drivers/egl/egl_manager.h
@@ -98,7 +98,6 @@ public:
void window_destroy(DisplayServer::WindowID p_window_id);
void release_current();
- void make_current();
void swap_buffers();
void window_make_current(DisplayServer::WindowID p_window_id);
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index ecb563214c..bc1af86938 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -3112,39 +3112,47 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
if (pass == 0) {
spec_constants |= SceneShaderGLES3::BASE_PASS;
- if (inst->omni_light_gl_cache.size() == 0) {
- spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI;
- }
- if (inst->spot_light_gl_cache.size() == 0) {
+ if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_UNSHADED) {
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI;
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT;
- }
-
- if (p_render_data->directional_light_count == p_render_data->directional_shadow_count) {
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
- }
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
+ } else {
+ if (inst->omni_light_gl_cache.size() == 0) {
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI;
+ }
- if (inst->reflection_probe_rid_cache.size() == 0) {
- // We don't have any probes.
- spec_constants |= SceneShaderGLES3::DISABLE_REFLECTION_PROBE;
- } else if (inst->reflection_probe_rid_cache.size() > 1) {
- // We have a second probe.
- spec_constants |= SceneShaderGLES3::SECOND_REFLECTION_PROBE;
- }
+ if (inst->spot_light_gl_cache.size() == 0) {
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT;
+ }
- if (inst->lightmap_instance.is_valid()) {
- spec_constants |= SceneShaderGLES3::USE_LIGHTMAP;
+ if (p_render_data->directional_light_count == p_render_data->directional_shadow_count) {
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
+ }
- GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance);
- GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap);
+ if (inst->reflection_probe_rid_cache.size() == 0) {
+ // We don't have any probes.
+ spec_constants |= SceneShaderGLES3::DISABLE_REFLECTION_PROBE;
+ } else if (inst->reflection_probe_rid_cache.size() > 1) {
+ // We have a second probe.
+ spec_constants |= SceneShaderGLES3::SECOND_REFLECTION_PROBE;
+ }
- if (lm->uses_spherical_harmonics) {
- spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP;
+ if (inst->lightmap_instance.is_valid()) {
+ spec_constants |= SceneShaderGLES3::USE_LIGHTMAP;
+
+ GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance);
+ GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap);
+
+ if (lm->uses_spherical_harmonics) {
+ spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP;
+ }
+ } else if (inst->lightmap_sh) {
+ spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE;
+ } else {
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
}
- } else if (inst->lightmap_sh) {
- spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE;
- } else {
- spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
}
} else {
// Only base pass uses the radiance map.
diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp
index 09878aaee4..fee90599e0 100644
--- a/drivers/gles3/storage/particles_storage.cpp
+++ b/drivers/gles3/storage/particles_storage.cpp
@@ -135,7 +135,6 @@ bool ParticlesStorage::particles_get_emitting(RID p_particles) {
return false;
}
- ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer.");
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, false);
@@ -380,10 +379,6 @@ void ParticlesStorage::particles_request_process(RID p_particles) {
}
AABB ParticlesStorage::particles_get_current_aabb(RID p_particles) {
- if (RSG::threaded) {
- WARN_PRINT_ONCE("Calling this function with threaded rendering enabled stalls the renderer, use with care.");
- }
-
const Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, AABB());
@@ -1207,7 +1202,6 @@ Dependency *ParticlesStorage::particles_get_dependency(RID p_particles) const {
}
bool ParticlesStorage::particles_is_inactive(RID p_particles) const {
- ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer.");
const Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, false);
return !particles->emitting && particles->inactive;
diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp
index 46efb45934..2a85a81b91 100644
--- a/drivers/unix/dir_access_unix.cpp
+++ b/drivers/unix/dir_access_unix.cpp
@@ -419,7 +419,7 @@ Error DirAccessUnix::remove(String p_path) {
return FAILED;
}
- if (S_ISDIR(flags.st_mode)) {
+ if (S_ISDIR(flags.st_mode) && !is_link(p_path)) {
return ::rmdir(p_path.utf8().get_data()) == 0 ? OK : FAILED;
} else {
return ::unlink(p_path.utf8().get_data()) == 0 ? OK : FAILED;
@@ -435,7 +435,7 @@ bool DirAccessUnix::is_link(String p_file) {
struct stat flags = {};
if ((lstat(p_file.utf8().get_data(), &flags) != 0)) {
- return FAILED;
+ return false;
}
return S_ISLNK(flags.st_mode);
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index ffeb5ad752..1a8cd53486 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -168,11 +168,13 @@ void OS_Unix::initialize_core() {
NetSocketPosix::make_default();
IPUnix::make_default();
+ process_map = memnew((HashMap<ProcessID, ProcessInfo>));
_setup_clock();
}
void OS_Unix::finalize_core() {
+ memdelete(process_map);
NetSocketPosix::cleanup();
}
@@ -582,6 +584,11 @@ Dictionary OS_Unix::execute_with_pipe(const String &p_path, const List<String> &
err_pipe.instantiate();
err_pipe->open_existing(pipe_err[0], 0);
+ ProcessInfo pi;
+ process_map_mutex.lock();
+ process_map->insert(pid, pi);
+ process_map_mutex.unlock();
+
ret["stdio"] = main_pipe;
ret["stderr"] = err_pipe;
ret["pid"] = pid;
@@ -698,6 +705,11 @@ Error OS_Unix::create_process(const String &p_path, const List<String> &p_argume
raise(SIGKILL);
}
+ ProcessInfo pi;
+ process_map_mutex.lock();
+ process_map->insert(pid, pi);
+ process_map_mutex.unlock();
+
if (r_child_id) {
*r_child_id = pid;
}
@@ -720,14 +732,45 @@ int OS_Unix::get_process_id() const {
}
bool OS_Unix::is_process_running(const ProcessID &p_pid) const {
+ MutexLock lock(process_map_mutex);
+ const ProcessInfo *pi = process_map->getptr(p_pid);
+
+ if (pi && !pi->is_running) {
+ return false;
+ }
+
int status = 0;
if (waitpid(p_pid, &status, WNOHANG) != 0) {
+ if (pi) {
+ pi->is_running = false;
+ pi->exit_code = status;
+ }
return false;
}
return true;
}
+int OS_Unix::get_process_exit_code(const ProcessID &p_pid) const {
+ MutexLock lock(process_map_mutex);
+ const ProcessInfo *pi = process_map->getptr(p_pid);
+
+ if (pi && !pi->is_running) {
+ return pi->exit_code;
+ }
+
+ int status = 0;
+ if (waitpid(p_pid, &status, WNOHANG) != 0) {
+ status = WIFEXITED(status) ? WEXITSTATUS(status) : status;
+ if (pi) {
+ pi->is_running = false;
+ pi->exit_code = status;
+ }
+ return status;
+ }
+ return -1;
+}
+
String OS_Unix::get_locale() const {
if (!has_environment("LANG")) {
return "en";
diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h
index 2936c28797..a107e7a0e3 100644
--- a/drivers/unix/os_unix.h
+++ b/drivers/unix/os_unix.h
@@ -37,6 +37,13 @@
#include "drivers/unix/ip_unix.h"
class OS_Unix : public OS {
+ struct ProcessInfo {
+ mutable bool is_running = true;
+ mutable int exit_code = -1;
+ };
+ HashMap<ProcessID, ProcessInfo> *process_map = nullptr;
+ Mutex process_map_mutex;
+
protected:
// UNIX only handles the core functions.
// inheriting platforms under unix (eg. X11) should handle the rest
@@ -81,6 +88,7 @@ public:
virtual Error kill(const ProcessID &p_pid) override;
virtual int get_process_id() const override;
virtual bool is_process_running(const ProcessID &p_pid) const override;
+ virtual int get_process_exit_code(const ProcessID &p_pid) const override;
virtual bool has_environment(const String &p_var) const override;
virtual String get_environment(const String &p_var) const override;
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index bfad38d341..779080786a 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -196,7 +196,7 @@ bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col)
text_editor->set_search_flags(p_flags);
}
- _update_matches_label();
+ _update_matches_display();
return pos.x != -1;
}
@@ -434,7 +434,7 @@ void FindReplaceBar::_update_results_count() {
}
}
-void FindReplaceBar::_update_matches_label() {
+void FindReplaceBar::_update_matches_display() {
if (search_text->get_text().is_empty() || results_count == -1) {
matches_label->hide();
} else {
@@ -450,6 +450,10 @@ void FindReplaceBar::_update_matches_label() {
matches_label->set_text(vformat(TTRN("%d of %d match", "%d of %d matches", results_count), results_count_to_current, results_count));
}
}
+ find_prev->set_disabled(results_count < 1);
+ find_next->set_disabled(results_count < 1);
+ replace->set_disabled(search_text->get_text().is_empty());
+ replace_all->set_disabled(search_text->get_text().is_empty());
}
bool FindReplaceBar::search_current() {
@@ -517,13 +521,16 @@ void FindReplaceBar::_hide_bar(bool p_force_focus) {
hide();
}
-void FindReplaceBar::_show_search(bool p_focus_replace, bool p_show_only) {
+void FindReplaceBar::_show_search(bool p_with_replace, bool p_show_only) {
show();
if (p_show_only) {
return;
}
- if (p_focus_replace) {
+ const bool on_one_line = text_editor->has_selection(0) && text_editor->get_selection_from_line(0) == text_editor->get_selection_to_line(0);
+ const bool focus_replace = p_with_replace && on_one_line;
+
+ if (focus_replace) {
search_text->deselect();
callable_mp((Control *)replace_text, &Control::grab_focus).call_deferred();
} else {
@@ -531,14 +538,14 @@ void FindReplaceBar::_show_search(bool p_focus_replace, bool p_show_only) {
callable_mp((Control *)search_text, &Control::grab_focus).call_deferred();
}
- if (text_editor->has_selection(0) && !is_selection_only()) {
+ if (on_one_line) {
search_text->set_text(text_editor->get_selected_text(0));
result_line = text_editor->get_selection_from_line();
result_col = text_editor->get_selection_from_column();
}
if (!get_search_text().is_empty()) {
- if (p_focus_replace) {
+ if (focus_replace) {
replace_text->select_all();
replace_text->set_caret_column(replace_text->get_text().length());
} else {
@@ -568,9 +575,9 @@ void FindReplaceBar::popup_replace() {
hbc_option_replace->show();
}
- selection_only->set_pressed((text_editor->has_selection(0) && text_editor->get_selection_from_line(0) < text_editor->get_selection_to_line(0)));
+ selection_only->set_pressed(text_editor->has_selection(0) && text_editor->get_selection_from_line(0) < text_editor->get_selection_to_line(0));
- _show_search(is_visible() || text_editor->has_selection(0));
+ _show_search(true, false);
}
void FindReplaceBar::_search_options_changed(bool p_pressed) {
@@ -666,7 +673,7 @@ void FindReplaceBar::set_text_edit(CodeTextEditor *p_text_editor) {
text_editor->connect("text_changed", callable_mp(this, &FindReplaceBar::_editor_text_changed));
_update_results_count();
- _update_matches_label();
+ _update_matches_display();
}
void FindReplaceBar::_bind_methods() {
@@ -700,6 +707,8 @@ FindReplaceBar::FindReplaceBar() {
// Search toolbar
search_text = memnew(LineEdit);
vbc_lineedit->add_child(search_text);
+ search_text->set_placeholder(TTR("Find"));
+ search_text->set_tooltip_text(TTR("Find"));
search_text->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
search_text->connect("text_changed", callable_mp(this, &FindReplaceBar::_search_text_changed));
search_text->connect("text_submitted", callable_mp(this, &FindReplaceBar::_search_text_submitted));
@@ -711,12 +720,14 @@ FindReplaceBar::FindReplaceBar() {
find_prev = memnew(Button);
find_prev->set_flat(true);
+ find_prev->set_tooltip_text(TTR("Previous Match"));
hbc_button_search->add_child(find_prev);
find_prev->set_focus_mode(FOCUS_NONE);
find_prev->connect("pressed", callable_mp(this, &FindReplaceBar::search_prev));
find_next = memnew(Button);
find_next->set_flat(true);
+ find_next->set_tooltip_text(TTR("Next Match"));
hbc_button_search->add_child(find_next);
find_next->set_focus_mode(FOCUS_NONE);
find_next->connect("pressed", callable_mp(this, &FindReplaceBar::search_next));
@@ -736,6 +747,8 @@ FindReplaceBar::FindReplaceBar() {
// Replace toolbar
replace_text = memnew(LineEdit);
vbc_lineedit->add_child(replace_text);
+ replace_text->set_placeholder(TTR("Replace"));
+ replace_text->set_tooltip_text(TTR("Replace"));
replace_text->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
replace_text->connect("text_submitted", callable_mp(this, &FindReplaceBar::_replace_text_submitted));
replace_text->connect("focus_exited", callable_mp(this, &FindReplaceBar::_focus_lost));
@@ -758,6 +771,7 @@ FindReplaceBar::FindReplaceBar() {
hide_button = memnew(TextureButton);
add_child(hide_button);
+ hide_button->set_tooltip_text(TTR("Hide"));
hide_button->set_focus_mode(FOCUS_NONE);
hide_button->connect("pressed", callable_mp(this, &FindReplaceBar::_hide_bar).bind(false));
hide_button->set_v_size_flags(SIZE_SHRINK_CENTER);
diff --git a/editor/code_editor.h b/editor/code_editor.h
index 87031b672c..64b13b9006 100644
--- a/editor/code_editor.h
+++ b/editor/code_editor.h
@@ -96,9 +96,9 @@ class FindReplaceBar : public HBoxContainer {
void _get_search_from(int &r_line, int &r_col, bool p_is_searching_next = false);
void _update_results_count();
- void _update_matches_label();
+ void _update_matches_display();
- void _show_search(bool p_focus_replace = false, bool p_show_only = false);
+ void _show_search(bool p_with_replace, bool p_show_only);
void _hide_bar(bool p_force_focus = false);
void _editor_text_changed();
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 4c66068953..f7914d3aaa 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -516,7 +516,14 @@ void CreateDialog::select_type(const String &p_type, bool p_center_on_item) {
favorite->set_disabled(false);
favorite->set_pressed(favorite_list.has(p_type));
- get_ok_button()->set_disabled(false);
+
+ if (to_select->get_meta("__instantiable", true)) {
+ get_ok_button()->set_disabled(false);
+ get_ok_button()->set_tooltip_text(String());
+ } else {
+ get_ok_button()->set_disabled(true);
+ get_ok_button()->set_tooltip_text(TTR("The selected class can't be instantiated."));
+ }
}
void CreateDialog::select_base() {
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 51e1c27070..14190a43a5 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -1664,8 +1664,13 @@ void EditorFileSystem::_queue_update_script_class(const String &p_path) {
}
void EditorFileSystem::_update_scene_groups() {
- update_scene_mutex.lock();
+ EditorProgress *ep = nullptr;
+ if (update_scene_paths.size() > 1) {
+ ep = memnew(EditorProgress("update_scene_groups", TTR("Update Scene Groups"), update_scene_paths.size()));
+ }
+ int step_count = 0;
+ update_scene_mutex.lock();
for (const String &path : update_scene_paths) {
ProjectSettings::get_singleton()->remove_scene_groups_cache(path);
@@ -1681,8 +1686,13 @@ void EditorFileSystem::_update_scene_groups() {
if (!scene_groups.is_empty()) {
ProjectSettings::get_singleton()->add_scene_groups_cache(path, scene_groups);
}
+
+ if (ep) {
+ ep->step(TTR("Updating Scene Groups..."), step_count++);
+ }
}
+ memdelete_notnull(ep);
update_scene_paths.clear();
update_scene_mutex.unlock();
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 7fded0b807..1f7505633b 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -2324,7 +2324,8 @@ void EditorHelp::_help_callback(const String &p_topic) {
}
if (class_desc->is_ready()) {
- callable_mp(class_desc, &RichTextLabel::scroll_to_paragraph).call_deferred(line);
+ // call_deferred() is not enough.
+ class_desc->connect("draw", callable_mp(class_desc, &RichTextLabel::scroll_to_paragraph).bind(line), CONNECT_ONE_SHOT | CONNECT_DEFERRED);
} else {
scroll_to = line;
}
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 7919d61f26..aed1462eb6 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -3220,12 +3220,13 @@ void EditorInspector::update_tree() {
}
// Search for the doc path in the cache.
- HashMap<StringName, HashMap<StringName, String>>::Iterator E = doc_path_cache.find(classname);
+ HashMap<StringName, HashMap<StringName, DocCacheInfo>>::Iterator E = doc_cache.find(classname);
if (E) {
- HashMap<StringName, String>::Iterator F = E->value.find(propname);
+ HashMap<StringName, DocCacheInfo>::Iterator F = E->value.find(propname);
if (F) {
found = true;
- doc_path = F->value;
+ doc_path = F->value.doc_path;
+ theme_item_name = F->value.theme_item_name;
}
}
@@ -3246,23 +3247,22 @@ void EditorInspector::update_tree() {
theme_item_name = F->value.theme_properties[i].name;
}
}
-
- if (is_native_class) {
- doc_path_cache[classname][propname] = doc_path;
- }
} else {
for (int i = 0; i < F->value.properties.size(); i++) {
String doc_path_current = "class_property:" + F->value.name + ":" + F->value.properties[i].name;
if (F->value.properties[i].name == propname.operator String()) {
doc_path = doc_path_current;
}
-
- if (is_native_class) {
- doc_path_cache[classname][propname] = doc_path;
- }
}
}
+ if (is_native_class) {
+ DocCacheInfo cache_info;
+ cache_info.doc_path = doc_path;
+ cache_info.theme_item_name = theme_item_name;
+ doc_cache[classname][propname] = cache_info;
+ }
+
if (!doc_path.is_empty() || F->value.inherits.is_empty()) {
break;
}
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index 8c55950a2b..eff4f9caa5 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -505,7 +505,12 @@ class EditorInspector : public ScrollContainer {
int property_focusable;
int update_scroll_request;
- HashMap<StringName, HashMap<StringName, String>> doc_path_cache;
+ struct DocCacheInfo {
+ String doc_path;
+ String theme_item_name;
+ };
+
+ HashMap<StringName, HashMap<StringName, DocCacheInfo>> doc_cache;
HashSet<StringName> restart_request_props;
HashMap<String, String> custom_property_descriptions;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 4fb1a86ce2..5b24fb1559 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -771,6 +771,12 @@ void EditorNode::_notification(int p_what) {
EditorFileDialog::set_default_display_mode((EditorFileDialog::DisplayMode)EDITOR_GET("filesystem/file_dialog/display_mode").operator int());
}
+ if (EDITOR_GET("interface/editor/import_resources_when_unfocused")) {
+ scan_changes_timer->start();
+ } else {
+ scan_changes_timer->stop();
+ }
+
follow_system_theme = EDITOR_GET("interface/theme/follow_system_theme");
use_system_accent_color = EDITOR_GET("interface/theme/use_system_accent_color");
@@ -1445,6 +1451,9 @@ void EditorNode::_dialog_display_load_error(String p_file, Error p_error) {
case ERR_FILE_NOT_FOUND: {
show_accept(vformat(TTR("Missing file '%s' or one of its dependencies."), p_file.get_file()), TTR("OK"));
} break;
+ case ERR_FILE_UNRECOGNIZED: {
+ show_accept(vformat(TTR("File '%s' is saved in a format that is newer than the formats supported by this version of Godot, so it can't be opened."), p_file.get_file()), TTR("OK"));
+ } break;
default: {
show_accept(vformat(TTR("Error while loading file '%s'."), p_file.get_file()), TTR("OK"));
} break;
@@ -2355,7 +2364,13 @@ static bool overrides_external_editor(Object *p_object) {
void EditorNode::_add_to_history(const Object *p_object, const String &p_property, bool p_inspector_only) {
ObjectID id = p_object->get_instance_id();
- if (id != editor_history.get_current()) {
+ ObjectID history_id = editor_history.get_current();
+ if (id != history_id) {
+ const MultiNodeEdit *multi_node_edit = Object::cast_to<const MultiNodeEdit>(p_object);
+ const MultiNodeEdit *history_multi_node_edit = Object::cast_to<const MultiNodeEdit>(ObjectDB::get_instance(history_id));
+ if (multi_node_edit && history_multi_node_edit && multi_node_edit->is_same_selection(history_multi_node_edit)) {
+ return;
+ }
if (p_inspector_only) {
editor_history.add_object(id, String(), true);
} else if (p_property.is_empty()) {
@@ -2449,6 +2464,7 @@ void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update
if (current_node->is_inside_tree()) {
NodeDock::get_singleton()->set_node(current_node);
SceneTreeDock::get_singleton()->set_selected(current_node);
+ SceneTreeDock::get_singleton()->set_selection({ current_node });
InspectorDock::get_singleton()->update(current_node);
if (!inspector_only && !skip_main_plugin) {
skip_main_plugin = stay_in_script_editor_on_node_selected && !ScriptEditor::get_singleton()->is_editor_floating() && ScriptEditor::get_singleton()->is_visible_in_tree();
@@ -2470,13 +2486,13 @@ void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update
} else {
Node *selected_node = nullptr;
+ Vector<Node *> multi_nodes;
if (current_obj->is_class("MultiNodeEdit")) {
Node *scene = get_edited_scene();
if (scene) {
MultiNodeEdit *multi_node_edit = Object::cast_to<MultiNodeEdit>(current_obj);
int node_count = multi_node_edit->get_node_count();
if (node_count > 0) {
- List<Node *> multi_nodes;
for (int node_index = 0; node_index < node_count; ++node_index) {
Node *node = scene->get_node(multi_node_edit->get_node(node_index));
if (node) {
@@ -2486,7 +2502,7 @@ void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update
if (!multi_nodes.is_empty()) {
// Pick the top-most node.
multi_nodes.sort_custom<Node::Comparator>();
- selected_node = multi_nodes.front()->get();
+ selected_node = multi_nodes[0];
}
}
}
@@ -2495,6 +2511,7 @@ void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update
InspectorDock::get_inspector_singleton()->edit(current_obj);
NodeDock::get_singleton()->set_node(nullptr);
SceneTreeDock::get_singleton()->set_selected(selected_node);
+ SceneTreeDock::get_singleton()->set_selection(multi_nodes);
InspectorDock::get_singleton()->update(nullptr);
}
@@ -3055,8 +3072,8 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
case HELP_DOCS: {
OS::get_singleton()->shell_open(VERSION_DOCS_URL "/");
} break;
- case HELP_QA: {
- OS::get_singleton()->shell_open("https://godotengine.org/qa/");
+ case HELP_FORUM: {
+ OS::get_singleton()->shell_open("https://forum.godotengine.org/");
} break;
case HELP_REPORT_A_BUG: {
OS::get_singleton()->shell_open("https://github.com/godotengine/godot/issues");
@@ -3832,6 +3849,8 @@ void EditorNode::_set_current_scene_nocheck(int p_idx) {
if (tabs_to_close.is_empty()) {
callable_mp(this, &EditorNode::_set_main_scene_state).call_deferred(state, get_edited_scene()); // Do after everything else is done setting up.
}
+
+ _update_undo_redo_allowed();
}
void EditorNode::setup_color_picker(ColorPicker *p_picker) {
@@ -3917,14 +3936,18 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
}
int prev = editor_data.get_edited_scene();
- int idx = editor_data.add_edited_scene(-1);
+ int idx = prev;
- if (!editor_data.get_edited_scene_root() && editor_data.get_edited_scene_count() == 2) {
- _remove_edited_scene();
- } else if (p_silent_change_tab) {
- _set_current_scene_nocheck(idx);
+ if (prev == -1 || editor_data.get_edited_scene_root() || !editor_data.get_scene_path(prev).is_empty()) {
+ idx = editor_data.add_edited_scene(-1);
+
+ if (p_silent_change_tab) {
+ _set_current_scene_nocheck(idx);
+ } else {
+ _set_current_scene(idx);
+ }
} else {
- _set_current_scene(idx);
+ EditorUndoRedoManager::get_singleton()->clear_history(false, editor_data.get_current_edited_scene_history_id());
}
dependency_errors.clear();
@@ -3941,7 +3964,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
dependency_error->show(DependencyErrorDialog::MODE_SCENE, lpath, errors);
opening_prev = false;
- if (prev != -1) {
+ if (prev != -1 && prev != idx) {
_set_current_scene(prev);
editor_data.remove_scene(idx);
}
@@ -3952,7 +3975,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
_dialog_display_load_error(lpath, err);
opening_prev = false;
- if (prev != -1) {
+ if (prev != -1 && prev != idx) {
_set_current_scene(prev);
editor_data.remove_scene(idx);
}
@@ -3988,7 +4011,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
sdata.unref();
_dialog_display_load_error(lpath, ERR_FILE_CORRUPT);
opening_prev = false;
- if (prev != -1) {
+ if (prev != -1 && prev != idx) {
_set_current_scene(prev);
editor_data.remove_scene(idx);
}
@@ -4014,10 +4037,6 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
_load_editor_plugin_states_from_config(editor_state_cf);
}
- _update_title();
- scene_tabs->update_scene_tabs();
- _add_to_recent_scenes(lpath);
-
if (editor_folding.has_folding_data(lpath)) {
editor_folding.load_scene_folding(new_scene, lpath);
} else if (EDITOR_GET("interface/inspector/auto_unfold_foreign_scenes")) {
@@ -4057,6 +4076,14 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
save_editor_layout_delayed();
}
+ if (p_set_inherited) {
+ EditorUndoRedoManager::get_singleton()->set_history_as_unsaved(editor_data.get_current_edited_scene_history_id());
+ }
+
+ _update_title();
+ scene_tabs->update_scene_tabs();
+ _add_to_recent_scenes(lpath);
+
return OK;
}
@@ -6574,6 +6601,12 @@ EditorNode::EditorNode() {
editor_layout_save_delay_timer->set_one_shot(true);
editor_layout_save_delay_timer->connect("timeout", callable_mp(this, &EditorNode::_save_editor_layout));
+ scan_changes_timer = memnew(Timer);
+ scan_changes_timer->set_wait_time(0.5);
+ scan_changes_timer->set_autostart(EDITOR_GET("interface/editor/import_resources_when_unfocused"));
+ scan_changes_timer->connect("timeout", callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::scan_changes));
+ add_child(scan_changes_timer);
+
top_split = memnew(VSplitContainer);
center_split->add_child(top_split);
top_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
@@ -6642,6 +6675,8 @@ EditorNode::EditorNode() {
main_menu->set_menu_tooltip(0, TTR("Operations with scene files."));
accept = memnew(AcceptDialog);
+ accept->set_autowrap(true);
+ accept->set_min_size(Vector2i(600, 0));
accept->set_unparent_when_invisible(true);
save_accept = memnew(AcceptDialog);
@@ -6886,7 +6921,7 @@ EditorNode::EditorNode() {
help_menu->add_icon_shortcut(theme->get_icon(SNAME("HelpSearch"), EditorStringName(EditorIcons)), ED_GET_SHORTCUT("editor/editor_help"), HELP_SEARCH);
help_menu->add_separator();
help_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/online_docs", TTR("Online Documentation")), HELP_DOCS);
- help_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/q&a", TTR("Questions & Answers")), HELP_QA);
+ help_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/forum", TTR("Forum")), HELP_FORUM);
help_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/community", TTR("Community")), HELP_COMMUNITY);
help_menu->add_separator();
help_menu->add_icon_shortcut(theme->get_icon(SNAME("ActionCopy"), EditorStringName(EditorIcons)), ED_SHORTCUT_AND_COMMAND("editor/copy_system_info", TTR("Copy System Info")), HELP_COPY_SYSTEM_INFO);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 96acc8bf46..ad0a7ec5e0 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -226,7 +226,7 @@ private:
HELP_SEARCH,
HELP_COMMAND_PALETTE,
HELP_DOCS,
- HELP_QA,
+ HELP_FORUM,
HELP_REPORT_A_BUG,
HELP_COPY_SYSTEM_INFO,
HELP_SUGGEST_A_FEATURE,
@@ -420,6 +420,7 @@ private:
EditorDockManager *editor_dock_manager = nullptr;
Timer *editor_layout_save_delay_timer = nullptr;
+ Timer *scan_changes_timer = nullptr;
Button *distraction_free = nullptr;
EditorBottomPanel *bottom_panel = nullptr;
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 32d581a26e..452715a577 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -49,6 +49,7 @@
#include "editor/editor_paths.h"
#include "editor/editor_property_name_processor.h"
#include "editor/editor_translation.h"
+#include "editor/engine_update_label.h"
#include "scene/gui/color_picker.h"
#include "scene/main/node.h"
#include "scene/main/scene_tree.h"
@@ -413,6 +414,14 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/editor_screen", -2, ed_screen_hints)
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/project_manager_screen", -2, ed_screen_hints)
+ {
+ EngineUpdateLabel::UpdateMode default_update_mode = EngineUpdateLabel::UpdateMode::NEWEST_UNSTABLE;
+ if (String(VERSION_STATUS) == String("stable")) {
+ default_update_mode = EngineUpdateLabel::UpdateMode::NEWEST_STABLE;
+ }
+ EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "network/connection/engine_version_update_mode", int(default_update_mode), "Disable Update Checks,Check Newest Preview,Check Newest Stable,Check Newest Patch"); // Uses EngineUpdateLabel::UpdateMode.
+ }
+
_initial_set("interface/editor/debug/enable_pseudolocalization", false);
set_restart_if_changed("interface/editor/debug/enable_pseudolocalization", true);
// Use pseudolocalization in editor.
@@ -436,7 +445,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/main_font", "", "*.ttf,*.otf,*.woff,*.woff2,*.pfb,*.pfm")
EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/main_font_bold", "", "*.ttf,*.otf,*.woff,*.woff2,*.pfb,*.pfm")
EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/code_font", "", "*.ttf,*.otf,*.woff,*.woff2,*.pfb,*.pfm")
-
_initial_set("interface/editor/separate_distraction_mode", false);
_initial_set("interface/editor/automatically_open_screenshots", true);
EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/single_window_mode", false, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
@@ -462,6 +470,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// being focused again, so this should be used at the user's discretion.
EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_RANGE, "interface/editor/unfocused_low_processor_mode_sleep_usec", 100000, "1,1000000,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
+ EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/import_resources_when_unfocused", false, "")
+
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/vsync_mode", 1, "Disabled,Enabled,Adaptive,Mailbox")
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/update_continuously", false, "")
diff --git a/editor/engine_update_label.cpp b/editor/engine_update_label.cpp
new file mode 100644
index 0000000000..0b20738e99
--- /dev/null
+++ b/editor/engine_update_label.cpp
@@ -0,0 +1,344 @@
+/**************************************************************************/
+/* engine_update_label.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "engine_update_label.h"
+
+#include "core/os/time.h"
+#include "editor/editor_settings.h"
+#include "editor/editor_string_names.h"
+#include "editor/themes/editor_scale.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/main/http_request.h"
+
+bool EngineUpdateLabel::_can_check_updates() const {
+ return int(EDITOR_GET("network/connection/network_mode")) == EditorSettings::NETWORK_ONLINE &&
+ UpdateMode(int(EDITOR_GET("network/connection/engine_version_update_mode"))) != UpdateMode::DISABLED;
+}
+
+void EngineUpdateLabel::_check_update() {
+ checked_update = true;
+ _set_status(UpdateStatus::BUSY);
+ http->request("https://raw.githubusercontent.com/godotengine/godot-website/master/_data/versions.yml");
+}
+
+void EngineUpdateLabel::_http_request_completed(int p_result, int p_response_code, const PackedStringArray &p_headers, const PackedByteArray &p_body) {
+ if (p_result != OK) {
+ _set_status(UpdateStatus::ERROR);
+ _set_message(vformat(TTR("Failed to check for updates. Error: %d."), p_result), theme_cache.error_color);
+ return;
+ }
+
+ if (p_response_code != 200) {
+ _set_status(UpdateStatus::ERROR);
+ _set_message(vformat(TTR("Failed to check for updates. Response code: %d."), p_response_code), theme_cache.error_color);
+ return;
+ }
+
+ PackedStringArray lines;
+ {
+ String s;
+ const uint8_t *r = p_body.ptr();
+ s.parse_utf8((const char *)r, p_body.size());
+ lines = s.split("\n");
+ }
+
+ UpdateMode update_mode = UpdateMode(int(EDITOR_GET("network/connection/engine_version_update_mode")));
+ bool stable_only = update_mode == UpdateMode::NEWEST_STABLE || update_mode == UpdateMode::NEWEST_PATCH;
+
+ const Dictionary version_info = Engine::get_singleton()->get_version_info();
+ int current_major = version_info["major"];
+ int current_minor = version_info["minor"];
+ int current_patch = version_info["patch"];
+
+ int current_version_line = -1;
+ for (int i = 0; i < lines.size(); i++) {
+ const String &line = lines[i];
+ if (!line.begins_with("- name")) {
+ continue;
+ }
+
+ const String version_string = _extract_sub_string(line);
+ const PackedStringArray version_bits = version_string.split(".");
+
+ if (version_bits.size() < 2) {
+ continue;
+ }
+
+ int minor = version_bits[1].to_int();
+ if (version_bits[0].to_int() != current_major || minor < current_minor) {
+ continue;
+ }
+
+ int patch = 0;
+ if (version_bits.size() >= 3) {
+ patch = version_bits[2].to_int();
+ }
+
+ if (minor == current_minor && patch < current_patch) {
+ continue;
+ }
+
+ if (update_mode == UpdateMode::NEWEST_PATCH && minor > current_minor) {
+ continue;
+ }
+
+ if (minor > current_minor || patch > current_patch) {
+ String version_type = _extract_sub_string(lines[i + 1]);
+ if (stable_only && _get_version_type(version_type, nullptr) != VersionType::STABLE) {
+ continue;
+ }
+
+ found_version = version_string;
+ found_version += "-" + version_type;
+ break;
+ } else if (minor == current_minor && patch == current_patch) {
+ current_version_line = i;
+ found_version = version_string;
+ break;
+ }
+ }
+
+ if (current_version_line == -1 && !found_version.is_empty()) {
+ _set_status(UpdateStatus::UPDATE_AVAILABLE);
+ _set_message(vformat(TTR("Update available: %s."), found_version), theme_cache.update_color);
+ return;
+ } else if (current_version_line == -1 || stable_only) {
+ _set_status(UpdateStatus::UP_TO_DATE);
+ return;
+ }
+
+ int current_version_index;
+ VersionType current_version_type = _get_version_type(version_info["status"], &current_version_index);
+
+ for (int i = current_version_line + 1; i < lines.size(); i++) {
+ const String &line = lines[i];
+ if (line.begins_with("- name")) {
+ break;
+ }
+
+ if (!line.begins_with(" - name") && !line.begins_with(" flavor")) {
+ continue;
+ }
+
+ const String version_string = _extract_sub_string(line);
+ int version_index;
+ VersionType version_type = _get_version_type(version_string, &version_index);
+
+ if (int(version_type) < int(current_version_type) || version_index > current_version_index) {
+ found_version += "-" + version_string;
+
+ _set_status(UpdateStatus::UPDATE_AVAILABLE);
+ _set_message(vformat(TTR("Update available: %s."), found_version), theme_cache.update_color);
+ return;
+ }
+ }
+
+ if (current_version_index == DEV_VERSION) {
+ // Since version index can't be determined and no strictly newer version exists, display a different status.
+ _set_status(UpdateStatus::DEV);
+ } else {
+ _set_status(UpdateStatus::UP_TO_DATE);
+ }
+}
+
+void EngineUpdateLabel::_set_message(const String &p_message, const Color &p_color) {
+ if (is_disabled()) {
+ add_theme_color_override("font_disabled_color", p_color);
+ } else {
+ add_theme_color_override("font_color", p_color);
+ }
+ set_text(p_message);
+}
+
+void EngineUpdateLabel::_set_status(UpdateStatus p_status) {
+ status = p_status;
+ if (compact_mode) {
+ if (status != UpdateStatus::BUSY && status != UpdateStatus::UPDATE_AVAILABLE) {
+ hide();
+ return;
+ } else {
+ show();
+ }
+ }
+
+ switch (status) {
+ case UpdateStatus::DEV: {
+ set_disabled(true);
+ _set_message(TTR("Running a development build."), theme_cache.disabled_color);
+ set_tooltip_text(TTR("Exact version can't be determined for update checking."));
+ break;
+ }
+ case UpdateStatus::OFFLINE: {
+ set_disabled(false);
+ if (int(EDITOR_GET("network/connection/network_mode")) == EditorSettings::NETWORK_OFFLINE) {
+ _set_message(TTR("Offline mode, update checks disabled."), theme_cache.disabled_color);
+ } else {
+ _set_message(TTR("Update checks disabled."), theme_cache.disabled_color);
+ }
+ set_tooltip_text("");
+ break;
+ }
+ case UpdateStatus::BUSY: {
+ set_disabled(true);
+ _set_message(TTR("Checking for updates..."), theme_cache.default_color);
+ set_tooltip_text("");
+ } break;
+
+ case UpdateStatus::ERROR: {
+ set_disabled(false);
+ set_tooltip_text(TTR("An error has occurred. Click to try again."));
+ } break;
+
+ case UpdateStatus::UP_TO_DATE: {
+ set_disabled(false);
+ _set_message(TTR("Current version up to date."), theme_cache.disabled_color);
+ set_tooltip_text(TTR("Click to check again."));
+ } break;
+
+ case UpdateStatus::UPDATE_AVAILABLE: {
+ set_disabled(false);
+ set_tooltip_text(TTR("Click to open download page."));
+ } break;
+
+ default: {
+ }
+ }
+}
+
+EngineUpdateLabel::VersionType EngineUpdateLabel::_get_version_type(const String &p_string, int *r_index) const {
+ VersionType type = VersionType::UNKNOWN;
+ String index_string;
+
+ static HashMap<String, VersionType> type_map;
+ if (type_map.is_empty()) {
+ type_map["stable"] = VersionType::STABLE;
+ type_map["rc"] = VersionType::RC;
+ type_map["beta"] = VersionType::BETA;
+ type_map["alpha"] = VersionType::ALPHA;
+ type_map["dev"] = VersionType::DEV;
+ }
+
+ for (const KeyValue<String, VersionType> &kv : type_map) {
+ if (p_string.begins_with(kv.key)) {
+ index_string = p_string.trim_prefix(kv.key);
+ type = kv.value;
+ break;
+ }
+ }
+
+ if (r_index) {
+ if (index_string.is_empty()) {
+ *r_index = DEV_VERSION;
+ } else {
+ *r_index = index_string.to_int();
+ }
+ }
+ return type;
+}
+
+String EngineUpdateLabel::_extract_sub_string(const String &p_line) const {
+ int j = p_line.find("\"") + 1;
+ return p_line.substr(j, p_line.find("\"", j) - j);
+}
+
+void EngineUpdateLabel::_notification(int p_what) {
+ switch (p_what) {
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("network/connection")) {
+ break;
+ }
+
+ if (_can_check_updates()) {
+ if (!checked_update) {
+ _check_update();
+ } else {
+ // This will be wrong when user toggles online mode twice when update is available, but it's not worth handling.
+ _set_status(UpdateStatus::UP_TO_DATE);
+ }
+ } else {
+ _set_status(UpdateStatus::OFFLINE);
+ }
+ } break;
+
+ case NOTIFICATION_THEME_CHANGED: {
+ theme_cache.default_color = get_theme_color("font_color", "Button");
+ theme_cache.disabled_color = get_theme_color("font_disabled_color", "Button");
+ theme_cache.error_color = get_theme_color("error_color", EditorStringName(Editor));
+ theme_cache.update_color = get_theme_color("warning_color", EditorStringName(Editor));
+ } break;
+
+ case NOTIFICATION_READY: {
+ if (_can_check_updates()) {
+ _check_update();
+ } else {
+ _set_status(UpdateStatus::OFFLINE);
+ }
+ } break;
+ }
+}
+
+void EngineUpdateLabel::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("offline_clicked"));
+}
+
+void EngineUpdateLabel::pressed() {
+ switch (status) {
+ case UpdateStatus::OFFLINE: {
+ emit_signal("offline_clicked");
+ } break;
+
+ case UpdateStatus::ERROR:
+ case UpdateStatus::UP_TO_DATE: {
+ _check_update();
+ } break;
+
+ case UpdateStatus::UPDATE_AVAILABLE: {
+ OS::get_singleton()->shell_open("https://godotengine.org/download/archive/" + found_version);
+ } break;
+
+ default: {
+ }
+ }
+}
+
+void EngineUpdateLabel::enable_compact_mode() {
+ compact_mode = true;
+}
+
+EngineUpdateLabel::EngineUpdateLabel() {
+ set_underline_mode(UNDERLINE_MODE_ON_HOVER);
+
+ http = memnew(HTTPRequest);
+ http->set_https_proxy(EDITOR_GET("network/http_proxy/host"), EDITOR_GET("network/http_proxy/port"));
+ http->set_timeout(10.0);
+ add_child(http);
+ http->connect("request_completed", callable_mp(this, &EngineUpdateLabel::_http_request_completed));
+}
diff --git a/editor/engine_update_label.h b/editor/engine_update_label.h
new file mode 100644
index 0000000000..d00fe53e1e
--- /dev/null
+++ b/editor/engine_update_label.h
@@ -0,0 +1,107 @@
+/**************************************************************************/
+/* engine_update_label.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef ENGINE_UPDATE_LABEL_H
+#define ENGINE_UPDATE_LABEL_H
+
+#include "scene/gui/link_button.h"
+
+class HTTPRequest;
+
+class EngineUpdateLabel : public LinkButton {
+ GDCLASS(EngineUpdateLabel, LinkButton);
+
+public:
+ enum class UpdateMode {
+ DISABLED,
+ NEWEST_UNSTABLE,
+ NEWEST_STABLE,
+ NEWEST_PATCH,
+ };
+
+private:
+ static constexpr int DEV_VERSION = 9999; // Version index for unnumbered builds (assumed to always be newest).
+
+ enum class VersionType {
+ STABLE,
+ RC,
+ BETA,
+ ALPHA,
+ DEV,
+ UNKNOWN,
+ };
+
+ enum class UpdateStatus {
+ NONE,
+ DEV,
+ OFFLINE,
+ BUSY,
+ ERROR,
+ UPDATE_AVAILABLE,
+ UP_TO_DATE,
+ };
+
+ struct ThemeCache {
+ Color default_color;
+ Color disabled_color;
+ Color error_color;
+ Color update_color;
+ } theme_cache;
+
+ HTTPRequest *http = nullptr;
+ bool compact_mode = false;
+
+ UpdateStatus status = UpdateStatus::NONE;
+ bool checked_update = false;
+ String found_version;
+
+ bool _can_check_updates() const;
+ void _check_update();
+ void _http_request_completed(int p_result, int p_response_code, const PackedStringArray &p_headers, const PackedByteArray &p_body);
+
+ void _set_message(const String &p_message, const Color &p_color);
+ void _set_status(UpdateStatus p_status);
+
+ VersionType _get_version_type(const String &p_string, int *r_index) const;
+ String _extract_sub_string(const String &p_line) const;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+ virtual void pressed() override;
+
+public:
+ void enable_compact_mode();
+
+ EngineUpdateLabel();
+};
+
+#endif // ENGINE_UPDATE_LABEL_H
diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp
index 1be9fe5740..0f02ab5a7a 100644
--- a/editor/export/editor_export.cpp
+++ b/editor/export/editor_export.cpp
@@ -364,6 +364,7 @@ void EditorExport::update_export_presets() {
if (platform_options.has(preset->get_platform()->get_name())) {
export_presets_updated = true;
+ bool update_value_overrides = false;
List<EditorExportPlatform::ExportOption> options = platform_options[preset->get_platform()->get_name()];
// Clear the preset properties prior to reloading, keep the values to preserve options from plugins that may be currently disabled.
@@ -377,6 +378,13 @@ void EditorExport::update_export_presets() {
preset->values[option_name] = E.default_value;
}
preset->update_visibility[option_name] = E.update_visibility;
+ if (E.update_visibility) {
+ update_value_overrides = true;
+ }
+ }
+
+ if (update_value_overrides) {
+ preset->update_value_overrides();
}
}
}
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 3b6ce8d396..2ed547a970 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -3693,6 +3693,10 @@ void FileSystemDock::_feature_profile_changed() {
_update_display_mode(true);
}
+void FileSystemDock::_project_settings_changed() {
+ assigned_folder_colors = ProjectSettings::get_singleton()->get_setting("file_customization/folder_colors");
+}
+
void FileSystemDock::set_file_sort(FileSortOption p_file_sort) {
for (int i = 0; i != FILE_SORT_MAX; i++) {
tree_button_sort->get_popup()->set_item_checked(i, (i == (int)p_file_sort));
@@ -4122,6 +4126,7 @@ FileSystemDock::FileSystemDock() {
old_display_mode = DISPLAY_MODE_TREE_ONLY;
file_list_display_mode = FILE_LIST_DISPLAY_THUMBNAILS;
+ ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &FileSystemDock::_project_settings_changed));
add_resource_tooltip_plugin(memnew(EditorTextureTooltipPlugin));
}
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index c9fe3a8baf..8830f31d2d 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -362,6 +362,7 @@ private:
bool _is_file_type_disabled_by_feature_profile(const StringName &p_class);
void _feature_profile_changed();
+ void _project_settings_changed();
static Vector<String> _remove_self_included_paths(Vector<String> selected_strings);
void _change_bottom_dock_placement();
diff --git a/editor/gui/editor_bottom_panel.cpp b/editor/gui/editor_bottom_panel.cpp
index 1c95b546f4..b575910976 100644
--- a/editor/gui/editor_bottom_panel.cpp
+++ b/editor/gui/editor_bottom_panel.cpp
@@ -37,6 +37,7 @@
#include "editor/editor_command_palette.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
+#include "editor/engine_update_label.h"
#include "editor/gui/editor_toaster.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/box_container.h"
diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp
index 25c8610ff4..a7cdb65145 100644
--- a/editor/gui/editor_file_dialog.cpp
+++ b/editor/gui/editor_file_dialog.cpp
@@ -968,8 +968,10 @@ void EditorFileDialog::update_file_list() {
}
} else if (!dir_access->current_is_hidden()) {
String full_path = cdir == "res://" ? item : dir_access->get_current_dir() + "/" + item;
- if (dir_access->current_is_dir() && (Engine::get_singleton()->is_project_manager_hint() || !EditorFileSystem::_should_skip_directory(full_path))) {
- dirs.push_back(item);
+ if (dir_access->current_is_dir()) {
+ if (Engine::get_singleton()->is_project_manager_hint() || !EditorFileSystem::_should_skip_directory(full_path)) {
+ dirs.push_back(item);
+ }
} else {
files.push_back(item);
}
diff --git a/editor/gui/editor_scene_tabs.cpp b/editor/gui/editor_scene_tabs.cpp
index b6cb3d7371..5d1e68f008 100644
--- a/editor/gui/editor_scene_tabs.cpp
+++ b/editor/gui/editor_scene_tabs.cpp
@@ -135,6 +135,17 @@ void EditorSceneTabs::_scene_tab_input(const Ref<InputEvent> &p_input) {
}
}
+void EditorSceneTabs::unhandled_key_input(const Ref<InputEvent> &p_event) {
+ if (!tab_preview_panel->is_visible()) {
+ return;
+ }
+
+ Ref<InputEventKey> k = p_event;
+ if (k.is_valid() && k->is_action_pressed(SNAME("ui_cancel"), false, true)) {
+ tab_preview_panel->hide();
+ }
+}
+
void EditorSceneTabs::_reposition_active_tab(int p_to_index) {
EditorNode::get_editor_data().move_edited_scene_to_index(p_to_index);
update_scene_tabs();
@@ -369,6 +380,7 @@ EditorSceneTabs::EditorSceneTabs() {
singleton = this;
set_process_shortcut_input(true);
+ set_process_unhandled_key_input(true);
tabbar_panel = memnew(PanelContainer);
add_child(tabbar_panel);
diff --git a/editor/gui/editor_scene_tabs.h b/editor/gui/editor_scene_tabs.h
index 770114835a..ac9e6b8c43 100644
--- a/editor/gui/editor_scene_tabs.h
+++ b/editor/gui/editor_scene_tabs.h
@@ -79,6 +79,7 @@ class EditorSceneTabs : public MarginContainer {
protected:
void _notification(int p_what);
+ virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
static void _bind_methods();
public:
diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp
index f7e8657525..dce68a97ac 100644
--- a/editor/import/3d/resource_importer_scene.cpp
+++ b/editor/import/3d/resource_importer_scene.cpp
@@ -1993,7 +1993,6 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
case INTERNAL_IMPORT_CATEGORY_MESH: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/path", PROPERTY_HINT_SAVE_FILE, "*.res,*.tres"), ""));
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/make_streamable"), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/shadow_meshes", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lightmap_uv", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lods", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
@@ -2147,7 +2146,7 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor
}
} break;
case INTERNAL_IMPORT_CATEGORY_MESH: {
- if (p_option == "save_to_file/path" || p_option == "save_to_file/make_streamable") {
+ if (p_option == "save_to_file/path") {
return p_options["save_to_file/enabled"];
}
} break;
diff --git a/editor/import/3d/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp
index 67b13e3328..620ebce44b 100644
--- a/editor/import/3d/scene_import_settings.cpp
+++ b/editor/import/3d/scene_import_settings.cpp
@@ -1653,6 +1653,28 @@ SceneImportSettingsDialog::SceneImportSettingsDialog() {
camera->set_attributes(camera_attributes);
}
+ // Use a grayscale gradient sky to avoid skewing the preview towards a specific color,
+ // but still allow shaded areas to be easily distinguished (using the ambient and reflected light).
+ // This also helps the user orient themselves in the preview, since the bottom of the sky is black
+ // and the top of the sky is white.
+ procedural_sky_material.instantiate();
+ procedural_sky_material->set_sky_top_color(Color(1, 1, 1));
+ procedural_sky_material->set_sky_horizon_color(Color(0.5, 0.5, 0.5));
+ procedural_sky_material->set_ground_horizon_color(Color(0.5, 0.5, 0.5));
+ procedural_sky_material->set_ground_bottom_color(Color(0, 0, 0));
+ procedural_sky_material->set_sky_curve(2.0);
+ procedural_sky_material->set_ground_curve(0.5);
+ // Hide the sun from the sky.
+ procedural_sky_material->set_sun_angle_max(0.0);
+ sky.instantiate();
+ sky->set_material(procedural_sky_material);
+ environment.instantiate();
+ environment->set_background(Environment::BG_SKY);
+ environment->set_sky(sky);
+ // A custom FOV must be specified, as an orthogonal camera is used for the preview.
+ environment->set_sky_custom_fov(50.0);
+ camera->set_environment(environment);
+
light = memnew(DirectionalLight3D);
light->set_transform(Transform3D().looking_at(Vector3(-1, -2, -0.6), Vector3(0, 1, 0)));
base_viewport->add_child(light);
diff --git a/editor/import/3d/scene_import_settings.h b/editor/import/3d/scene_import_settings.h
index f4954c41db..17d6616fc0 100644
--- a/editor/import/3d/scene_import_settings.h
+++ b/editor/import/3d/scene_import_settings.h
@@ -47,6 +47,7 @@
#include "scene/gui/tab_container.h"
#include "scene/gui/tree.h"
#include "scene/resources/3d/primitive_meshes.h"
+#include "scene/resources/3d/sky_material.h"
class EditorFileDialog;
class EditorInspector;
@@ -78,6 +79,9 @@ class SceneImportSettingsDialog : public ConfirmationDialog {
Camera3D *camera = nullptr;
Ref<CameraAttributesPractical> camera_attributes;
+ Ref<Environment> environment;
+ Ref<Sky> sky;
+ Ref<ProceduralSkyMaterial> procedural_sky_material;
bool first_aabb = false;
AABB contents_aabb;
diff --git a/editor/multi_node_edit.cpp b/editor/multi_node_edit.cpp
index 4f0db70681..45786c0ab5 100644
--- a/editor/multi_node_edit.cpp
+++ b/editor/multi_node_edit.cpp
@@ -244,7 +244,7 @@ int MultiNodeEdit::get_node_count() const {
}
NodePath MultiNodeEdit::get_node(int p_index) const {
- ERR_FAIL_INDEX_V(p_index, nodes.size(), NodePath());
+ ERR_FAIL_INDEX_V(p_index, get_node_count(), NodePath());
return nodes[p_index];
}
diff --git a/editor/multi_node_edit.h b/editor/multi_node_edit.h
index deef88633e..000d41c4c1 100644
--- a/editor/multi_node_edit.h
+++ b/editor/multi_node_edit.h
@@ -36,7 +36,7 @@
class MultiNodeEdit : public RefCounted {
GDCLASS(MultiNodeEdit, RefCounted);
- List<NodePath> nodes;
+ LocalVector<NodePath> nodes;
struct PLData {
int uses = 0;
PropertyInfo info;
@@ -67,6 +67,19 @@ public:
void set_property_field(const StringName &p_property, const Variant &p_value, const String &p_field);
+ // If the nodes selected are the same independently of order then return true.
+ bool is_same_selection(const MultiNodeEdit *p_other) const {
+ if (get_node_count() != p_other->get_node_count()) {
+ return false;
+ }
+ for (int i = 0; i < get_node_count(); i++) {
+ if (nodes.find(p_other->get_node(i)) == -1) {
+ return false;
+ }
+ }
+
+ return true;
+ }
MultiNodeEdit();
};
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index bfac9c8cf9..43928b9c17 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -2041,10 +2041,10 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug
vb->add_child(name_hb);
name_dialog->register_text_enter(name);
- error_dialog = memnew(ConfirmationDialog);
+ error_dialog = memnew(AcceptDialog);
error_dialog->set_ok_button_text(TTR("Close"));
error_dialog->set_title(TTR("Error!"));
- add_child(error_dialog);
+ name_dialog->add_child(error_dialog);
name_dialog->connect(SNAME("confirmed"), callable_mp(this, &AnimationPlayerEditor::_animation_name_edited));
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index c780023c6d..a1175e2a0f 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -128,7 +128,7 @@ class AnimationPlayerEditor : public VBoxContainer {
} blend_editor;
ConfirmationDialog *name_dialog = nullptr;
- ConfirmationDialog *error_dialog = nullptr;
+ AcceptDialog *error_dialog = nullptr;
int name_dialog_op = TOOL_NEW_ANIM;
bool updating = false;
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 8d24e90cce..8a9118a03e 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -5860,13 +5860,34 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons
}
void CanvasItemEditorViewport::_perform_drop_data() {
+ ERR_FAIL_COND(selected_files.size() <= 0);
+
_remove_preview();
- // Without root dropping multiple files is not allowed
- if (!target_node && selected_files.size() > 1) {
- accept->set_text(TTR("Cannot instantiate multiple nodes without root."));
- accept->popup_centered();
- return;
+ if (!target_node) {
+ // Without root dropping multiple files is not allowed
+ if (selected_files.size() > 1) {
+ accept->set_text(TTR("Cannot instantiate multiple nodes without root."));
+ accept->popup_centered();
+ return;
+ }
+
+ const String &path = selected_files[0];
+ Ref<Resource> res = ResourceLoader::load(path);
+ if (res.is_null()) {
+ return;
+ }
+
+ Ref<PackedScene> scene = res;
+ if (scene.is_valid()) {
+ // Without root node act the same as "Load Inherited Scene".
+ Error err = EditorNode::get_singleton()->load_scene(path, false, true);
+ if (err != OK) {
+ accept->set_text(vformat(TTR("Error instantiating scene from %s."), path.get_file()));
+ accept->popup_centered();
+ }
+ return;
+ }
}
PackedStringArray error_files;
@@ -5882,27 +5903,21 @@ void CanvasItemEditorViewport::_perform_drop_data() {
if (res.is_null()) {
continue;
}
- Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
- if (scene != nullptr && scene.is_valid()) {
- if (!target_node) {
- // Without root node act the same as "Load Inherited Scene"
- Error err = EditorNode::get_singleton()->load_scene(path, false, true);
- if (err != OK) {
- error_files.push_back(path.get_file());
- }
- } else {
- bool success = _create_instance(target_node, path, drop_pos);
- if (!success) {
- error_files.push_back(path.get_file());
- }
- }
- } else {
- Ref<Texture2D> texture = Ref<Texture2D>(Object::cast_to<Texture2D>(*res));
- if (texture != nullptr && texture.is_valid()) {
- Node *child = Object::cast_to<Node>(ClassDB::instantiate(default_texture_node_type));
- _create_nodes(target_node, child, path, drop_pos);
- undo_redo->add_do_method(editor_selection, "add_node", child);
+
+ Ref<PackedScene> scene = res;
+ if (scene.is_valid()) {
+ bool success = _create_instance(target_node, path, drop_pos);
+ if (!success) {
+ error_files.push_back(path.get_file());
}
+ continue;
+ }
+
+ Ref<Texture2D> texture = res;
+ if (texture.is_valid()) {
+ Node *child = Object::cast_to<Node>(ClassDB::instantiate(default_texture_node_type));
+ _create_nodes(target_node, child, path, drop_pos);
+ undo_redo->add_do_method(editor_selection, "add_node", child);
}
}
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index fd6622bad6..c4b995b048 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -297,6 +297,7 @@ private:
};
HashMap<BoneKey, BoneList> bone_list;
+ MenuButton *skeleton_menu = nullptr;
struct PoseClipboard {
Vector2 pos;
@@ -329,7 +330,6 @@ private:
Button *group_button = nullptr;
Button *ungroup_button = nullptr;
- MenuButton *skeleton_menu = nullptr;
Button *override_camera_button = nullptr;
MenuButton *view_menu = nullptr;
PopupMenu *grid_menu = nullptr;
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 6c63d9ff0d..c832570fee 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -3886,6 +3886,8 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_open_scripts"), &ScriptEditor::_get_open_scripts);
ClassDB::bind_method(D_METHOD("open_script_create_dialog", "base_name", "base_path"), &ScriptEditor::open_script_create_dialog);
+ ClassDB::bind_method(D_METHOD("goto_help", "topic"), &ScriptEditor::goto_help);
+
ADD_SIGNAL(MethodInfo("editor_script_changed", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script")));
ADD_SIGNAL(MethodInfo("script_close", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script")));
}
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index fc361ab264..e4e66b38e9 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -2317,15 +2317,15 @@ void SpriteFramesEditorPlugin::edit(Object *p_object) {
}
bool SpriteFramesEditorPlugin::handles(Object *p_object) const {
- AnimatedSprite2D *animated_sprite = Object::cast_to<AnimatedSprite2D>(p_object);
- AnimatedSprite3D *animated_sprite_3d = Object::cast_to<AnimatedSprite3D>(p_object);
- if (animated_sprite && *animated_sprite->get_sprite_frames()) {
+ AnimatedSprite2D *animated_sprite_2d = Object::cast_to<AnimatedSprite2D>(p_object);
+ if (animated_sprite_2d && *animated_sprite_2d->get_sprite_frames()) {
return true;
- } else if (animated_sprite_3d && *animated_sprite_3d->get_sprite_frames()) {
+ }
+ AnimatedSprite3D *animated_sprite_3d = Object::cast_to<AnimatedSprite3D>(p_object);
+ if (animated_sprite_3d && *animated_sprite_3d->get_sprite_frames()) {
return true;
- } else {
- return p_object->is_class("SpriteFrames");
}
+ return p_object->is_class("SpriteFrames");
}
void SpriteFramesEditorPlugin::make_visible(bool p_visible) {
diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp
index c00a6d99d3..e1a58a0998 100644
--- a/editor/plugins/tiles/tile_map_layer_editor.cpp
+++ b/editor/plugins/tiles/tile_map_layer_editor.cpp
@@ -3672,10 +3672,16 @@ void TileMapLayerEditor::_node_change(Node *p_node) {
void TileMapLayerEditor::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_READY: {
+ case NOTIFICATION_ENTER_TREE: {
get_tree()->connect("node_added", callable_mp(this, &TileMapLayerEditor::_node_change));
get_tree()->connect("node_removed", callable_mp(this, &TileMapLayerEditor::_node_change));
} break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ get_tree()->disconnect("node_added", callable_mp(this, &TileMapLayerEditor::_node_change));
+ get_tree()->disconnect("node_removed", callable_mp(this, &TileMapLayerEditor::_node_change));
+ } break;
+
case NOTIFICATION_THEME_CHANGED: {
missing_tile_texture = get_editor_theme_icon(SNAME("StatusWarning"));
warning_pattern_texture = get_editor_theme_icon(SNAME("WarningPattern"));
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index f9efc62f03..1da2f89c1c 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -1838,7 +1838,8 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i tile_size = tile_set_atlas_source->get_texture_region_size();
Vector2i origin = margins + (area.position * (tile_size + separation));
- TilesEditorUtils::draw_selection_rect(tile_atlas_control, Rect2i(origin, area.size * tile_size));
+ Vector2i size = area.size * tile_size + (area.size - Vector2i(1, 1)).max(Vector2i(0, 0)) * separation;
+ TilesEditorUtils::draw_selection_rect(tile_atlas_control, Rect2i(origin, size));
} else {
Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
if (hovered_base_tile_coords.x >= 0 && hovered_base_tile_coords.y >= 0 && hovered_base_tile_coords.x < grid_size.x && hovered_base_tile_coords.y < grid_size.y) {
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 05b8b81fa7..0dd845270a 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -953,8 +953,8 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
bool port_left_used = false;
String name_left;
if (valid_left) {
- name_left = vsnode->get_input_port_name(i);
- port_left = vsnode->get_input_port_type(i);
+ name_left = vsnode->get_input_port_name(j);
+ port_left = vsnode->get_input_port_type(j);
for (const VisualShader::Connection &E : connections) {
if (E.to_node == p_id && E.to_port == j) {
port_left_used = true;
@@ -989,15 +989,15 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
Variant default_value;
if (valid_left && !port_left_used) {
- default_value = vsnode->get_input_port_default_value(i);
+ default_value = vsnode->get_input_port_default_value(j);
}
Button *button = memnew(Button);
hb->add_child(button);
- register_default_input_button(p_id, i, button);
- button->connect("pressed", callable_mp(editor, &VisualShaderEditor::_edit_port_default_input).bind(button, p_id, i));
+ register_default_input_button(p_id, j, button);
+ button->connect("pressed", callable_mp(editor, &VisualShaderEditor::_edit_port_default_input).bind(button, p_id, j));
if (default_value.get_type() != Variant::NIL) { // only a label
- set_input_port_default_value(p_type, p_id, i, default_value);
+ set_input_port_default_value(p_type, p_id, j, default_value);
} else {
button->hide();
}
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 86c59ec99e..a16767d916 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -43,6 +43,7 @@
#include "editor/editor_about.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
+#include "editor/engine_update_label.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/gui/editor_title_bar.h"
#include "editor/plugins/asset_library_editor_plugin.h"
@@ -526,7 +527,7 @@ void ProjectManager::_open_selected_projects_ask() {
return;
}
- const Size2i popup_min_size = Size2i(600.0 * EDSCALE, 0);
+ const Size2i popup_min_size = Size2i(400.0 * EDSCALE, 0);
if (selected_list.size() > 1) {
multi_open_ask->set_text(vformat(TTR("You requested to open %d projects in parallel. Do you confirm?\nNote that usual checks for engine version compatibility will be bypassed."), selected_list.size()));
@@ -1397,8 +1398,15 @@ ProjectManager::ProjectManager() {
{
HBoxContainer *footer_bar = memnew(HBoxContainer);
footer_bar->set_alignment(BoxContainer::ALIGNMENT_END);
+ footer_bar->add_theme_constant_override("separation", 20 * EDSCALE);
main_vbox->add_child(footer_bar);
+#ifdef ENGINE_UPDATE_CHECK_ENABLED
+ EngineUpdateLabel *update_label = memnew(EngineUpdateLabel);
+ footer_bar->add_child(update_label);
+ update_label->connect("offline_clicked", callable_mp(this, &ProjectManager::_show_quick_settings));
+#endif
+
version_btn = memnew(LinkButton);
String hash = String(VERSION_HASH);
if (hash.length() != 0) {
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 757b9e72ea..e1084d4873 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -2079,17 +2079,19 @@ bool SceneTreeDock::_validate_no_foreign() {
return false;
}
- // When edited_scene inherits from another one the root Node will be the parent Scene,
- // we don't want to consider that Node a foreign one otherwise we would not be able to
- // delete it.
- if (edited_scene->get_scene_inherited_state().is_valid() && edited_scene == E) {
- continue;
- }
+ if (edited_scene->get_scene_inherited_state().is_valid()) {
+ // When edited_scene inherits from another one the root Node will be the parent Scene,
+ // we don't want to consider that Node a foreign one otherwise we would not be able to
+ // delete it.
+ if (edited_scene == E && current_option != TOOL_REPLACE) {
+ continue;
+ }
- if (edited_scene->get_scene_inherited_state().is_valid() && edited_scene->get_scene_inherited_state()->find_node_by_path(edited_scene->get_path_to(E)) >= 0) {
- accept->set_text(TTR("Can't operate on nodes the current scene inherits from!"));
- accept->popup_centered();
- return false;
+ if (edited_scene == E || edited_scene->get_scene_inherited_state()->find_node_by_path(edited_scene->get_path_to(E)) >= 0) {
+ accept->set_text(TTR("Can't operate on nodes the current scene inherits from!"));
+ accept->popup_centered();
+ return false;
+ }
}
}
@@ -3018,6 +3020,13 @@ void SceneTreeDock::set_edited_scene(Node *p_scene) {
edited_scene = p_scene;
}
+void SceneTreeDock::set_selection(const Vector<Node *> &p_nodes) {
+ editor_selection->clear();
+ for (Node *node : p_nodes) {
+ editor_selection->add_node(node);
+ }
+}
+
void SceneTreeDock::set_selected(Node *p_node, bool p_emit_selected) {
scene_tree->set_selected(p_node, p_emit_selected);
}
@@ -3443,6 +3452,13 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
can_replace = false;
break;
}
+
+ if (edited_scene->get_scene_inherited_state().is_valid()) {
+ if (E == edited_scene || edited_scene->get_scene_inherited_state()->find_node_by_path(edited_scene->get_path_to(E)) >= 0) {
+ can_replace = false;
+ break;
+ }
+ }
}
if (can_replace) {
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index 86e9ff8a47..21e1b00f93 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -322,6 +322,7 @@ public:
void set_edited_scene(Node *p_scene);
void instantiate(const String &p_file);
void instantiate_scenes(const Vector<String> &p_files, Node *p_parent = nullptr);
+ void set_selection(const Vector<Node *> &p_nodes);
void set_selected(Node *p_node, bool p_emit_selected = false);
void fill_path_renames(Node *p_node, Node *p_new_parent, HashMap<Node *, NodePath> *p_renames);
void perform_node_renames(Node *p_base, HashMap<Node *, NodePath> *p_renames, HashMap<Ref<Animation>, HashSet<int>> *r_rem_anims = nullptr);
diff --git a/main/main.cpp b/main/main.cpp
index 357033b6d8..ed74094c9e 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -69,16 +69,18 @@
#include "servers/display_server.h"
#include "servers/movie_writer/movie_writer.h"
#include "servers/movie_writer/movie_writer_mjpeg.h"
-#include "servers/navigation_server_2d.h"
-#include "servers/navigation_server_2d_dummy.h"
#include "servers/navigation_server_3d.h"
#include "servers/navigation_server_3d_dummy.h"
-#include "servers/physics_server_2d.h"
#include "servers/register_server_types.h"
#include "servers/rendering/rendering_server_default.h"
#include "servers/text/text_server_dummy.h"
#include "servers/text_server.h"
+// 2D
+#include "servers/navigation_server_2d.h"
+#include "servers/navigation_server_2d_dummy.h"
+#include "servers/physics_server_2d.h"
+
#ifndef _3D_DISABLED
#include "servers/physics_server_3d.h"
#include "servers/xr_server.h"
@@ -143,15 +145,15 @@ static MessageQueue *message_queue = nullptr;
// Initialized in setup2()
static AudioServer *audio_server = nullptr;
+static CameraServer *camera_server = nullptr;
static DisplayServer *display_server = nullptr;
static RenderingServer *rendering_server = nullptr;
-static CameraServer *camera_server = nullptr;
static TextServerManager *tsman = nullptr;
+static ThemeDB *theme_db = nullptr;
+static NavigationServer2D *navigation_server_2d = nullptr;
static PhysicsServer2DManager *physics_server_2d_manager = nullptr;
static PhysicsServer2D *physics_server_2d = nullptr;
static NavigationServer3D *navigation_server_3d = nullptr;
-static NavigationServer2D *navigation_server_2d = nullptr;
-static ThemeDB *theme_db = nullptr;
#ifndef _3D_DISABLED
static PhysicsServer3DManager *physics_server_3d_manager = nullptr;
static PhysicsServer3D *physics_server_3d = nullptr;
@@ -695,7 +697,9 @@ Error Main::test_setup() {
/** INITIALIZE SERVERS **/
register_server_types();
+#ifndef _3D_DISABLED
XRServer::set_xr_mode(XRServer::XRMODE_OFF); // Skip in tests.
+#endif // _3D_DISABLED
initialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
@@ -2274,6 +2278,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
// Editor and project manager cannot run with rendering in a separate thread (they will crash on startup).
rtm = OS::RENDER_THREAD_SAFE;
}
+#if !defined(THREADS_ENABLED)
+ rtm = OS::RENDER_THREAD_SAFE;
+#endif
OS::get_singleton()->_render_thread_mode = OS::RenderThreadMode(rtm);
}
@@ -2717,7 +2724,9 @@ Error Main::setup2() {
}
if (OS::get_singleton()->_render_thread_mode == OS::RENDER_SEPARATE_THREAD) {
- WARN_PRINT("The Multi-Threaded rendering thread model is experimental, and has known issues which can lead to project crashes. Use the Single-Safe option in the project settings instead.");
+ WARN_PRINT("The Multi-Threaded rendering thread model is experimental. Feel free to try it since it will eventually become a stable feature.\n"
+ "However, bear in mind that at the moment it can lead to project crashes or instability.\n"
+ "So, unless you want to test the engine, use the Single-Safe option in the project settings instead.");
}
/* Initialize Pen Tablet Driver */
@@ -4025,11 +4034,11 @@ bool Main::iteration() {
if ((!force_redraw_requested) && OS::get_singleton()->is_in_low_processor_usage_mode()) {
if (RenderingServer::get_singleton()->has_changed()) {
RenderingServer::get_singleton()->draw(true, scaled_step); // flush visual commands
- Engine::get_singleton()->frames_drawn++;
+ Engine::get_singleton()->increment_frames_drawn();
}
} else {
RenderingServer::get_singleton()->draw(true, scaled_step); // flush visual commands
- Engine::get_singleton()->frames_drawn++;
+ Engine::get_singleton()->increment_frames_drawn();
force_redraw_requested = false;
}
}
diff --git a/main/performance.cpp b/main/performance.cpp
index e8d519bb46..9d4e2ae9d5 100644
--- a/main/performance.cpp
+++ b/main/performance.cpp
@@ -36,9 +36,11 @@
#include "scene/main/scene_tree.h"
#include "servers/audio_server.h"
#include "servers/navigation_server_3d.h"
-#include "servers/physics_server_2d.h"
#include "servers/rendering_server.h"
+// 2D
+#include "servers/physics_server_2d.h"
+
#ifndef _3D_DISABLED
#include "servers/physics_server_3d.h"
#endif // _3D_DISABLED
diff --git a/methods.py b/methods.py
index dd01469b86..4d1f4c1cda 100644
--- a/methods.py
+++ b/methods.py
@@ -1621,8 +1621,8 @@ def generate_vs_project(env, original_args, project_name="godot"):
sln_template = sln_template.replace("%%NAME%%", project_name)
sln_template = sln_template.replace("%%UUID%%", proj_uuid)
sln_template = sln_template.replace("%%SLNUUID%%", sln_uuid)
- sln_template = sln_template.replace("%%SECTION1%%", "\n ".join(section1))
- sln_template = sln_template.replace("%%SECTION2%%", "\n ".join(section2))
+ sln_template = sln_template.replace("%%SECTION1%%", "\n\t\t".join(section1))
+ sln_template = sln_template.replace("%%SECTION2%%", "\n\t\t".join(section2))
with open(f"{project_name}.sln", "w", encoding="utf-8", newline="\r\n") as f:
f.write(sln_template)
diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected
index 9cd732813e..6471c5a142 100644
--- a/misc/extension_api_validation/4.2-stable.expected
+++ b/misc/extension_api_validation/4.2-stable.expected
@@ -263,12 +263,30 @@ Removed VisualShaderNodeComment, which is replaced by VisualShaderNodeFrame.
GH-87888
--------
-Validate extension JSON: API was removed: classes/OpenXRHand/methods/get_hand_skeleton
-Validate extension JSON: API was removed: classes/OpenXRHand/methods/set_hand_skeleton
-Validate extension JSON: API was removed: classes/OpenXRHand/properties/hand_skeleton
Validate extension JSON: API was removed: classes/Skeleton3D/properties/animate_physical_bones
Validate extension JSON: API was removed: classes/SkeletonIK3D/methods/get_interpolation
Validate extension JSON: API was removed: classes/SkeletonIK3D/methods/set_interpolation
Validate extension JSON: API was removed: classes/SkeletonIK3D/properties/interpolation
These base class is changed to SkeletonModifier3D which is processed by Skeleton3D with the assumption that it is Skeleton3D's child.
+
+
+GH-90575
+--------
+Validate extension JSON: API was removed: classes/BoneAttachment3D/methods/on_bone_pose_update
+Validate extension JSON: API was removed: classes/Skeleton3D/signals/bone_pose_changed
+
+They have been replaced by a safer API due to performance concerns. Compatibility method registered.
+
+GH-90747
+--------
+Validate extension JSON: API was removed: classes/NavigationRegion2D/methods/get_avoidance_layers
+Validate extension JSON: API was removed: classes/NavigationRegion2D/methods/set_avoidance_layers
+Validate extension JSON: API was removed: classes/NavigationRegion2D/properties/avoidance_layers
+Validate extension JSON: API was removed: classes/NavigationRegion2D/methods/get_avoidance_layer_value
+Validate extension JSON: API was removed: classes/NavigationRegion2D/methods/set_avoidance_layer_value
+Validate extension JSON: API was removed: classes/NavigationRegion2D/methods/set_constrain_avoidance
+Validate extension JSON: API was removed: classes/NavigationRegion2D/methods/get_constrain_avoidance
+Validate extension JSON: API was removed: classes/NavigationRegion2D/properties/constrain_avoidance
+
+Experimental NavigationRegion2D feature "constrain_avoidance" was discontinued with no replacement.
diff --git a/misc/scripts/copyright_headers.py b/misc/scripts/copyright_headers.py
index 169795921f..2b1201b3c0 100755
--- a/misc/scripts/copyright_headers.py
+++ b/misc/scripts/copyright_headers.py
@@ -73,7 +73,7 @@ for f in sys.argv[1:]:
line = fileread.readline()
header_done = False
- while line.strip() == "": # Skip empty lines at the top
+ while line.strip() == "" and line != "": # Skip empty lines at the top
line = fileread.readline()
if line.find("/**********") == -1: # Godot header starts this way
diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp
index 1dbe4c535a..bde23289f4 100644
--- a/modules/fbx/fbx_document.cpp
+++ b/modules/fbx/fbx_document.cpp
@@ -990,8 +990,8 @@ Error FBXDocument::_parse_images(Ref<FBXState> p_state, const String &p_base_pat
for (int texture_i = 0; texture_i < static_cast<int>(fbx_scene->texture_files.count); texture_i++) {
const ufbx_texture_file &fbx_texture_file = fbx_scene->texture_files[texture_i];
String path = _as_string(fbx_texture_file.filename);
- path = ProjectSettings::get_singleton()->localize_path(path);
- if (path.is_absolute_path() && !path.is_resource_file()) {
+ // Use only filename for absolute paths to avoid portability issues.
+ if (path.is_absolute_path()) {
path = path.get_file();
}
if (!p_base_path.is_empty()) {
@@ -1080,7 +1080,7 @@ Error FBXDocument::_parse_materials(Ref<FBXState> p_state) {
if (fbx_material->pbr.base_color.has_value) {
Color albedo = _material_color(fbx_material->pbr.base_color, fbx_material->pbr.base_factor);
- material->set_albedo(albedo);
+ material->set_albedo(albedo.linear_to_srgb());
}
if (fbx_material->features.double_sided.enabled) {
@@ -1178,7 +1178,11 @@ Error FBXDocument::_parse_materials(Ref<FBXState> p_state) {
}
// Combined textures and factors are very unreliable in FBX
- material->set_albedo(Color(1, 1, 1));
+ Color albedo_factor = Color(1, 1, 1);
+ if (fbx_material->pbr.base_factor.has_value) {
+ albedo_factor *= (float)fbx_material->pbr.base_factor.value_real;
+ }
+ material->set_albedo(albedo_factor.linear_to_srgb());
// TODO: Does not support rotation, could be inverted?
material->set_uv1_offset(_as_vec3(base_texture->uv_transform.translation));
@@ -1232,11 +1236,11 @@ Error FBXDocument::_parse_materials(Ref<FBXState> p_state) {
if (fbx_material->pbr.emission_color.has_value) {
material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true);
- material->set_emission(_material_color(fbx_material->pbr.emission_color));
+ material->set_emission(_material_color(fbx_material->pbr.emission_color).linear_to_srgb());
material->set_emission_energy_multiplier(float(fbx_material->pbr.emission_factor.value_real));
}
- const ufbx_texture *emission_texture = _get_file_texture(fbx_material->pbr.ambient_occlusion.texture);
+ const ufbx_texture *emission_texture = _get_file_texture(fbx_material->pbr.emission_color.texture);
if (emission_texture) {
material->set_texture(BaseMaterial3D::TEXTURE_EMISSION, _get_texture(p_state, GLTFTextureIndex(emission_texture->file_index), TEXTURE_TYPE_GENERIC));
material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true);
@@ -1266,7 +1270,7 @@ Error FBXDocument::_parse_cameras(Ref<FBXState> p_state) {
camera->set_fov(Math::deg_to_rad(real_t(fbx_camera->field_of_view_deg.y)));
} else {
camera->set_perspective(false);
- camera->set_size_mag(real_t(fbx_camera->orthographic_size.y));
+ camera->set_size_mag(real_t(fbx_camera->orthographic_size.y * 0.5f));
}
if (fbx_camera->near_plane != 0.0f) {
camera->set_depth_near(fbx_camera->near_plane);
@@ -2235,6 +2239,10 @@ Error FBXDocument::_parse_lights(Ref<FBXState> p_state) {
}
String FBXDocument::_get_texture_path(const String &p_base_dir, const String &p_source_file_path) const {
+ // Check if the original path exists first.
+ if (FileAccess::exists(p_source_file_path)) {
+ return p_source_file_path.strip_edges();
+ }
const String tex_file_name = p_source_file_path.get_file();
const Vector<String> subdirs = {
"", "textures/", "Textures/", "images/",
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index a30a87f3a1..cd19887d82 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -1957,6 +1957,18 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
} else {
parser->push_warning(p_assignable, GDScriptWarning::UNTYPED_DECLARATION, declaration_type, p_assignable->identifier->name);
}
+ } else if (specified_type.kind == GDScriptParser::DataType::ENUM && p_assignable->initializer == nullptr) {
+ // Warn about enum variables without default value. Unless the enum defines the "0" value, then it's fine.
+ bool has_zero_value = false;
+ for (const KeyValue<StringName, int64_t> &kv : specified_type.enum_values) {
+ if (kv.value == 0) {
+ has_zero_value = true;
+ break;
+ }
+ }
+ if (!has_zero_value) {
+ parser->push_warning(p_assignable, GDScriptWarning::ENUM_VARIABLE_WITHOUT_DEFAULT, p_assignable->identifier->name);
+ }
}
#endif
@@ -3469,6 +3481,8 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
if (op_type.builtin_type == Variant::INT && cast_type.kind == GDScriptParser::DataType::ENUM) {
mark_node_unsafe(p_cast);
valid = true;
+ } else if (op_type.kind == GDScriptParser::DataType::ENUM && cast_type.builtin_type == Variant::INT) {
+ valid = true;
} else if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) {
valid = Variant::can_convert(op_type.builtin_type, cast_type.builtin_type);
} else if (op_type.kind != GDScriptParser::DataType::BUILTIN && cast_type.kind != GDScriptParser::DataType::BUILTIN) {
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 2940af585d..5b1639e250 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -1455,10 +1455,11 @@ GDScriptTokenizer::Token GDScriptTokenizerText::scan() {
if (_peek() != '\n') {
return make_error("Expected new line after \"\\\".");
}
- continuation_lines.push_back(line);
_advance();
newline(false);
line_continuation = true;
+ _skip_whitespace(); // Skip whitespace/comment lines after `\`. See GH-89403.
+ continuation_lines.push_back(line);
return scan(); // Recurse to get next token.
}
diff --git a/modules/gdscript/gdscript_tokenizer_buffer.cpp b/modules/gdscript/gdscript_tokenizer_buffer.cpp
index db523ea941..e53bc5bc41 100644
--- a/modules/gdscript/gdscript_tokenizer_buffer.cpp
+++ b/modules/gdscript/gdscript_tokenizer_buffer.cpp
@@ -285,9 +285,9 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code,
// Remove continuation lines from map.
for (int line : tokenizer.get_continuation_lines()) {
- if (rev_token_lines.has(line + 1)) {
- token_lines.erase(rev_token_lines[line + 1]);
- token_columns.erase(rev_token_lines[line + 1]);
+ if (rev_token_lines.has(line)) {
+ token_lines.erase(rev_token_lines[line]);
+ token_columns.erase(rev_token_lines[line]);
}
}
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 708966a0a8..48a0abe617 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -126,6 +126,9 @@ String GDScriptWarning::get_message() const {
case INT_AS_ENUM_WITHOUT_MATCH:
CHECK_SYMBOLS(3);
return vformat(R"(Cannot %s %s as Enum "%s": no enum member has matching value.)", symbols[0], symbols[1], symbols[2]);
+ case ENUM_VARIABLE_WITHOUT_DEFAULT:
+ CHECK_SYMBOLS(1);
+ return vformat(R"(The variable "%s" has an enum type and does not set an explicit default value. The default will be set to "0".)", symbols[0]);
case EMPTY_FILE:
return "Empty script file.";
case DEPRECATED_KEYWORD:
@@ -221,6 +224,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"NARROWING_CONVERSION",
"INT_AS_ENUM_WITHOUT_CAST",
"INT_AS_ENUM_WITHOUT_MATCH",
+ "ENUM_VARIABLE_WITHOUT_DEFAULT",
"EMPTY_FILE",
"DEPRECATED_KEYWORD",
"RENAMED_IN_GODOT_4_HINT",
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index 93c232a0f8..3ad9488138 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -78,6 +78,7 @@ public:
NARROWING_CONVERSION, // Float value into an integer slot, precision is lost.
INT_AS_ENUM_WITHOUT_CAST, // An integer value was used as an enum value without casting.
INT_AS_ENUM_WITHOUT_MATCH, // An integer value was used as an enum value without matching enum member.
+ ENUM_VARIABLE_WITHOUT_DEFAULT, // A variable with an enum type does not have a default value. The default will be set to `0` instead of the first enum value.
EMPTY_FILE, // A script file is empty.
DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced.
RENAMED_IN_GODOT_4_HINT, // A variable or function that could not be found has been renamed in Godot 4.
@@ -129,6 +130,7 @@ public:
WARN, // NARROWING_CONVERSION
WARN, // INT_AS_ENUM_WITHOUT_CAST
WARN, // INT_AS_ENUM_WITHOUT_MATCH
+ WARN, // ENUM_VARIABLE_WITHOUT_DEFAULT
WARN, // EMPTY_FILE
WARN, // DEPRECATED_KEYWORD
WARN, // RENAMED_IN_GODOT_4_HINT
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index a0329eb8d2..e3d16eaf42 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -300,14 +300,23 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) {
#endif
String out_file = next.get_basename() + ".out";
- if (!is_generating && !dir->file_exists(out_file)) {
- ERR_FAIL_V_MSG(false, "Could not find output file for " + next);
- }
- GDScriptTest test(current_dir.path_join(next), current_dir.path_join(out_file), source_dir);
- if (binary_tokens) {
- test.set_tokenizer_mode(GDScriptTest::TOKENIZER_BUFFER);
+ ERR_FAIL_COND_V_MSG(!is_generating && !dir->file_exists(out_file), false, "Could not find output file for " + next);
+
+ if (next.ends_with(".bin.gd")) {
+ // Test text mode first.
+ GDScriptTest text_test(current_dir.path_join(next), current_dir.path_join(out_file), source_dir);
+ tests.push_back(text_test);
+ // Test binary mode even without `--use-binary-tokens`.
+ GDScriptTest bin_test(current_dir.path_join(next), current_dir.path_join(out_file), source_dir);
+ bin_test.set_tokenizer_mode(GDScriptTest::TOKENIZER_BUFFER);
+ tests.push_back(bin_test);
+ } else {
+ GDScriptTest test(current_dir.path_join(next), current_dir.path_join(out_file), source_dir);
+ if (binary_tokens) {
+ test.set_tokenizer_mode(GDScriptTest::TOKENIZER_BUFFER);
+ }
+ tests.push_back(test);
}
- tests.push_back(test);
}
}
diff --git a/modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.gd b/modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.gd
new file mode 100644
index 0000000000..77ef9e2073
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.gd
@@ -0,0 +1,9 @@
+# GH-85882
+
+enum Foo { A, B, C }
+
+func test():
+ var a := Foo.A
+ var b := a as int + 1
+ print(b)
+
diff --git a/modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.out b/modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.out
new file mode 100644
index 0000000000..a7f1357bb2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.gd b/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.gd
new file mode 100644
index 0000000000..13e3edf93f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.gd
@@ -0,0 +1,9 @@
+enum HasZero { A = 0, B = 1 }
+enum HasNoZero { A = 1, B = 2 }
+var has_zero: HasZero # No warning, because the default `0` is valid.
+var has_no_zero: HasNoZero # Warning, because there is no `0` in the enum.
+
+
+func test():
+ print(has_zero)
+ print(has_no_zero)
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.out b/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.out
new file mode 100644
index 0000000000..ae40e0bc8c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+>> WARNING
+>> Line: 4
+>> ENUM_VARIABLE_WITHOUT_DEFAULT
+>> The variable "has_no_zero" has an enum type and does not set an explicit default value. The default will be set to "0".
+0
+0
diff --git a/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.gd b/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.gd
new file mode 100644
index 0000000000..cb0bc94d2e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.gd
@@ -0,0 +1,12 @@
+# GH-89403
+
+func test():
+ var x := 1
+ if x == 0 \
+ # Comment.
+ # Comment.
+ and (x < 1 or x > 2) \
+ # Comment.
+ and x != 3:
+ pass
+ print("Ok")
diff --git a/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.out b/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.out
new file mode 100644
index 0000000000..0e9f482af4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+Ok
diff --git a/modules/gdscript/tests/scripts/runtime/features/member_info.gd b/modules/gdscript/tests/scripts/runtime/features/member_info.gd
index d7485f49e6..42b29eee43 100644
--- a/modules/gdscript/tests/scripts/runtime/features/member_info.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/member_info.gd
@@ -23,6 +23,7 @@ var test_var_hard_int: int
var test_var_hard_variant_type: Variant.Type
@export var test_var_hard_variant_type_exported: Variant.Type
var test_var_hard_node_process_mode: Node.ProcessMode
+@warning_ignore("enum_variable_without_default")
var test_var_hard_my_enum: MyEnum
var test_var_hard_array: Array
var test_var_hard_array_int: Array[int]
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index 9cd5498fa8..37f319b697 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -1208,39 +1208,39 @@ namespace Godot
/// Do a simple expression match, where '*' matches zero or more
/// arbitrary characters and '?' matches any single character except '.'.
/// </summary>
- /// <param name="instance">The string to check.</param>
- /// <param name="expr">Expression to check.</param>
+ /// <param name="str">The string to check.</param>
+ /// <param name="pattern">Expression to check.</param>
/// <param name="caseSensitive">
/// If <see langword="true"/>, the check will be case sensitive.
/// </param>
/// <returns>If the expression has any matches.</returns>
- private static bool ExprMatch(this string instance, string expr, bool caseSensitive)
+ private static bool WildcardMatch(ReadOnlySpan<char> str, ReadOnlySpan<char> pattern, bool caseSensitive)
{
// case '\0':
- if (expr.Length == 0)
- return instance.Length == 0;
+ if (pattern.IsEmpty)
+ return str.IsEmpty;
- switch (expr[0])
+ switch (pattern[0])
{
case '*':
- return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 &&
- ExprMatch(instance.Substring(1), expr, caseSensitive));
+ return WildcardMatch(str, pattern.Slice(1), caseSensitive)
+ || (!str.IsEmpty && WildcardMatch(str.Slice(1), pattern, caseSensitive));
case '?':
- return instance.Length > 0 && instance[0] != '.' &&
- ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
+ return !str.IsEmpty && str[0] != '.' &&
+ WildcardMatch(str.Slice(1), pattern.Slice(1), caseSensitive);
default:
- if (instance.Length == 0)
+ if (str.IsEmpty)
return false;
- if (caseSensitive)
- return instance[0] == expr[0];
- return (char.ToUpperInvariant(instance[0]) == char.ToUpperInvariant(expr[0])) &&
- ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
+ bool charMatches = caseSensitive ?
+ str[0] == pattern[0] :
+ char.ToUpperInvariant(str[0]) == char.ToUpperInvariant(pattern[0]);
+ return charMatches &&
+ WildcardMatch(str.Slice(1), pattern.Slice(1), caseSensitive);
}
}
/// <summary>
- /// Do a simple case sensitive expression match, using ? and * wildcards
- /// (see <see cref="ExprMatch(string, string, bool)"/>).
+ /// Do a simple case sensitive expression match, using ? and * wildcards.
/// </summary>
/// <seealso cref="MatchN(string, string)"/>
/// <param name="instance">The string to check.</param>
@@ -1254,12 +1254,11 @@ namespace Godot
if (instance.Length == 0 || expr.Length == 0)
return false;
- return instance.ExprMatch(expr, caseSensitive);
+ return WildcardMatch(instance, expr, caseSensitive);
}
/// <summary>
- /// Do a simple case insensitive expression match, using ? and * wildcards
- /// (see <see cref="ExprMatch(string, string, bool)"/>).
+ /// Do a simple case insensitive expression match, using ? and * wildcards.
/// </summary>
/// <seealso cref="Match(string, string, bool)"/>
/// <param name="instance">The string to check.</param>
@@ -1270,7 +1269,7 @@ namespace Godot
if (instance.Length == 0 || expr.Length == 0)
return false;
- return instance.ExprMatch(expr, caseSensitive: false);
+ return WildcardMatch(instance, expr, caseSensitive: false);
}
/// <summary>
diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp
index d8f1170f6a..9000259524 100644
--- a/modules/navigation/2d/nav_mesh_generator_2d.cpp
+++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp
@@ -43,9 +43,9 @@
#include "scene/resources/2d/circle_shape_2d.h"
#include "scene/resources/2d/concave_polygon_shape_2d.h"
#include "scene/resources/2d/convex_polygon_shape_2d.h"
+#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
+#include "scene/resources/2d/navigation_polygon.h"
#include "scene/resources/2d/rectangle_shape_2d.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_2d.h"
-#include "scene/resources/navigation_polygon.h"
#include "thirdparty/clipper2/include/clipper2/clipper.h"
#include "thirdparty/misc/polypartition.h"
diff --git a/modules/navigation/3d/nav_mesh_generator_3d.cpp b/modules/navigation/3d/nav_mesh_generator_3d.cpp
index 11f4359146..b1b3cbed5d 100644
--- a/modules/navigation/3d/nav_mesh_generator_3d.cpp
+++ b/modules/navigation/3d/nav_mesh_generator_3d.cpp
@@ -45,12 +45,12 @@
#include "scene/resources/3d/convex_polygon_shape_3d.h"
#include "scene/resources/3d/cylinder_shape_3d.h"
#include "scene/resources/3d/height_map_shape_3d.h"
+#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "scene/resources/3d/primitive_meshes.h"
#include "scene/resources/3d/shape_3d.h"
#include "scene/resources/3d/sphere_shape_3d.h"
#include "scene/resources/3d/world_boundary_shape_3d.h"
#include "scene/resources/navigation_mesh.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
@@ -700,7 +700,7 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
cfg.detailSampleDist = MAX(p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance(), 0.1f);
cfg.detailSampleMaxError = p_navigation_mesh->get_cell_height() * p_navigation_mesh->get_detail_sample_max_error();
- if (p_navigation_mesh->get_border_size() > 0.0 && !Math::is_equal_approx(p_navigation_mesh->get_cell_size(), p_navigation_mesh->get_border_size())) {
+ if (p_navigation_mesh->get_border_size() > 0.0 && Math::fmod(p_navigation_mesh->get_border_size(), p_navigation_mesh->get_cell_size()) != 0.0) {
WARN_PRINT("Property border_size is ceiled to cell_size voxel units and loses precision.");
}
if (!Math::is_equal_approx((float)cfg.walkableHeight * cfg.ch, p_navigation_mesh->get_agent_height())) {
diff --git a/modules/navigation/3d/navigation_mesh_generator.cpp b/modules/navigation/3d/navigation_mesh_generator.cpp
index 8393896db1..54df42e266 100644
--- a/modules/navigation/3d/navigation_mesh_generator.cpp
+++ b/modules/navigation/3d/navigation_mesh_generator.cpp
@@ -32,7 +32,7 @@
#include "navigation_mesh_generator.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
+#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "servers/navigation_server_3d.h"
NavigationMeshGenerator *NavigationMeshGenerator::singleton = nullptr;
diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
index 352203f5aa..d7bf1cdd38 100644
--- a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
+++ b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
@@ -42,7 +42,6 @@
#include "scene/gui/button.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/label.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
void NavigationMeshEditor::_node_removed(Node *p_node) {
if (p_node == node) {
diff --git a/modules/openxr/doc_classes/OpenXRHand.xml b/modules/openxr/doc_classes/OpenXRHand.xml
index 9cc548dd6f..23d932ddd7 100644
--- a/modules/openxr/doc_classes/OpenXRHand.xml
+++ b/modules/openxr/doc_classes/OpenXRHand.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="OpenXRHand" inherits="SkeletonModifier3D" deprecated="Use [XRHandModifier3D] instead." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="OpenXRHand" inherits="Node3D" deprecated="Use [XRHandModifier3D] instead." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Node supporting hand and finger tracking in OpenXR.
</brief_description>
@@ -18,11 +18,14 @@
<member name="hand" type="int" setter="set_hand" getter="get_hand" enum="OpenXRHand.Hands" default="0">
Specifies whether this node tracks the left or right hand of the player.
</member>
+ <member name="hand_skeleton" type="NodePath" setter="set_hand_skeleton" getter="get_hand_skeleton" default="NodePath(&quot;&quot;)">
+ Set a [Skeleton3D] node for which the pose positions will be updated.
+ </member>
<member name="motion_range" type="int" setter="set_motion_range" getter="get_motion_range" enum="OpenXRHand.MotionRange" default="0">
Set the motion range (if supported) limiting the hand motion.
</member>
<member name="skeleton_rig" type="int" setter="set_skeleton_rig" getter="get_skeleton_rig" enum="OpenXRHand.SkeletonRig" default="0">
- Set the type of skeleton rig the parent [Skeleton3D] is compliant with.
+ Set the type of skeleton rig the [member hand_skeleton] is compliant with.
</member>
</members>
<constants>
diff --git a/modules/openxr/scene/openxr_hand.cpp b/modules/openxr/scene/openxr_hand.cpp
index f20d1f8e19..2a4104f6ee 100644
--- a/modules/openxr/scene/openxr_hand.cpp
+++ b/modules/openxr/scene/openxr_hand.cpp
@@ -40,6 +40,9 @@ void OpenXRHand::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hand", "hand"), &OpenXRHand::set_hand);
ClassDB::bind_method(D_METHOD("get_hand"), &OpenXRHand::get_hand);
+ ClassDB::bind_method(D_METHOD("set_hand_skeleton", "hand_skeleton"), &OpenXRHand::set_hand_skeleton);
+ ClassDB::bind_method(D_METHOD("get_hand_skeleton"), &OpenXRHand::get_hand_skeleton);
+
ClassDB::bind_method(D_METHOD("set_motion_range", "motion_range"), &OpenXRHand::set_motion_range);
ClassDB::bind_method(D_METHOD("get_motion_range"), &OpenXRHand::get_motion_range);
@@ -51,6 +54,7 @@ void OpenXRHand::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "hand", PROPERTY_HINT_ENUM, "Left,Right"), "set_hand", "get_hand");
ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_range", PROPERTY_HINT_ENUM, "Unobstructed,Conform to controller"), "set_motion_range", "get_motion_range");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "hand_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_hand_skeleton", "get_hand_skeleton");
ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton_rig", PROPERTY_HINT_ENUM, "OpenXR,Humanoid"), "set_skeleton_rig", "get_skeleton_rig");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update");
@@ -86,6 +90,12 @@ OpenXRHand::Hands OpenXRHand::get_hand() const {
return hand;
}
+void OpenXRHand::set_hand_skeleton(const NodePath &p_hand_skeleton) {
+ hand_skeleton = p_hand_skeleton;
+
+ // TODO if inside tree call _get_bones()
+}
+
void OpenXRHand::set_motion_range(MotionRange p_motion_range) {
ERR_FAIL_INDEX(p_motion_range, MOTION_RANGE_MAX);
motion_range = p_motion_range;
@@ -97,6 +107,10 @@ OpenXRHand::MotionRange OpenXRHand::get_motion_range() const {
return motion_range;
}
+NodePath OpenXRHand::get_hand_skeleton() const {
+ return hand_skeleton;
+}
+
void OpenXRHand::_set_motion_range() {
if (!hand_tracking_ext) {
return;
@@ -138,6 +152,20 @@ OpenXRHand::BoneUpdate OpenXRHand::get_bone_update() const {
return bone_update;
}
+Skeleton3D *OpenXRHand::get_skeleton() {
+ if (!has_node(hand_skeleton)) {
+ return nullptr;
+ }
+
+ Node *node = get_node(hand_skeleton);
+ if (!node) {
+ return nullptr;
+ }
+
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
+ return skeleton;
+}
+
void OpenXRHand::_get_joint_data() {
// Table of bone names for different rig types.
static const String bone_names[SKELETON_RIG_MAX][XR_HAND_JOINT_COUNT_EXT] = {
@@ -262,7 +290,7 @@ void OpenXRHand::_get_joint_data() {
}
}
-void OpenXRHand::_process_modification() {
+void OpenXRHand::_update_skeleton() {
if (openxr_api == nullptr || !openxr_api->is_initialized()) {
return;
} else if (hand_tracking_ext == nullptr || !hand_tracking_ext->get_active()) {
@@ -367,14 +395,21 @@ void OpenXRHand::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
_get_joint_data();
+
+ set_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
+ set_process_internal(false);
+
// reset
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
joints[i].bone = -1;
joints[i].parent_joint = -1;
}
} break;
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ _update_skeleton();
+ } break;
default: {
} break;
}
diff --git a/modules/openxr/scene/openxr_hand.h b/modules/openxr/scene/openxr_hand.h
index fc0a994f48..4c77e7277c 100644
--- a/modules/openxr/scene/openxr_hand.h
+++ b/modules/openxr/scene/openxr_hand.h
@@ -31,15 +31,16 @@
#ifndef OPENXR_HAND_H
#define OPENXR_HAND_H
-#include "scene/3d/skeleton_modifier_3d.h"
+#include "scene/3d/node_3d.h"
+#include "scene/3d/skeleton_3d.h"
#include <openxr/openxr.h>
class OpenXRAPI;
class OpenXRHandTrackingExtension;
-class OpenXRHand : public SkeletonModifier3D {
- GDCLASS(OpenXRHand, SkeletonModifier3D);
+class OpenXRHand : public Node3D {
+ GDCLASS(OpenXRHand, Node3D);
public:
enum Hands { // Deprecated, need to change this to OpenXRInterface::Hands.
@@ -85,13 +86,13 @@ private:
void _set_motion_range();
+ Skeleton3D *get_skeleton();
void _get_joint_data();
+ void _update_skeleton();
protected:
static void _bind_methods();
- virtual void _process_modification() override;
-
public:
OpenXRHand();
@@ -101,6 +102,9 @@ public:
void set_motion_range(MotionRange p_motion_range);
MotionRange get_motion_range() const;
+ void set_hand_skeleton(const NodePath &p_hand_skeleton);
+ NodePath get_hand_skeleton() const;
+
void set_skeleton_rig(SkeletonRig p_skeleton_rig);
SkeletonRig get_skeleton_rig() const;
diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp
index affe163aeb..d903137195 100644
--- a/modules/svg/image_loader_svg.cpp
+++ b/modules/svg/image_loader_svg.cpp
@@ -173,9 +173,17 @@ void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const
}
Error ImageLoaderSVG::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
- String svg = p_fileaccess->get_as_utf8_string();
+ const uint64_t len = p_fileaccess->get_length() - p_fileaccess->get_position();
+ Vector<uint8_t> buffer;
+ buffer.resize(len);
+ p_fileaccess->get_buffer(buffer.ptrw(), buffer.size());
+
+ String svg;
+ Error err = svg.parse_utf8((const char *)buffer.ptr(), buffer.size());
+ if (err != OK) {
+ return err;
+ }
- Error err;
if (p_flags & FLAG_CONVERT_COLORS) {
err = create_image_from_string(p_image, svg, p_scale, false, forced_color_map);
} else {
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index b19760026a..7cab5e8d90 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -1393,6 +1393,14 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
p_manifest = ret;
}
+String EditorExportPlatformAndroid::_get_keystore_path(const Ref<EditorExportPreset> &p_preset, bool p_debug) {
+ String keystore_preference = p_debug ? "keystore/debug" : "keystore/release";
+ String keystore_env_variable = p_debug ? ENV_ANDROID_KEYSTORE_DEBUG_PATH : ENV_ANDROID_KEYSTORE_RELEASE_PATH;
+ String keystore_path = p_preset->get_or_env(keystore_preference, keystore_env_variable);
+
+ return ProjectSettings::get_singleton()->globalize_path(keystore_path).simplify_path();
+}
+
String EditorExportPlatformAndroid::_parse_string(const uint8_t *p_bytes, bool p_utf8) {
uint32_t offset = 0;
uint32_t len = 0;
@@ -2347,10 +2355,10 @@ static bool has_valid_keystore_credentials(String &r_error_str, const String &p_
}
bool EditorExportPlatformAndroid::has_valid_username_and_password(const Ref<EditorExportPreset> &p_preset, String &r_error) {
- String dk = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
+ String dk = _get_keystore_path(p_preset, true);
String dk_user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
String dk_password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
- String rk = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
+ String rk = _get_keystore_path(p_preset, false);
String rk_user = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
String rk_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
@@ -2449,7 +2457,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
// Validate the rest of the export configuration.
- String dk = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
+ String dk = _get_keystore_path(p_preset, true);
String dk_user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
String dk_password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
@@ -2467,7 +2475,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
}
}
- String rk = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
+ String rk = _get_keystore_path(p_preset, false);
String rk_user = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
String rk_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
@@ -2724,7 +2732,7 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP
Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) {
int export_format = int(p_preset->get("gradle_build/export_format"));
String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK";
- String release_keystore = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
+ String release_keystore = _get_keystore_path(p_preset, false);
String release_username = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
String release_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
String target_sdk_version = p_preset->get("gradle_build/target_sdk");
@@ -2746,7 +2754,7 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
String password;
String user;
if (p_debug) {
- keystore = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
+ keystore = _get_keystore_path(p_preset, true);
password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
@@ -3231,7 +3239,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
if (should_sign) {
if (p_debug) {
- String debug_keystore = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
+ String debug_keystore = _get_keystore_path(p_preset, true);
String debug_password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
String debug_user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
@@ -3253,7 +3261,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
cmdline.push_back("-Pdebug_keystore_password=" + debug_password); // argument to specify the debug keystore password.
} else {
// Pass the release keystore info as well
- String release_keystore = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
+ String release_keystore = _get_keystore_path(p_preset, false);
String release_username = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
String release_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
if (release_keystore.is_relative_path()) {
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index b968302449..7bc7bbf9e9 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -166,6 +166,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet);
+ static String _get_keystore_path(const Ref<EditorExportPreset> &p_preset, bool p_debug);
+
static String _parse_string(const uint8_t *p_bytes, bool p_utf8);
void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &r_manifest);
diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm
index c989dc6221..bcf21bc802 100644
--- a/platform/ios/os_ios.mm
+++ b/platform/ios/os_ios.mm
@@ -149,10 +149,6 @@ void OS_IOS::deinitialize_modules() {
void OS_IOS::set_main_loop(MainLoop *p_main_loop) {
main_loop = p_main_loop;
-
- if (main_loop) {
- main_loop->initialize();
- }
}
MainLoop *OS_IOS::get_main_loop() const {
@@ -181,7 +177,9 @@ bool OS_IOS::iterate() {
}
void OS_IOS::start() {
- Main::start();
+ if (Main::start() == EXIT_SUCCESS) {
+ main_loop->initialize();
+ }
if (joypad_ios) {
joypad_ios->start_processing();
diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp
index d00d5deb2c..a815db1c05 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.cpp
+++ b/platform/linuxbsd/wayland/display_server_wayland.cpp
@@ -1174,14 +1174,6 @@ void DisplayServerWayland::release_rendering_thread() {
#endif
}
-void DisplayServerWayland::make_rendering_thread() {
-#ifdef GLES3_ENABLED
- if (egl_manager) {
- egl_manager->make_current();
- }
-#endif
-}
-
void DisplayServerWayland::swap_buffers() {
#ifdef GLES3_ENABLED
if (egl_manager) {
diff --git a/platform/linuxbsd/wayland/display_server_wayland.h b/platform/linuxbsd/wayland/display_server_wayland.h
index b7d7bee005..368f1b402b 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.h
+++ b/platform/linuxbsd/wayland/display_server_wayland.h
@@ -276,7 +276,6 @@ public:
virtual void process_events() override;
virtual void release_rendering_thread() override;
- virtual void make_rendering_thread() override;
virtual void swap_buffers() override;
virtual void set_context(Context p_context) override;
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 9069dd423f..4c7dfbb107 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -4268,7 +4268,7 @@ bool DisplayServerX11::_window_focus_check() {
}
void DisplayServerX11::process_events() {
- _THREAD_SAFE_METHOD_
+ _THREAD_SAFE_LOCK_
#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED
static int frame = 0;
@@ -5097,6 +5097,8 @@ void DisplayServerX11::process_events() {
*/
}
+ _THREAD_SAFE_UNLOCK_
+
Input::get_singleton()->flush_buffered_events();
}
@@ -5111,17 +5113,6 @@ void DisplayServerX11::release_rendering_thread() {
#endif
}
-void DisplayServerX11::make_rendering_thread() {
-#if defined(GLES3_ENABLED)
- if (gl_manager) {
- gl_manager->make_current();
- }
- if (gl_manager_egl) {
- gl_manager_egl->make_current();
- }
-#endif
-}
-
void DisplayServerX11::swap_buffers() {
#if defined(GLES3_ENABLED)
if (gl_manager) {
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 715a8e48e6..8a7062857c 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -526,7 +526,6 @@ public:
virtual void process_events() override;
virtual void release_rendering_thread() override;
- virtual void make_rendering_thread() override;
virtual void swap_buffers() override;
virtual void set_context(Context p_context) override;
diff --git a/platform/linuxbsd/x11/gl_manager_x11.cpp b/platform/linuxbsd/x11/gl_manager_x11.cpp
index 602dd784e0..febb7ae584 100644
--- a/platform/linuxbsd/x11/gl_manager_x11.cpp
+++ b/platform/linuxbsd/x11/gl_manager_x11.cpp
@@ -311,20 +311,6 @@ void GLManager_X11::window_make_current(DisplayServer::WindowID p_window_id) {
_internal_set_current_window(&win);
}
-void GLManager_X11::make_current() {
- if (!_current_window) {
- return;
- }
- if (!_current_window->in_use) {
- WARN_PRINT("current window not in use!");
- return;
- }
- const GLDisplay &disp = get_current_display();
- if (!glXMakeCurrent(_x_windisp.x11_display, _x_windisp.x11_window, disp.context->glx_context)) {
- ERR_PRINT("glXMakeCurrent failed");
- }
-}
-
void GLManager_X11::swap_buffers() {
if (!_current_window) {
return;
diff --git a/platform/linuxbsd/x11/gl_manager_x11.h b/platform/linuxbsd/x11/gl_manager_x11.h
index 235c7d22f9..06e147e39f 100644
--- a/platform/linuxbsd/x11/gl_manager_x11.h
+++ b/platform/linuxbsd/x11/gl_manager_x11.h
@@ -117,7 +117,6 @@ public:
void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height);
void release_current();
- void make_current();
void swap_buffers();
void window_make_current(DisplayServer::WindowID p_window_id);
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 7c9d073afc..5d38bf55ea 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -426,7 +426,6 @@ public:
virtual void force_process_and_drop_events() override;
virtual void release_rendering_thread() override;
- virtual void make_rendering_thread() override;
virtual void swap_buffers() override;
virtual void set_native_icon(const String &p_filename) override;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index b7a9fb1bbd..cfe925e79b 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -358,7 +358,6 @@ void DisplayServerMacOS::_dispatch_input_events(const Ref<InputEvent> &p_event)
}
void DisplayServerMacOS::_dispatch_input_event(const Ref<InputEvent> &p_event) {
- _THREAD_SAFE_METHOD_
if (!in_dispatch_input_event) {
in_dispatch_input_event = true;
@@ -2986,7 +2985,7 @@ Key DisplayServerMacOS::keyboard_get_label_from_physical(Key p_keycode) const {
}
void DisplayServerMacOS::process_events() {
- _THREAD_SAFE_METHOD_
+ _THREAD_SAFE_LOCK_
while (true) {
NSEvent *event = [NSApp
@@ -3019,7 +3018,9 @@ void DisplayServerMacOS::process_events() {
if (!drop_events) {
_process_key_events();
+ _THREAD_SAFE_UNLOCK_
Input::get_singleton()->flush_buffered_events();
+ _THREAD_SAFE_LOCK_
}
for (KeyValue<WindowID, WindowData> &E : windows) {
@@ -3045,6 +3046,8 @@ void DisplayServerMacOS::process_events() {
}
}
}
+
+ _THREAD_SAFE_UNLOCK_
}
void DisplayServerMacOS::force_process_and_drop_events() {
@@ -3056,9 +3059,14 @@ void DisplayServerMacOS::force_process_and_drop_events() {
}
void DisplayServerMacOS::release_rendering_thread() {
-}
-
-void DisplayServerMacOS::make_rendering_thread() {
+#if defined(GLES3_ENABLED)
+ if (gl_manager_angle) {
+ gl_manager_angle->release_current();
+ }
+ if (gl_manager_legacy) {
+ gl_manager_legacy->release_current();
+ }
+#endif
}
void DisplayServerMacOS::swap_buffers() {
diff --git a/platform/macos/gl_manager_macos_legacy.h b/platform/macos/gl_manager_macos_legacy.h
index bafe825efb..af9be8f5ba 100644
--- a/platform/macos/gl_manager_macos_legacy.h
+++ b/platform/macos/gl_manager_macos_legacy.h
@@ -73,7 +73,6 @@ public:
void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height);
void release_current();
- void make_current();
void swap_buffers();
void window_make_current(DisplayServer::WindowID p_window_id);
diff --git a/platform/macos/gl_manager_macos_legacy.mm b/platform/macos/gl_manager_macos_legacy.mm
index 701de6df78..6ce3831d9c 100644
--- a/platform/macos/gl_manager_macos_legacy.mm
+++ b/platform/macos/gl_manager_macos_legacy.mm
@@ -117,6 +117,7 @@ void GLManagerLegacy_MacOS::release_current() {
}
[NSOpenGLContext clearCurrentContext];
+ current_window = DisplayServer::INVALID_WINDOW_ID;
}
void GLManagerLegacy_MacOS::window_make_current(DisplayServer::WindowID p_window_id) {
@@ -133,18 +134,6 @@ void GLManagerLegacy_MacOS::window_make_current(DisplayServer::WindowID p_window
current_window = p_window_id;
}
-void GLManagerLegacy_MacOS::make_current() {
- if (current_window == DisplayServer::INVALID_WINDOW_ID) {
- return;
- }
- if (!windows.has(current_window)) {
- return;
- }
-
- GLWindow &win = windows[current_window];
- [win.context makeCurrentContext];
-}
-
void GLManagerLegacy_MacOS::swap_buffers() {
GLWindow &win = windows[current_window];
[win.context flushBuffer];
diff --git a/platform/macos/godot_open_save_delegate.mm b/platform/macos/godot_open_save_delegate.mm
index 6b55b70629..6ffd939545 100644
--- a/platform/macos/godot_open_save_delegate.mm
+++ b/platform/macos/godot_open_save_delegate.mm
@@ -177,14 +177,14 @@
if ([new_allowed_types count] > 0) {
NSMutableArray *type_filters = [new_allowed_types objectAtIndex:0];
if (type_filters && [type_filters count] == 1 && [[type_filters objectAtIndex:0] isEqualToString:@"*"]) {
- [p_panel setAllowedFileTypes:@[]];
+ [p_panel setAllowedFileTypes:nil];
[p_panel setAllowsOtherFileTypes:true];
} else {
[p_panel setAllowsOtherFileTypes:false];
[p_panel setAllowedFileTypes:type_filters];
}
} else {
- [p_panel setAllowedFileTypes:@[]];
+ [p_panel setAllowedFileTypes:nil];
[p_panel setAllowsOtherFileTypes:true];
}
}
@@ -248,7 +248,7 @@
if (allowed_types && index < [allowed_types count]) {
NSMutableArray *type_filters = [allowed_types objectAtIndex:index];
if (type_filters && [type_filters count] == 1 && [[type_filters objectAtIndex:0] isEqualToString:@"*"]) {
- [dialog setAllowedFileTypes:@[]];
+ [dialog setAllowedFileTypes:nil];
[dialog setAllowsOtherFileTypes:true];
} else {
[dialog setAllowsOtherFileTypes:false];
@@ -256,7 +256,7 @@
}
cur_index = index;
} else {
- [dialog setAllowedFileTypes:@[]];
+ [dialog setAllowedFileTypes:nil];
[dialog setAllowsOtherFileTypes:true];
cur_index = -1;
}
diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp
index 8fc89a79d9..4158295520 100644
--- a/platform/web/os_web.cpp
+++ b/platform/web/os_web.cpp
@@ -132,6 +132,10 @@ bool OS_Web::is_process_running(const ProcessID &p_pid) const {
return false;
}
+int OS_Web::get_process_exit_code(const ProcessID &p_pid) const {
+ return -1;
+}
+
int OS_Web::get_processor_count() const {
return godot_js_os_hw_concurrency_get();
}
diff --git a/platform/web/os_web.h b/platform/web/os_web.h
index 537b05048a..eeeafdac34 100644
--- a/platform/web/os_web.h
+++ b/platform/web/os_web.h
@@ -85,6 +85,7 @@ public:
Error kill(const ProcessID &p_pid) override;
int get_process_id() const override;
bool is_process_running(const ProcessID &p_pid) const override;
+ int get_process_exit_code(const ProcessID &p_pid) const override;
int get_processor_count() const override;
String get_unique_id() const override;
int get_default_thread_pool_size() const override { return 1; }
diff --git a/platform/web/serve.py b/platform/web/serve.py
index 6a3efcc463..89dff63ca3 100755
--- a/platform/web/serve.py
+++ b/platform/web/serve.py
@@ -5,9 +5,20 @@ from pathlib import Path
import os
import sys
import argparse
+import contextlib
+import socket
import subprocess
+# See cpython GH-17851 and GH-17864.
+class DualStackServer(HTTPServer):
+ def server_bind(self):
+ # Suppress exception when protocol is IPv4.
+ with contextlib.suppress(Exception):
+ self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
+ return super().server_bind()
+
+
class CORSRequestHandler(SimpleHTTPRequestHandler):
def end_headers(self):
self.send_header("Cross-Origin-Opener-Policy", "same-origin")
@@ -32,7 +43,7 @@ def serve(root, port, run_browser):
print("Opening the served URL in the default browser (use `--no-browser` or `-n` to disable this).")
shell_open(f"http://127.0.0.1:{port}")
- test(CORSRequestHandler, HTTPServer, port=port)
+ test(CORSRequestHandler, DualStackServer, port=port)
if __name__ == "__main__":
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 9aa32a7b2d..f34d479345 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -411,10 +411,13 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
ret = old_spawn(sh, escape, cmd, args, env)
try:
- with open(tmp_stdout_name, "rt", encoding=sys.stdout.encoding) as tmp_stdout:
- # First line is always bloat, subsequent lines are always errors. This filter sends
- # either just the errors to stderr, or an empty string to effectively do nothing.
- sys.stderr.write("".join(tmp_stdout.readlines()[1:]))
+ with open(tmp_stdout_name, "rb") as tmp_stdout:
+ # First line is always bloat, subsequent lines are always errors. If content
+ # exists after discarding the first line, safely decode & send to stderr.
+ tmp_stdout.readline()
+ content = tmp_stdout.read()
+ if content:
+ sys.stderr.write(content.decode(sys.stdout.encoding, "replace"))
os.remove(tmp_stdout_name)
except OSError:
pass
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 74665664b1..b6b713687f 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -2962,7 +2962,7 @@ String DisplayServerWindows::keyboard_get_layout_name(int p_index) const {
}
void DisplayServerWindows::process_events() {
- _THREAD_SAFE_METHOD_
+ _THREAD_SAFE_LOCK_
MSG msg;
@@ -2977,7 +2977,10 @@ void DisplayServerWindows::process_events() {
if (!drop_events) {
_process_key_events();
+ _THREAD_SAFE_UNLOCK_
Input::get_singleton()->flush_buffered_events();
+ } else {
+ _THREAD_SAFE_UNLOCK_
}
}
@@ -2990,9 +2993,14 @@ void DisplayServerWindows::force_process_and_drop_events() {
}
void DisplayServerWindows::release_rendering_thread() {
-}
-
-void DisplayServerWindows::make_rendering_thread() {
+#if defined(GLES3_ENABLED)
+ if (gl_manager_angle) {
+ gl_manager_angle->release_current();
+ }
+ if (gl_manager_native) {
+ gl_manager_native->release_current();
+ }
+#endif
}
void DisplayServerWindows::swap_buffers() {
@@ -3433,7 +3441,6 @@ void DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> &p_event
}
void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) {
- _THREAD_SAFE_METHOD_
if (in_dispatch_input_event) {
return;
}
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 4792b65801..2fe1b0733d 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -679,7 +679,6 @@ public:
virtual void force_process_and_drop_events() override;
virtual void release_rendering_thread() override;
- virtual void make_rendering_thread() override;
virtual void swap_buffers() override;
virtual void set_native_icon(const String &p_filename) override;
diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp
index da837b3f94..80cf818199 100644
--- a/platform/windows/gl_manager_windows_native.cpp
+++ b/platform/windows/gl_manager_windows_native.cpp
@@ -427,10 +427,6 @@ Error GLManagerNative_Windows::window_create(DisplayServer::WindowID p_window_id
return OK;
}
-void GLManagerNative_Windows::_internal_set_current_window(GLWindow *p_win) {
- _current_window = p_win;
-}
-
void GLManagerNative_Windows::window_destroy(DisplayServer::WindowID p_window_id) {
GLWindow &win = get_window(p_window_id);
if (_current_window == &win) {
@@ -447,6 +443,8 @@ void GLManagerNative_Windows::release_current() {
if (!gd_wglMakeCurrent(_current_window->hDC, nullptr)) {
ERR_PRINT("Could not detach OpenGL context from window marked current: " + format_error_message(GetLastError()));
}
+
+ _current_window = nullptr;
}
void GLManagerNative_Windows::window_make_current(DisplayServer::WindowID p_window_id) {
@@ -467,17 +465,7 @@ void GLManagerNative_Windows::window_make_current(DisplayServer::WindowID p_wind
ERR_PRINT("Could not switch OpenGL context to other window: " + format_error_message(GetLastError()));
}
- _internal_set_current_window(&win);
-}
-
-void GLManagerNative_Windows::make_current() {
- if (!_current_window) {
- return;
- }
- const GLDisplay &disp = get_current_display();
- if (!gd_wglMakeCurrent(_current_window->hDC, disp.hRC)) {
- ERR_PRINT("Could not switch OpenGL context to window marked current: " + format_error_message(GetLastError()));
- }
+ _current_window = &win;
}
void GLManagerNative_Windows::swap_buffers() {
@@ -491,7 +479,6 @@ Error GLManagerNative_Windows::initialize() {
void GLManagerNative_Windows::set_use_vsync(DisplayServer::WindowID p_window_id, bool p_use) {
GLWindow &win = get_window(p_window_id);
- GLWindow *current = _current_window;
if (&win != _current_window) {
window_make_current(p_window_id);
@@ -506,11 +493,6 @@ void GLManagerNative_Windows::set_use_vsync(DisplayServer::WindowID p_window_id,
} else {
WARN_PRINT("Could not set V-Sync mode. V-Sync is not supported.");
}
-
- if (current != _current_window) {
- _current_window = current;
- make_current();
- }
}
bool GLManagerNative_Windows::is_using_vsync(DisplayServer::WindowID p_window_id) const {
diff --git a/platform/windows/gl_manager_windows_native.h b/platform/windows/gl_manager_windows_native.h
index 829c53b3d2..b4e2a3acdf 100644
--- a/platform/windows/gl_manager_windows_native.h
+++ b/platform/windows/gl_manager_windows_native.h
@@ -68,9 +68,6 @@ private:
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr;
- // funcs
- void _internal_set_current_window(GLWindow *p_win);
-
GLWindow &get_window(unsigned int id) { return _windows[id]; }
const GLWindow &get_window(unsigned int id) const { return _windows[id]; }
@@ -91,7 +88,6 @@ public:
void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {}
void release_current();
- void make_current();
void swap_buffers();
void window_make_current(DisplayServer::WindowID p_window_id);
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index e0e35c6e0f..f2a9989606 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -867,7 +867,9 @@ Dictionary OS_Windows::execute_with_pipe(const String &p_path, const List<String
CloseHandle(pipe_err[1]);
ProcessID pid = pi.pi.dwProcessId;
+ process_map_mutex.lock();
process_map->insert(pid, pi);
+ process_map_mutex.unlock();
Ref<FileAccessWindowsPipe> main_pipe;
main_pipe.instantiate();
@@ -1014,13 +1016,16 @@ Error OS_Windows::create_process(const String &p_path, const List<String> &p_arg
if (r_child_id) {
*r_child_id = pid;
}
+ process_map_mutex.lock();
process_map->insert(pid, pi);
+ process_map_mutex.unlock();
return OK;
}
Error OS_Windows::kill(const ProcessID &p_pid) {
int ret = 0;
+ MutexLock lock(process_map_mutex);
if (process_map->has(p_pid)) {
const PROCESS_INFORMATION pi = (*process_map)[p_pid].pi;
process_map->erase(p_pid);
@@ -1046,24 +1051,58 @@ int OS_Windows::get_process_id() const {
}
bool OS_Windows::is_process_running(const ProcessID &p_pid) const {
+ MutexLock lock(process_map_mutex);
if (!process_map->has(p_pid)) {
return false;
}
- const PROCESS_INFORMATION &pi = (*process_map)[p_pid].pi;
+ const ProcessInfo &info = (*process_map)[p_pid];
+ if (!info.is_running) {
+ return false;
+ }
+ const PROCESS_INFORMATION &pi = info.pi;
DWORD dw_exit_code = 0;
if (!GetExitCodeProcess(pi.hProcess, &dw_exit_code)) {
return false;
}
if (dw_exit_code != STILL_ACTIVE) {
+ info.is_running = false;
+ info.exit_code = dw_exit_code;
return false;
}
return true;
}
+int OS_Windows::get_process_exit_code(const ProcessID &p_pid) const {
+ MutexLock lock(process_map_mutex);
+ if (!process_map->has(p_pid)) {
+ return -1;
+ }
+
+ const ProcessInfo &info = (*process_map)[p_pid];
+ if (!info.is_running) {
+ return info.exit_code;
+ }
+
+ const PROCESS_INFORMATION &pi = info.pi;
+
+ DWORD dw_exit_code = 0;
+ if (!GetExitCodeProcess(pi.hProcess, &dw_exit_code)) {
+ return -1;
+ }
+
+ if (dw_exit_code == STILL_ACTIVE) {
+ return -1;
+ }
+
+ info.is_running = false;
+ info.exit_code = dw_exit_code;
+ return dw_exit_code;
+}
+
Error OS_Windows::set_cwd(const String &p_cwd) {
if (_wchdir((LPCWSTR)(p_cwd.utf16().get_data())) != 0) {
return ERR_CANT_OPEN;
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index c506af13b2..288154745f 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -150,8 +150,11 @@ protected:
struct ProcessInfo {
STARTUPINFO si;
PROCESS_INFORMATION pi;
+ mutable bool is_running = true;
+ mutable int exit_code = -1;
};
HashMap<ProcessID, ProcessInfo> *process_map = nullptr;
+ Mutex process_map_mutex;
public:
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
@@ -189,6 +192,7 @@ public:
virtual Error kill(const ProcessID &p_pid) override;
virtual int get_process_id() const override;
virtual bool is_process_running(const ProcessID &p_pid) const override;
+ virtual int get_process_exit_code(const ProcessID &p_pid) const override;
virtual bool has_environment(const String &p_var) const override;
virtual String get_environment(const String &p_var) const override;
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index 5510b59903..ab44e57d05 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -31,7 +31,6 @@
#include "navigation_region_2d.h"
#include "core/math/geometry_2d.h"
-#include "scene/2d/navigation_obstacle_2d.h"
#include "scene/resources/world_2d.h"
#include "servers/navigation_server_2d.h"
@@ -212,11 +211,6 @@ void NavigationRegion2D::set_navigation_map(RID p_navigation_map) {
map_override = p_navigation_map;
NavigationServer2D::get_singleton()->region_set_map(region, map_override);
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], map_override);
- }
- }
}
RID NavigationRegion2D::get_navigation_map() const {
@@ -265,7 +259,6 @@ void NavigationRegion2D::_navigation_polygon_changed() {
if (navigation_polygon.is_valid()) {
NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, navigation_polygon);
}
- _update_avoidance_constrain();
}
#ifdef DEBUG_ENABLED
@@ -317,13 +310,6 @@ void NavigationRegion2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationRegion2D::set_navigation_layer_value);
ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationRegion2D::get_navigation_layer_value);
- ClassDB::bind_method(D_METHOD("set_constrain_avoidance", "enabled"), &NavigationRegion2D::set_constrain_avoidance);
- ClassDB::bind_method(D_METHOD("get_constrain_avoidance"), &NavigationRegion2D::get_constrain_avoidance);
- ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationRegion2D::set_avoidance_layers);
- ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationRegion2D::get_avoidance_layers);
- ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationRegion2D::set_avoidance_layer_value);
- ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationRegion2D::get_avoidance_layer_value);
-
ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationRegion2D::get_region_rid);
ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationRegion2D::set_enter_cost);
@@ -343,8 +329,6 @@ void NavigationRegion2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constrain_avoidance"), "set_constrain_avoidance", "get_constrain_avoidance");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
ADD_SIGNAL(MethodInfo("navigation_polygon_changed"));
ADD_SIGNAL(MethodInfo("bake_finished"));
@@ -391,131 +375,12 @@ NavigationRegion2D::~NavigationRegion2D() {
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
NavigationServer2D::get_singleton()->free(region);
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->free(constrain_avoidance_obstacles[i]);
- }
- }
- constrain_avoidance_obstacles.clear();
-
#ifdef DEBUG_ENABLED
NavigationServer2D::get_singleton()->disconnect(SNAME("map_changed"), callable_mp(this, &NavigationRegion2D::_navigation_map_changed));
NavigationServer2D::get_singleton()->disconnect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationRegion2D::_navigation_debug_changed));
#endif // DEBUG_ENABLED
}
-void NavigationRegion2D::_update_avoidance_constrain() {
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->free(constrain_avoidance_obstacles[i]);
- constrain_avoidance_obstacles[i] = RID();
- }
- }
- constrain_avoidance_obstacles.clear();
-
- if (!constrain_avoidance) {
- return;
- }
-
- if (get_navigation_polygon() == nullptr) {
- return;
- }
-
- Ref<NavigationPolygon> _navpoly = get_navigation_polygon();
- int _outline_count = _navpoly->get_outline_count();
- if (_outline_count == 0) {
- return;
- }
-
- for (int outline_index(0); outline_index < _outline_count; outline_index++) {
- const Vector<Vector2> &_outline = _navpoly->get_outline(outline_index);
-
- const int outline_size = _outline.size();
- if (outline_size < 3) {
- ERR_FAIL_COND_MSG(_outline.size() < 3, "NavigationPolygon outline needs to have at least 3 vertex to create avoidance obstacles to constrain avoidance agent's");
- continue;
- }
-
- RID obstacle_rid = NavigationServer2D::get_singleton()->obstacle_create();
- constrain_avoidance_obstacles.push_back(obstacle_rid);
-
- Vector<Vector2> new_obstacle_outline;
-
- if (outline_index == 0) {
- for (int i(0); i < outline_size; i++) {
- new_obstacle_outline.push_back(_outline[outline_size - i - 1]);
- }
- ERR_FAIL_COND_MSG(Geometry2D::is_polygon_clockwise(_outline), "Outer most outline needs to be clockwise to push avoidance agent inside");
- } else {
- for (int i(0); i < outline_size; i++) {
- new_obstacle_outline.push_back(_outline[i]);
- }
- }
- new_obstacle_outline.resize(outline_size);
-
- NavigationServer2D::get_singleton()->obstacle_set_vertices(obstacle_rid, new_obstacle_outline);
- NavigationServer2D::get_singleton()->obstacle_set_avoidance_layers(obstacle_rid, avoidance_layers);
- if (is_inside_tree()) {
- if (map_override.is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_map(obstacle_rid, map_override);
- } else {
- NavigationServer2D::get_singleton()->obstacle_set_map(obstacle_rid, get_world_2d()->get_navigation_map());
- }
- NavigationServer2D::get_singleton()->obstacle_set_position(obstacle_rid, get_global_position());
- }
- }
- constrain_avoidance_obstacles.resize(_outline_count);
-}
-
-void NavigationRegion2D::set_constrain_avoidance(bool p_enabled) {
- constrain_avoidance = p_enabled;
- _update_avoidance_constrain();
- notify_property_list_changed();
-}
-
-bool NavigationRegion2D::get_constrain_avoidance() const {
- return constrain_avoidance;
-}
-
-void NavigationRegion2D::_validate_property(PropertyInfo &p_property) const {
- if (p_property.name == "avoidance_layers") {
- if (!constrain_avoidance) {
- p_property.usage = PROPERTY_USAGE_NO_EDITOR;
- }
- }
-}
-
-void NavigationRegion2D::set_avoidance_layers(uint32_t p_layers) {
- avoidance_layers = p_layers;
- if (constrain_avoidance_obstacles.size() > 0) {
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- NavigationServer2D::get_singleton()->obstacle_set_avoidance_layers(constrain_avoidance_obstacles[i], avoidance_layers);
- }
- }
-}
-
-uint32_t NavigationRegion2D::get_avoidance_layers() const {
- return avoidance_layers;
-}
-
-void NavigationRegion2D::set_avoidance_layer_value(int p_layer_number, bool p_value) {
- ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
- ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
- uint32_t avoidance_layers_new = get_avoidance_layers();
- if (p_value) {
- avoidance_layers_new |= 1 << (p_layer_number - 1);
- } else {
- avoidance_layers_new &= ~(1 << (p_layer_number - 1));
- }
- set_avoidance_layers(avoidance_layers_new);
-}
-
-bool NavigationRegion2D::get_avoidance_layer_value(int p_layer_number) const {
- ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
- ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
- return get_avoidance_layers() & (1 << (p_layer_number - 1));
-}
-
void NavigationRegion2D::_region_enter_navigation_map() {
if (!is_inside_tree()) {
return;
@@ -523,27 +388,12 @@ void NavigationRegion2D::_region_enter_navigation_map() {
if (map_override.is_valid()) {
NavigationServer2D::get_singleton()->region_set_map(region, map_override);
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], map_override);
- }
- }
} else {
NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], get_world_2d()->get_navigation_map());
- }
- }
}
current_global_transform = get_global_transform();
NavigationServer2D::get_singleton()->region_set_transform(region, current_global_transform);
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_position(constrain_avoidance_obstacles[i], get_global_position());
- }
- }
NavigationServer2D::get_singleton()->region_set_enabled(region, enabled);
@@ -552,11 +402,6 @@ void NavigationRegion2D::_region_enter_navigation_map() {
void NavigationRegion2D::_region_exit_navigation_map() {
NavigationServer2D::get_singleton()->region_set_map(region, RID());
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], RID());
- }
- }
}
void NavigationRegion2D::_region_update_transform() {
@@ -568,11 +413,6 @@ void NavigationRegion2D::_region_update_transform() {
if (current_global_transform != new_global_transform) {
current_global_transform = new_global_transform;
NavigationServer2D::get_singleton()->region_set_transform(region, current_global_transform);
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_position(constrain_avoidance_obstacles[i], get_global_position());
- }
- }
}
queue_redraw();
diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h
index 87c2365b15..5a86dd607d 100644
--- a/scene/2d/navigation_region_2d.h
+++ b/scene/2d/navigation_region_2d.h
@@ -31,7 +31,7 @@
#ifndef NAVIGATION_REGION_2D_H
#define NAVIGATION_REGION_2D_H
-#include "scene/resources/navigation_polygon.h"
+#include "scene/resources/2d/navigation_polygon.h"
class NavigationRegion2D : public Node2D {
GDCLASS(NavigationRegion2D, Node2D);
@@ -46,10 +46,6 @@ class NavigationRegion2D : public Node2D {
real_t travel_cost = 1.0;
Ref<NavigationPolygon> navigation_polygon;
- bool constrain_avoidance = false;
- LocalVector<RID> constrain_avoidance_obstacles;
- uint32_t avoidance_layers = 1;
-
Transform2D current_global_transform;
void _navigation_polygon_changed();
@@ -65,7 +61,6 @@ private:
protected:
void _notification(int p_what);
- void _validate_property(PropertyInfo &p_property) const;
static void _bind_methods();
#ifndef DISABLE_DEPRECATED
@@ -106,15 +101,6 @@ public:
void set_navigation_polygon(const Ref<NavigationPolygon> &p_navigation_polygon);
Ref<NavigationPolygon> get_navigation_polygon() const;
- void set_constrain_avoidance(bool p_enabled);
- bool get_constrain_avoidance() const;
-
- void set_avoidance_layers(uint32_t p_layers);
- uint32_t get_avoidance_layers() const;
-
- void set_avoidance_layer_value(int p_layer_number, bool p_value);
- bool get_avoidance_layer_value(int p_layer_number) const;
-
PackedStringArray get_configuration_warnings() const override;
void bake_navigation_polygon(bool p_on_thread);
@@ -125,7 +111,6 @@ public:
~NavigationRegion2D();
private:
- void _update_avoidance_constrain();
void _region_enter_navigation_map();
void _region_exit_navigation_map();
void _region_update_transform();
diff --git a/scene/2d/parallax_2d.cpp b/scene/2d/parallax_2d.cpp
index 7e47e0f061..555f3b031c 100644
--- a/scene/2d/parallax_2d.cpp
+++ b/scene/2d/parallax_2d.cpp
@@ -102,14 +102,14 @@ void Parallax2D::_update_scroll() {
scroll_ofs *= scroll_scale;
if (repeat_size.x) {
- real_t mod = Math::fposmod(scroll_ofs.x - scroll_offset.x - autoscroll_offset.x, repeat_size.x);
+ real_t mod = Math::fposmod(scroll_ofs.x - scroll_offset.x - autoscroll_offset.x, repeat_size.x * get_scale().x);
scroll_ofs.x = screen_offset.x - mod;
} else {
scroll_ofs.x = screen_offset.x + scroll_offset.x - scroll_ofs.x;
}
if (repeat_size.y) {
- real_t mod = Math::fposmod(scroll_ofs.y - scroll_offset.y - autoscroll_offset.y, repeat_size.y);
+ real_t mod = Math::fposmod(scroll_ofs.y - scroll_offset.y - autoscroll_offset.y, repeat_size.y * get_scale().y);
scroll_ofs.y = screen_offset.y - mod;
} else {
scroll_ofs.y = screen_offset.y + scroll_offset.y - scroll_ofs.y;
@@ -127,8 +127,7 @@ void Parallax2D::_update_repeat() {
return;
}
- Point2 repeat_scale = repeat_size * get_scale();
- RenderingServer::get_singleton()->canvas_set_item_repeat(get_canvas_item(), repeat_scale, repeat_times);
+ RenderingServer::get_singleton()->canvas_set_item_repeat(get_canvas_item(), repeat_size, repeat_times);
RenderingServer::get_singleton()->canvas_item_set_interpolated(get_canvas_item(), false);
}
diff --git a/scene/2d/physics/character_body_2d.cpp b/scene/2d/physics/character_body_2d.cpp
index e5d575a159..a503f3cb78 100644
--- a/scene/2d/physics/character_body_2d.cpp
+++ b/scene/2d/physics/character_body_2d.cpp
@@ -501,7 +501,7 @@ Ref<KinematicCollision2D> CharacterBody2D::_get_slide_collision(int p_bounce) {
// Create a new instance when the cached reference is invalid or still in use in script.
if (slide_colliders[p_bounce].is_null() || slide_colliders[p_bounce]->get_reference_count() > 1) {
slide_colliders.write[p_bounce].instantiate();
- slide_colliders.write[p_bounce]->owner = this;
+ slide_colliders.write[p_bounce]->owner_id = get_instance_id();
}
slide_colliders.write[p_bounce]->result = motion_results[p_bounce];
@@ -745,11 +745,3 @@ void CharacterBody2D::_bind_methods() {
CharacterBody2D::CharacterBody2D() :
PhysicsBody2D(PhysicsServer2D::BODY_MODE_KINEMATIC) {
}
-
-CharacterBody2D::~CharacterBody2D() {
- for (int i = 0; i < slide_colliders.size(); i++) {
- if (slide_colliders[i].is_valid()) {
- slide_colliders.write[i]->owner = nullptr;
- }
- }
-}
diff --git a/scene/2d/physics/character_body_2d.h b/scene/2d/physics/character_body_2d.h
index 395438a1f1..536d0a1ebd 100644
--- a/scene/2d/physics/character_body_2d.h
+++ b/scene/2d/physics/character_body_2d.h
@@ -111,7 +111,6 @@ public:
PlatformOnLeave get_platform_on_leave() const;
CharacterBody2D();
- ~CharacterBody2D();
private:
real_t margin = 0.08;
diff --git a/scene/2d/physics/kinematic_collision_2d.cpp b/scene/2d/physics/kinematic_collision_2d.cpp
index 7e7c33b259..18b0254769 100644
--- a/scene/2d/physics/kinematic_collision_2d.cpp
+++ b/scene/2d/physics/kinematic_collision_2d.cpp
@@ -59,6 +59,7 @@ real_t KinematicCollision2D::get_depth() const {
}
Object *KinematicCollision2D::get_local_shape() const {
+ PhysicsBody2D *owner = Object::cast_to<PhysicsBody2D>(ObjectDB::get_instance(owner_id));
if (!owner) {
return nullptr;
}
diff --git a/scene/2d/physics/kinematic_collision_2d.h b/scene/2d/physics/kinematic_collision_2d.h
index 0d187b87a5..8d3d6ca8c1 100644
--- a/scene/2d/physics/kinematic_collision_2d.h
+++ b/scene/2d/physics/kinematic_collision_2d.h
@@ -40,7 +40,7 @@ class PhysicsBody2D;
class KinematicCollision2D : public RefCounted {
GDCLASS(KinematicCollision2D, RefCounted);
- PhysicsBody2D *owner = nullptr;
+ ObjectID owner_id;
friend class PhysicsBody2D;
friend class CharacterBody2D;
PhysicsServer2D::MotionResult result;
diff --git a/scene/2d/physics/physics_body_2d.cpp b/scene/2d/physics/physics_body_2d.cpp
index 81120d0b01..fc14e6ed62 100644
--- a/scene/2d/physics/physics_body_2d.cpp
+++ b/scene/2d/physics/physics_body_2d.cpp
@@ -30,8 +30,6 @@
#include "physics_body_2d.h"
-#include "scene/scene_string_names.h"
-
void PhysicsBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_collide", "motion", "test_only", "safe_margin", "recovery_as_collision"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08), DEFVAL(false));
ClassDB::bind_method(D_METHOD("test_move", "from", "motion", "collision", "safe_margin", "recovery_as_collision"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08), DEFVAL(false));
@@ -48,12 +46,6 @@ PhysicsBody2D::PhysicsBody2D(PhysicsServer2D::BodyMode p_mode) :
set_pickable(false);
}
-PhysicsBody2D::~PhysicsBody2D() {
- if (motion_cache.is_valid()) {
- motion_cache->owner = nullptr;
- }
-}
-
Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_test_only, real_t p_margin, bool p_recovery_as_collision) {
PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_motion, p_margin);
parameters.recovery_as_collision = p_recovery_as_collision;
@@ -64,7 +56,7 @@ Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_t
// Create a new instance when the cached reference is invalid or still in use in script.
if (motion_cache.is_null() || motion_cache->get_reference_count() > 1) {
motion_cache.instantiate();
- motion_cache->owner = this;
+ motion_cache->owner_id = get_instance_id();
}
motion_cache->result = result;
diff --git a/scene/2d/physics/physics_body_2d.h b/scene/2d/physics/physics_body_2d.h
index 43bc479881..d44eebabee 100644
--- a/scene/2d/physics/physics_body_2d.h
+++ b/scene/2d/physics/physics_body_2d.h
@@ -56,8 +56,6 @@ public:
TypedArray<PhysicsBody2D> get_collision_exceptions();
void add_collision_exception_with(Node *p_node); //must be physicsbody
void remove_collision_exception_with(Node *p_node);
-
- virtual ~PhysicsBody2D();
};
#endif // PHYSICS_BODY_2D_H
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index bbf1d09bbc..84d3a5f7fa 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -429,6 +429,7 @@ Color TileMap::get_layer_modulate(int p_layer) const {
void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) {
TILEMAP_CALL_FOR_LAYER(p_layer, set_y_sort_enabled, p_y_sort_enabled);
+ update_configuration_warnings();
}
bool TileMap::is_layer_y_sort_enabled(int p_layer) const {
@@ -437,6 +438,7 @@ bool TileMap::is_layer_y_sort_enabled(int p_layer) const {
void TileMap::set_layer_y_sort_origin(int p_layer, int p_y_sort_origin) {
TILEMAP_CALL_FOR_LAYER(p_layer, set_y_sort_origin, p_y_sort_origin);
+ update_configuration_warnings();
}
int TileMap::get_layer_y_sort_origin(int p_layer) const {
@@ -518,9 +520,6 @@ void TileMap::set_y_sort_enabled(bool p_enable) {
return;
}
Node2D::set_y_sort_enabled(p_enable);
- for (TileMapLayer *layer : layers) {
- layer->set_y_sort_enabled(p_enable);
- }
_emit_changed();
update_configuration_warnings();
}
diff --git a/scene/3d/bone_attachment_3d.compat.inc b/scene/3d/bone_attachment_3d.compat.inc
new file mode 100644
index 0000000000..1b21612d5f
--- /dev/null
+++ b/scene/3d/bone_attachment_3d.compat.inc
@@ -0,0 +1,43 @@
+/**************************************************************************/
+/* bone_attachment_3d.compat.inc */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef DISABLE_DEPRECATED
+
+#include "bone_attachment_3d.h"
+
+void BoneAttachment3D::_on_bone_pose_update_bind_compat_90575(int p_bone_index) {
+ return on_skeleton_update();
+}
+
+void BoneAttachment3D::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("on_bone_pose_update", "bone_index"), &BoneAttachment3D::_on_bone_pose_update_bind_compat_90575);
+}
+
+#endif // DISABLE_DEPRECATED
diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index 221200284d..6afbc68e42 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "bone_attachment_3d.h"
+#include "bone_attachment_3d.compat.inc"
void BoneAttachment3D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "bone_name") {
@@ -148,9 +149,9 @@ void BoneAttachment3D::_check_bind() {
bone_idx = sk->find_bone(bone_name);
}
if (bone_idx != -1) {
- sk->connect(SNAME("bone_pose_changed"), callable_mp(this, &BoneAttachment3D::on_bone_pose_update));
+ sk->connect(SNAME("skeleton_updated"), callable_mp(this, &BoneAttachment3D::on_skeleton_update));
bound = true;
- callable_mp(this, &BoneAttachment3D::on_bone_pose_update).call_deferred(bone_idx);
+ callable_mp(this, &BoneAttachment3D::on_skeleton_update);
}
}
}
@@ -176,7 +177,7 @@ void BoneAttachment3D::_check_unbind() {
Skeleton3D *sk = _get_skeleton3d();
if (sk) {
- sk->disconnect(SNAME("bone_pose_changed"), callable_mp(this, &BoneAttachment3D::on_bone_pose_update));
+ sk->disconnect(SNAME("skeleton_updated"), callable_mp(this, &BoneAttachment3D::on_skeleton_update));
}
bound = false;
}
@@ -308,12 +309,12 @@ void BoneAttachment3D::_notification(int p_what) {
}
}
-void BoneAttachment3D::on_bone_pose_update(int p_bone_index) {
+void BoneAttachment3D::on_skeleton_update() {
if (updating) {
return;
}
updating = true;
- if (bone_idx == p_bone_index) {
+ if (bone_idx >= 0) {
Skeleton3D *sk = _get_skeleton3d();
if (sk) {
if (!override_pose) {
@@ -371,7 +372,7 @@ void BoneAttachment3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bone_idx", "bone_idx"), &BoneAttachment3D::set_bone_idx);
ClassDB::bind_method(D_METHOD("get_bone_idx"), &BoneAttachment3D::get_bone_idx);
- ClassDB::bind_method(D_METHOD("on_bone_pose_update", "bone_index"), &BoneAttachment3D::on_bone_pose_update);
+ ClassDB::bind_method(D_METHOD("on_skeleton_update"), &BoneAttachment3D::on_skeleton_update);
ClassDB::bind_method(D_METHOD("set_override_pose", "override_pose"), &BoneAttachment3D::set_override_pose);
ClassDB::bind_method(D_METHOD("get_override_pose"), &BoneAttachment3D::get_override_pose);
diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h
index ec0f7344d7..e19180b0ea 100644
--- a/scene/3d/bone_attachment_3d.h
+++ b/scene/3d/bone_attachment_3d.h
@@ -67,6 +67,10 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ virtual void _on_bone_pose_update_bind_compat_90575(int p_bone_index);
+ static void _bind_compatibility_methods();
+#endif
public:
#ifdef TOOLS_ENABLED
@@ -89,7 +93,7 @@ public:
void set_external_skeleton(NodePath p_skeleton);
NodePath get_external_skeleton() const;
- virtual void on_bone_pose_update(int p_bone_index);
+ virtual void on_skeleton_update();
#ifdef TOOLS_ENABLED
virtual void notify_rebind_required();
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index e7b6aa1dba..0dc9834539 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -881,9 +881,9 @@ void CPUParticles3D::_particles_process(double p_delta) {
case EMISSION_SHAPE_RING: {
real_t ring_random_angle = Math::randf() * Math_TAU;
real_t ring_random_radius = Math::randf() * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius;
- Vector3 axis = emission_ring_axis.normalized();
+ Vector3 axis = emission_ring_axis == Vector3(0.0, 0.0, 0.0) ? Vector3(0.0, 0.0, 1.0) : emission_ring_axis.normalized();
Vector3 ortho_axis;
- if (axis == Vector3(1.0, 0.0, 0.0)) {
+ if (axis.abs() == Vector3(1.0, 0.0, 0.0)) {
ortho_axis = Vector3(0.0, 1.0, 0.0).cross(axis);
} else {
ortho_axis = Vector3(1.0, 0.0, 0.0).cross(axis);
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index d8a63c60a2..856c52b5d5 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -30,7 +30,7 @@
#include "navigation_region_3d.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
+#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "servers/navigation_server_3d.h"
RID NavigationRegion3D::get_rid() const {
diff --git a/scene/3d/physics/character_body_3d.cpp b/scene/3d/physics/character_body_3d.cpp
index 6759033358..b13c279234 100644
--- a/scene/3d/physics/character_body_3d.cpp
+++ b/scene/3d/physics/character_body_3d.cpp
@@ -704,7 +704,7 @@ Ref<KinematicCollision3D> CharacterBody3D::_get_slide_collision(int p_bounce) {
// Create a new instance when the cached reference is invalid or still in use in script.
if (slide_colliders[p_bounce].is_null() || slide_colliders[p_bounce]->get_reference_count() > 1) {
slide_colliders.write[p_bounce].instantiate();
- slide_colliders.write[p_bounce]->owner = this;
+ slide_colliders.write[p_bounce]->owner_id = get_instance_id();
}
slide_colliders.write[p_bounce]->result = motion_results[p_bounce];
@@ -936,11 +936,3 @@ void CharacterBody3D::_validate_property(PropertyInfo &p_property) const {
CharacterBody3D::CharacterBody3D() :
PhysicsBody3D(PhysicsServer3D::BODY_MODE_KINEMATIC) {
}
-
-CharacterBody3D::~CharacterBody3D() {
- for (int i = 0; i < slide_colliders.size(); i++) {
- if (slide_colliders[i].is_valid()) {
- slide_colliders.write[i]->owner = nullptr;
- }
- }
-}
diff --git a/scene/3d/physics/character_body_3d.h b/scene/3d/physics/character_body_3d.h
index cffc0f8e63..430d9c35cb 100644
--- a/scene/3d/physics/character_body_3d.h
+++ b/scene/3d/physics/character_body_3d.h
@@ -113,7 +113,6 @@ public:
PlatformOnLeave get_platform_on_leave() const;
CharacterBody3D();
- ~CharacterBody3D();
private:
real_t margin = 0.001;
diff --git a/scene/3d/physics/kinematic_collision_3d.cpp b/scene/3d/physics/kinematic_collision_3d.cpp
index 54371425bc..de13831ac3 100644
--- a/scene/3d/physics/kinematic_collision_3d.cpp
+++ b/scene/3d/physics/kinematic_collision_3d.cpp
@@ -67,6 +67,7 @@ real_t KinematicCollision3D::get_angle(int p_collision_index, const Vector3 &p_u
Object *KinematicCollision3D::get_local_shape(int p_collision_index) const {
ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, nullptr);
+ PhysicsBody3D *owner = Object::cast_to<PhysicsBody3D>(ObjectDB::get_instance(owner_id));
if (!owner) {
return nullptr;
}
diff --git a/scene/3d/physics/kinematic_collision_3d.h b/scene/3d/physics/kinematic_collision_3d.h
index 656531c82b..0573af0c21 100644
--- a/scene/3d/physics/kinematic_collision_3d.h
+++ b/scene/3d/physics/kinematic_collision_3d.h
@@ -40,7 +40,7 @@ class PhysicsBody3D;
class KinematicCollision3D : public RefCounted {
GDCLASS(KinematicCollision3D, RefCounted);
- PhysicsBody3D *owner = nullptr;
+ ObjectID owner_id;
friend class PhysicsBody3D;
friend class CharacterBody3D;
PhysicsServer3D::MotionResult result;
diff --git a/scene/3d/physics/physics_body_3d.cpp b/scene/3d/physics/physics_body_3d.cpp
index 711ca60a81..b723b452c1 100644
--- a/scene/3d/physics/physics_body_3d.cpp
+++ b/scene/3d/physics/physics_body_3d.cpp
@@ -30,8 +30,6 @@
#include "physics_body_3d.h"
-#include "scene/scene_string_names.h"
-
void PhysicsBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_collide", "motion", "test_only", "safe_margin", "recovery_as_collision", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(false), DEFVAL(1));
ClassDB::bind_method(D_METHOD("test_move", "from", "motion", "collision", "safe_margin", "recovery_as_collision", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(false), DEFVAL(1));
@@ -58,12 +56,6 @@ PhysicsBody3D::PhysicsBody3D(PhysicsServer3D::BodyMode p_mode) :
set_body_mode(p_mode);
}
-PhysicsBody3D::~PhysicsBody3D() {
- if (motion_cache.is_valid()) {
- motion_cache->owner = nullptr;
- }
-}
-
TypedArray<PhysicsBody3D> PhysicsBody3D::get_collision_exceptions() {
List<RID> exceptions;
PhysicsServer3D::get_singleton()->body_get_collision_exceptions(get_rid(), &exceptions);
@@ -102,7 +94,7 @@ Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_t
// Create a new instance when the cached reference is invalid or still in use in script.
if (motion_cache.is_null() || motion_cache->get_reference_count() > 1) {
motion_cache.instantiate();
- motion_cache->owner = this;
+ motion_cache->owner_id = get_instance_id();
}
motion_cache->result = result;
diff --git a/scene/3d/physics/physics_body_3d.h b/scene/3d/physics/physics_body_3d.h
index 92b3850085..71253be0b8 100644
--- a/scene/3d/physics/physics_body_3d.h
+++ b/scene/3d/physics/physics_body_3d.h
@@ -65,8 +65,6 @@ public:
TypedArray<PhysicsBody3D> get_collision_exceptions();
void add_collision_exception_with(Node *p_node); //must be physicsbody
void remove_collision_exception_with(Node *p_node);
-
- virtual ~PhysicsBody3D();
};
#endif // PHYSICS_BODY_3D_H
diff --git a/scene/3d/physics/ray_cast_3d.cpp b/scene/3d/physics/ray_cast_3d.cpp
index 0cb722a77e..a9272388c1 100644
--- a/scene/3d/physics/ray_cast_3d.cpp
+++ b/scene/3d/physics/ray_cast_3d.cpp
@@ -204,8 +204,10 @@ void RayCast3D::_notification(int p_what) {
bool prev_collision_state = collided;
_update_raycast_state();
- if (prev_collision_state != collided && get_tree()->is_debugging_collisions_hint()) {
- _update_debug_shape_material(true);
+ if (get_tree()->is_debugging_collisions_hint()) {
+ if (prev_collision_state != collided) {
+ _update_debug_shape_material(true);
+ }
if (is_inside_tree() && debug_instance.is_valid()) {
RenderingServer::get_singleton()->instance_set_transform(debug_instance, get_global_transform());
}
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index 95e94de0e3..db36a0a630 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -327,6 +327,8 @@ void Skeleton3D::_notification(int p_what) {
_process_modifiers();
}
+ emit_signal(SceneStringNames::get_singleton()->skeleton_updated);
+
// Update skins.
RenderingServer *rs = RenderingServer::get_singleton();
for (SkinReference *E : skin_bindings) {
@@ -921,8 +923,6 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
for (int i = 0; i < child_bone_size; i++) {
bones_to_process.push_back(b.child_bones[i]);
}
-
- emit_signal(SceneStringNames::get_singleton()->bone_pose_changed, current_bone_idx);
}
}
@@ -1059,7 +1059,7 @@ void Skeleton3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "modifier_callback_mode_process", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_modifier_callback_mode_process", "get_modifier_callback_mode_process");
ADD_SIGNAL(MethodInfo("pose_updated"));
- ADD_SIGNAL(MethodInfo("bone_pose_changed", PropertyInfo(Variant::INT, "bone_idx")));
+ ADD_SIGNAL(MethodInfo("skeleton_updated"));
ADD_SIGNAL(MethodInfo("bone_enabled_changed", PropertyInfo(Variant::INT, "bone_idx")));
ADD_SIGNAL(MethodInfo("bone_list_changed"));
ADD_SIGNAL(MethodInfo("show_rest_only_changed"));
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp
index 01e1de8f23..5a3a5f9bc0 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -698,9 +698,15 @@ bool AnimationMixer::_update_caches() {
if (has_reset_anim) {
int rt = reset_anim->find_track(path, track_src_type);
if (rt >= 0) {
- track_value->init_use_continuous = track_value->init_use_continuous || (reset_anim->value_track_get_update_mode(rt) != Animation::UPDATE_DISCRETE); // Take precedence Force Continuous.
- if (reset_anim->track_get_key_count(rt) > 0) {
- track_value->init_value = track_src_type == Animation::TYPE_VALUE ? reset_anim->track_get_key_value(rt, 0) : (reset_anim->track_get_key_value(rt, 0).operator Array())[0];
+ if (track_src_type == Animation::TYPE_VALUE) {
+ track_value->init_use_continuous = track_value->init_use_continuous || (reset_anim->value_track_get_update_mode(rt) != Animation::UPDATE_DISCRETE); // Take precedence Force Continuous.
+ if (reset_anim->track_get_key_count(rt) > 0) {
+ track_value->init_value = reset_anim->track_get_key_value(rt, 0);
+ }
+ } else {
+ if (reset_anim->track_get_key_count(rt) > 0) {
+ track_value->init_value = (reset_anim->track_get_key_value(rt, 0).operator Array())[0];
+ }
}
}
}
@@ -875,7 +881,7 @@ bool AnimationMixer::_update_caches() {
if (track_value->init_value.is_string() && anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE) {
WARN_PRINT_ONCE_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' blends String types. This is an experimental algorithm.");
}
- track_value->is_using_angle |= anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE;
+ track_value->is_using_angle = track_value->is_using_angle || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE;
}
if (check_angle_interpolation && (was_using_angle != track_value->is_using_angle)) {
WARN_PRINT_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' has different interpolation types for rotation between some animations which may be blended together. Blending prioritizes angle interpolation, so the blending result uses the shortest path referenced to the initial (RESET animation) value.");
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index f1a8c2f6ae..d430fe9bfc 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -3549,7 +3549,7 @@ void Control::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "localize_numeral_system"), "set_localize_numeral_system", "is_localizing_numeral_system");
#ifndef DISABLE_DEPRECATED
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_auto_translate", "is_auto_translating");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_auto_translate", "is_auto_translating");
#endif
ADD_GROUP("Tooltip", "tooltip_");
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index 72ec4e4eb7..87383283fd 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -194,8 +194,6 @@ Rect2i Popup::_popup_adjust_rect() const {
void Popup::_bind_methods() {
ADD_SIGNAL(MethodInfo("popup_hide"));
-
- BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, Popup, panel_style, "panel");
}
Popup::Popup() {
diff --git a/scene/gui/popup.h b/scene/gui/popup.h
index 25edca3657..48818686f7 100644
--- a/scene/gui/popup.h
+++ b/scene/gui/popup.h
@@ -43,10 +43,6 @@ class Popup : public Window {
LocalVector<Window *> visible_parents;
bool popped_up = false;
- struct ThemeCache {
- Ref<StyleBox> panel_style;
- } theme_cache;
-
void _initialize_visible_parents();
void _deinitialize_visible_parents();
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index e91a33f158..260956a775 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -642,11 +642,17 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
for (const Rect2 &E : autohide_areas) {
if (!Rect2(Point2(), get_size()).has_point(m->get_position()) && E.has_point(m->get_position())) {
+ // The mouse left the safe area, prepare to close.
_close_pressed();
return;
}
}
+ if (!minimum_lifetime_timer->is_stopped()) {
+ // The mouse left the safe area, but came back again, so cancel the auto-closing.
+ minimum_lifetime_timer->stop();
+ }
+
if (!item_clickable_area.has_point(m->get_position())) {
return;
}
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index f8bbedde09..4a838fc7f6 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -4410,6 +4410,10 @@ void RichTextLabel::append_text(const String &p_bbcode) {
push_strikethrough();
pos = brk_end + 1;
tag_stack.push_front(tag);
+ } else if (tag.begins_with("char=")) {
+ int32_t char_code = tag.substr(5, tag.length()).hex_to_int();
+ add_text(String::chr(char_code));
+ pos = brk_end + 1;
} else if (tag == "lb") {
add_text("[");
pos = brk_end + 1;
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 0c77a60af6..c465a3385f 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -120,7 +120,6 @@ void SceneTreeTimer::release_connections() {
SceneTreeTimer::SceneTreeTimer() {}
void SceneTree::tree_changed() {
- tree_version++;
emit_signal(tree_changed_name);
}
@@ -153,7 +152,6 @@ SceneTree::Group *SceneTree::add_to_group(const StringName &p_group, Node *p_nod
ERR_FAIL_COND_V_MSG(E->value.nodes.has(p_node), &E->value, "Already in group: " + p_group + ".");
E->value.nodes.push_back(p_node);
- //E->value.last_tree_version=0;
E->value.changed = true;
return &E->value;
}
@@ -476,8 +474,6 @@ void SceneTree::iteration_prepare() {
}
bool SceneTree::physics_process(double p_time) {
- root_lock++;
-
current_frame++;
flush_transform_notifications();
@@ -501,7 +497,6 @@ bool SceneTree::physics_process(double p_time) {
process_tweens(p_time, true);
flush_transform_notifications();
- root_lock--;
_flush_delete_queue();
_call_idle_callbacks();
@@ -510,8 +505,6 @@ bool SceneTree::physics_process(double p_time) {
}
bool SceneTree::process(double p_time) {
- root_lock++;
-
if (MainLoop::process(p_time)) {
_quit = true;
}
@@ -537,8 +530,6 @@ bool SceneTree::process(double p_time) {
MessageQueue::get_singleton()->flush(); //small little hack
flush_transform_notifications(); //transforms after world update, to avoid unnecessary enter/exit notifications
- root_lock--;
-
_flush_delete_queue();
if (unlikely(pending_new_scene)) {
@@ -1761,7 +1752,7 @@ SceneTree::SceneTree() {
root = memnew(Window);
root->set_min_size(Size2i(64, 64)); // Define a very small minimum window size to prevent bugs such as GH-37242.
root->set_process_mode(Node::PROCESS_MODE_PAUSABLE);
- root->set_auto_translate_mode(Node::AUTO_TRANSLATE_MODE_ALWAYS);
+ root->set_auto_translate_mode(GLOBAL_GET("internationalization/rendering/root_node_auto_translate") ? Node::AUTO_TRANSLATE_MODE_ALWAYS : Node::AUTO_TRANSLATE_MODE_DISABLED);
root->set_name("root");
root->set_title(GLOBAL_GET("application/config/name"));
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 603438cc5f..c9f3a4de1f 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -122,7 +122,6 @@ private:
Window *root = nullptr;
- uint64_t tree_version = 1;
double physics_process_time = 0.0;
double process_time = 0.0;
bool accept_quit = true;
@@ -134,7 +133,6 @@ private:
bool debug_navigation_hint = false;
#endif
bool paused = false;
- int root_lock = 0;
HashMap<StringName, Group> group_map;
bool _quit = false;
@@ -165,7 +163,6 @@ private:
// Safety for when a node is deleted while a group is being called.
- bool processing = false;
int nodes_removed_on_group_call_lock = 0;
HashSet<Node *> nodes_removed_on_group_call; // Skip erased nodes.
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 65f1365e67..0ccc056a8d 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -1215,10 +1215,10 @@ void Window::set_force_native(bool p_force_native) {
if (force_native == p_force_native) {
return;
}
- force_native = p_force_native;
if (is_visible() && !is_in_edited_scene_root()) {
- WARN_PRINT("Can't change \"force_native\" while a window is displayed. Consider hiding window before changing this value.");
+ ERR_FAIL_MSG("Can't change \"force_native\" while a window is displayed. Consider hiding window before changing this value.");
}
+ force_native = p_force_native;
}
bool Window::get_force_native() const {
@@ -1579,6 +1579,7 @@ bool Window::_can_consume_input_events() const {
}
void Window::_window_input(const Ref<InputEvent> &p_ev) {
+ ERR_MAIN_THREAD_GUARD;
if (EngineDebugger::is_active()) {
// Quit from game window using the stop shortcut (F8 by default).
// The custom shortcut is provided via environment variable when running from the editor.
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 752cfe2288..1c8833494d 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -33,53 +33,6 @@
#include "core/config/project_settings.h"
#include "core/object/class_db.h"
#include "core/os/os.h"
-#include "scene/2d/animated_sprite_2d.h"
-#include "scene/2d/audio_listener_2d.h"
-#include "scene/2d/audio_stream_player_2d.h"
-#include "scene/2d/back_buffer_copy.h"
-#include "scene/2d/camera_2d.h"
-#include "scene/2d/canvas_group.h"
-#include "scene/2d/canvas_modulate.h"
-#include "scene/2d/cpu_particles_2d.h"
-#include "scene/2d/gpu_particles_2d.h"
-#include "scene/2d/light_2d.h"
-#include "scene/2d/light_occluder_2d.h"
-#include "scene/2d/line_2d.h"
-#include "scene/2d/marker_2d.h"
-#include "scene/2d/mesh_instance_2d.h"
-#include "scene/2d/multimesh_instance_2d.h"
-#include "scene/2d/navigation_agent_2d.h"
-#include "scene/2d/navigation_link_2d.h"
-#include "scene/2d/navigation_obstacle_2d.h"
-#include "scene/2d/navigation_region_2d.h"
-#include "scene/2d/parallax_2d.h"
-#include "scene/2d/parallax_background.h"
-#include "scene/2d/parallax_layer.h"
-#include "scene/2d/path_2d.h"
-#include "scene/2d/physics/animatable_body_2d.h"
-#include "scene/2d/physics/area_2d.h"
-#include "scene/2d/physics/character_body_2d.h"
-#include "scene/2d/physics/collision_polygon_2d.h"
-#include "scene/2d/physics/collision_shape_2d.h"
-#include "scene/2d/physics/joints/damped_spring_joint_2d.h"
-#include "scene/2d/physics/joints/groove_joint_2d.h"
-#include "scene/2d/physics/joints/joint_2d.h"
-#include "scene/2d/physics/joints/pin_joint_2d.h"
-#include "scene/2d/physics/kinematic_collision_2d.h"
-#include "scene/2d/physics/physical_bone_2d.h"
-#include "scene/2d/physics/physics_body_2d.h"
-#include "scene/2d/physics/ray_cast_2d.h"
-#include "scene/2d/physics/rigid_body_2d.h"
-#include "scene/2d/physics/shape_cast_2d.h"
-#include "scene/2d/physics/static_body_2d.h"
-#include "scene/2d/polygon_2d.h"
-#include "scene/2d/remote_transform_2d.h"
-#include "scene/2d/skeleton_2d.h"
-#include "scene/2d/sprite_2d.h"
-#include "scene/2d/tile_map.h"
-#include "scene/2d/tile_map_layer.h"
-#include "scene/2d/touch_screen_button.h"
-#include "scene/2d/visible_on_screen_notifier_2d.h"
#include "scene/animation/animation_blend_space_1d.h"
#include "scene/animation/animation_blend_space_2d.h"
#include "scene/animation/animation_blend_tree.h"
@@ -146,29 +99,11 @@
#include "scene/main/multiplayer_api.h"
#include "scene/main/resource_preloader.h"
#include "scene/main/scene_tree.h"
+#include "scene/main/shader_globals_override.h"
#include "scene/main/status_indicator.h"
#include "scene/main/timer.h"
#include "scene/main/viewport.h"
#include "scene/main/window.h"
-#include "scene/resources/2d/capsule_shape_2d.h"
-#include "scene/resources/2d/circle_shape_2d.h"
-#include "scene/resources/2d/concave_polygon_shape_2d.h"
-#include "scene/resources/2d/convex_polygon_shape_2d.h"
-#include "scene/resources/2d/polygon_path_finder.h"
-#include "scene/resources/2d/rectangle_shape_2d.h"
-#include "scene/resources/2d/segment_shape_2d.h"
-#include "scene/resources/2d/separation_ray_shape_2d.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d_ccdik.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d_fabrik.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d_jiggle.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d_lookat.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d_physicalbones.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d_stackholder.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d_twoboneik.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_stack_2d.h"
-#include "scene/resources/2d/tile_set.h"
-#include "scene/resources/2d/world_boundary_shape_2d.h"
#include "scene/resources/animated_texture.h"
#include "scene/resources/animation_library.h"
#include "scene/resources/atlas_texture.h"
@@ -193,9 +128,6 @@
#include "scene/resources/mesh_texture.h"
#include "scene/resources/multimesh.h"
#include "scene/resources/navigation_mesh.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_2d.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
-#include "scene/resources/navigation_polygon.h"
#include "scene/resources/packed_scene.h"
#include "scene/resources/particle_process_material.h"
#include "scene/resources/physics_material.h"
@@ -226,7 +158,75 @@
#include "scene/scene_string_names.h"
#include "scene/theme/theme_db.h"
-#include "scene/main/shader_globals_override.h"
+// 2D
+#include "scene/2d/animated_sprite_2d.h"
+#include "scene/2d/audio_listener_2d.h"
+#include "scene/2d/audio_stream_player_2d.h"
+#include "scene/2d/back_buffer_copy.h"
+#include "scene/2d/camera_2d.h"
+#include "scene/2d/canvas_group.h"
+#include "scene/2d/canvas_modulate.h"
+#include "scene/2d/cpu_particles_2d.h"
+#include "scene/2d/gpu_particles_2d.h"
+#include "scene/2d/light_2d.h"
+#include "scene/2d/light_occluder_2d.h"
+#include "scene/2d/line_2d.h"
+#include "scene/2d/marker_2d.h"
+#include "scene/2d/mesh_instance_2d.h"
+#include "scene/2d/multimesh_instance_2d.h"
+#include "scene/2d/navigation_agent_2d.h"
+#include "scene/2d/navigation_link_2d.h"
+#include "scene/2d/navigation_obstacle_2d.h"
+#include "scene/2d/navigation_region_2d.h"
+#include "scene/2d/parallax_2d.h"
+#include "scene/2d/parallax_background.h"
+#include "scene/2d/parallax_layer.h"
+#include "scene/2d/path_2d.h"
+#include "scene/2d/physics/animatable_body_2d.h"
+#include "scene/2d/physics/area_2d.h"
+#include "scene/2d/physics/character_body_2d.h"
+#include "scene/2d/physics/collision_polygon_2d.h"
+#include "scene/2d/physics/collision_shape_2d.h"
+#include "scene/2d/physics/joints/damped_spring_joint_2d.h"
+#include "scene/2d/physics/joints/groove_joint_2d.h"
+#include "scene/2d/physics/joints/joint_2d.h"
+#include "scene/2d/physics/joints/pin_joint_2d.h"
+#include "scene/2d/physics/kinematic_collision_2d.h"
+#include "scene/2d/physics/physical_bone_2d.h"
+#include "scene/2d/physics/physics_body_2d.h"
+#include "scene/2d/physics/ray_cast_2d.h"
+#include "scene/2d/physics/rigid_body_2d.h"
+#include "scene/2d/physics/shape_cast_2d.h"
+#include "scene/2d/physics/static_body_2d.h"
+#include "scene/2d/polygon_2d.h"
+#include "scene/2d/remote_transform_2d.h"
+#include "scene/2d/skeleton_2d.h"
+#include "scene/2d/sprite_2d.h"
+#include "scene/2d/tile_map.h"
+#include "scene/2d/tile_map_layer.h"
+#include "scene/2d/touch_screen_button.h"
+#include "scene/2d/visible_on_screen_notifier_2d.h"
+#include "scene/resources/2d/capsule_shape_2d.h"
+#include "scene/resources/2d/circle_shape_2d.h"
+#include "scene/resources/2d/concave_polygon_shape_2d.h"
+#include "scene/resources/2d/convex_polygon_shape_2d.h"
+#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
+#include "scene/resources/2d/navigation_polygon.h"
+#include "scene/resources/2d/polygon_path_finder.h"
+#include "scene/resources/2d/rectangle_shape_2d.h"
+#include "scene/resources/2d/segment_shape_2d.h"
+#include "scene/resources/2d/separation_ray_shape_2d.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d_ccdik.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d_fabrik.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d_jiggle.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d_lookat.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d_physicalbones.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d_stackholder.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d_twoboneik.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_stack_2d.h"
+#include "scene/resources/2d/tile_set.h"
+#include "scene/resources/2d/world_boundary_shape_2d.h"
#ifndef _3D_DISABLED
#include "scene/3d/audio_listener_3d.h"
@@ -298,6 +298,7 @@
#include "scene/resources/3d/height_map_shape_3d.h"
#include "scene/resources/3d/importer_mesh.h"
#include "scene/resources/3d/mesh_library.h"
+#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "scene/resources/3d/primitive_meshes.h"
#include "scene/resources/3d/separation_ray_shape_3d.h"
#include "scene/resources/3d/sky_material.h"
@@ -858,6 +859,7 @@ void register_scene_types() {
GDREGISTER_CLASS(MeshDataTool);
#ifndef _3D_DISABLED
+ GDREGISTER_CLASS(AudioStreamPlayer3D);
GDREGISTER_VIRTUAL_CLASS(PrimitiveMesh);
GDREGISTER_CLASS(BoxMesh);
GDREGISTER_CLASS(CapsuleMesh);
@@ -881,6 +883,7 @@ void register_scene_types() {
BaseMaterial3D::init_shaders();
GDREGISTER_CLASS(MeshLibrary);
+ GDREGISTER_CLASS(NavigationMeshSourceGeometryData3D);
OS::get_singleton()->yield(); // may take time to init
@@ -969,7 +972,6 @@ void register_scene_types() {
GDREGISTER_CLASS(StyleBoxLine);
GDREGISTER_CLASS(Theme);
- GDREGISTER_CLASS(PolygonPathFinder);
GDREGISTER_CLASS(BitMap);
GDREGISTER_CLASS(Gradient);
@@ -980,16 +982,13 @@ void register_scene_types() {
OS::get_singleton()->yield(); // may take time to init
GDREGISTER_CLASS(AudioStreamPlayer);
- GDREGISTER_CLASS(AudioStreamPlayer2D);
-#ifndef _3D_DISABLED
- GDREGISTER_CLASS(AudioStreamPlayer3D);
-#endif
GDREGISTER_CLASS(AudioStreamWAV);
GDREGISTER_CLASS(AudioStreamPolyphonic);
GDREGISTER_ABSTRACT_CLASS(AudioStreamPlaybackPolyphonic);
OS::get_singleton()->yield(); // may take time to init
+ GDREGISTER_CLASS(AudioStreamPlayer2D);
GDREGISTER_ABSTRACT_CLASS(Shape2D);
GDREGISTER_CLASS(WorldBoundaryShape2D);
GDREGISTER_CLASS(SegmentShape2D);
@@ -1002,10 +1001,10 @@ void register_scene_types() {
GDREGISTER_CLASS(Curve2D);
GDREGISTER_CLASS(Path2D);
GDREGISTER_CLASS(PathFollow2D);
+ GDREGISTER_CLASS(PolygonPathFinder);
GDREGISTER_CLASS(NavigationMesh);
GDREGISTER_CLASS(NavigationMeshSourceGeometryData2D);
- GDREGISTER_CLASS(NavigationMeshSourceGeometryData3D);
GDREGISTER_CLASS(NavigationPolygon);
GDREGISTER_CLASS(NavigationRegion2D);
GDREGISTER_CLASS(NavigationAgent2D);
diff --git a/scene/resources/navigation_mesh_source_geometry_data_2d.cpp b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp
index d613b498a1..d613b498a1 100644
--- a/scene/resources/navigation_mesh_source_geometry_data_2d.cpp
+++ b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp
diff --git a/scene/resources/navigation_mesh_source_geometry_data_2d.h b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h
index 0e321fbeb9..11fc5d3850 100644
--- a/scene/resources/navigation_mesh_source_geometry_data_2d.h
+++ b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h
@@ -33,7 +33,7 @@
#include "core/os/rw_lock.h"
#include "scene/2d/node_2d.h"
-#include "scene/resources/navigation_polygon.h"
+#include "scene/resources/2d/navigation_polygon.h"
class NavigationMeshSourceGeometryData2D : public Resource {
GDCLASS(NavigationMeshSourceGeometryData2D, Resource);
diff --git a/scene/resources/navigation_polygon.cpp b/scene/resources/2d/navigation_polygon.cpp
index 274b13a487..274b13a487 100644
--- a/scene/resources/navigation_polygon.cpp
+++ b/scene/resources/2d/navigation_polygon.cpp
diff --git a/scene/resources/navigation_polygon.h b/scene/resources/2d/navigation_polygon.h
index b9816f900c..b9816f900c 100644
--- a/scene/resources/navigation_polygon.h
+++ b/scene/resources/2d/navigation_polygon.h
diff --git a/scene/resources/2d/tile_set.h b/scene/resources/2d/tile_set.h
index dceda1a791..e6d3f7e15d 100644
--- a/scene/resources/2d/tile_set.h
+++ b/scene/resources/2d/tile_set.h
@@ -38,8 +38,8 @@
#include "scene/2d/light_occluder_2d.h"
#include "scene/main/canvas_item.h"
#include "scene/resources/2d/convex_polygon_shape_2d.h"
+#include "scene/resources/2d/navigation_polygon.h"
#include "scene/resources/image_texture.h"
-#include "scene/resources/navigation_polygon.h"
#include "scene/resources/packed_scene.h"
#include "scene/resources/physics_material.h"
diff --git a/scene/resources/navigation_mesh_source_geometry_data_3d.cpp b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp
index 39a17946fa..39a17946fa 100644
--- a/scene/resources/navigation_mesh_source_geometry_data_3d.cpp
+++ b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp
diff --git a/scene/resources/navigation_mesh_source_geometry_data_3d.h b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.h
index 79e2f3740d..79e2f3740d 100644
--- a/scene/resources/navigation_mesh_source_geometry_data_3d.h
+++ b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.h
diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp
index 5acb08de14..30b90841e3 100644
--- a/scene/resources/particle_process_material.cpp
+++ b/scene/resources/particle_process_material.cpp
@@ -635,9 +635,9 @@ void ParticleProcessMaterial::_update_shader() {
code += " \n";
code += " float ring_spawn_angle = rand_from_seed(alt_seed) * 2.0 * pi;\n";
code += " float ring_random_radius = rand_from_seed(alt_seed) * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius;\n";
- code += " vec3 axis = normalize(emission_ring_axis);\n";
+ code += " vec3 axis = emission_ring_axis == vec3(0.0) ? vec3(0.0, 0.0, 1.0) : normalize(emission_ring_axis);\n";
code += " vec3 ortho_axis = vec3(0.0);\n";
- code += " if (axis == vec3(1.0, 0.0, 0.0)) {\n";
+ code += " if (abs(axis) == vec3(1.0, 0.0, 0.0)) {\n";
code += " ortho_axis = cross(axis, vec3(0.0, 1.0, 0.0));\n";
code += " } else {\n";
code += " ortho_axis = cross(axis, vec3(1.0, 0.0, 0.0));\n";
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 07868e7e49..98a12f1400 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -1067,7 +1067,7 @@ void ResourceLoaderText::open(Ref<FileAccess> p_f, bool p_skip_first_tag) {
if (fmt > FORMAT_VERSION) {
error_text = "Saved with newer format version";
_printerr();
- error = ERR_PARSE_ERROR;
+ error = ERR_FILE_UNRECOGNIZED;
return;
}
}
diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp
index e52e5d47bb..ec2c8ddb7b 100644
--- a/scene/resources/world_2d.cpp
+++ b/scene/resources/world_2d.cpp
@@ -66,6 +66,10 @@ RID World2D::get_navigation_map() const {
return navigation_map;
}
+PhysicsDirectSpaceState2D *World2D::get_direct_space_state() {
+ return PhysicsServer2D::get_singleton()->space_get_direct_state(get_space());
+}
+
void World2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_canvas"), &World2D::get_canvas);
ClassDB::bind_method(D_METHOD("get_space"), &World2D::get_space);
@@ -79,10 +83,6 @@ void World2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState2D", PROPERTY_USAGE_NONE), "", "get_direct_space_state");
}
-PhysicsDirectSpaceState2D *World2D::get_direct_space_state() {
- return PhysicsServer2D::get_singleton()->space_get_direct_state(get_space());
-}
-
void World2D::register_viewport(Viewport *p_viewport) {
viewports.insert(p_viewport);
}
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index cf8ece0d4c..5610b2f642 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -63,7 +63,7 @@ SceneStringNames::SceneStringNames() {
RESET = StaticCString::create("RESET");
pose_updated = StaticCString::create("pose_updated");
- bone_pose_changed = StaticCString::create("bone_pose_changed");
+ skeleton_updated = StaticCString::create("skeleton_updated");
bone_enabled_changed = StaticCString::create("bone_enabled_changed");
show_rest_only_changed = StaticCString::create("show_rest_only_changed");
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index 10b71e2a2a..60254e3006 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -99,7 +99,7 @@ public:
StringName RESET;
StringName pose_updated;
- StringName bone_pose_changed;
+ StringName skeleton_updated;
StringName bone_enabled_changed;
StringName show_rest_only_changed;
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 9ceb6909fe..9600caa214 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -212,7 +212,7 @@ String DisplayServer::global_menu_get_item_submenu(const String &p_menu_root, in
ERR_FAIL_NULL_V(nmenu, String());
RID rid = nmenu->get_item_submenu(_get_rid_from_name(nmenu, p_menu_root), p_idx);
if (!nmenu->is_system_menu(rid)) {
- for (HashMap<String, RID>::Iterator E = menu_names.begin(); E;) {
+ for (HashMap<String, RID>::Iterator E = menu_names.begin(); E; ++E) {
if (E->value == rid) {
return E->key;
}
@@ -697,10 +697,6 @@ void DisplayServer::release_rendering_thread() {
WARN_PRINT("Rendering thread not supported by this display server.");
}
-void DisplayServer::make_rendering_thread() {
- WARN_PRINT("Rendering thread not supported by this display server.");
-}
-
void DisplayServer::swap_buffers() {
WARN_PRINT("Swap buffers not supported by this display server.");
}
diff --git a/servers/display_server.h b/servers/display_server.h
index f1a98c2c17..aab51644c0 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -559,7 +559,6 @@ public:
virtual void force_process_and_drop_events();
virtual void release_rendering_thread();
- virtual void make_rendering_thread();
virtual void swap_buffers();
virtual void set_native_icon(const String &p_filename);
diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h
index 876b09d549..39d4c19064 100644
--- a/servers/navigation_server_2d.h
+++ b/servers/navigation_server_2d.h
@@ -34,8 +34,8 @@
#include "core/object/class_db.h"
#include "core/templates/rid.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_2d.h"
-#include "scene/resources/navigation_polygon.h"
+#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
+#include "scene/resources/2d/navigation_polygon.h"
#include "servers/navigation/navigation_path_query_parameters_2d.h"
#include "servers/navigation/navigation_path_query_result_2d.h"
diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp
index 7b54a24b59..fda26aacc1 100644
--- a/servers/navigation_server_3d.cpp
+++ b/servers/navigation_server_3d.cpp
@@ -181,10 +181,12 @@ void NavigationServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_layers", "obstacle", "layers"), &NavigationServer3D::obstacle_set_avoidance_layers);
ClassDB::bind_method(D_METHOD("obstacle_get_avoidance_layers", "obstacle"), &NavigationServer3D::obstacle_get_avoidance_layers);
+#ifndef _3D_DISABLED
ClassDB::bind_method(D_METHOD("parse_source_geometry_data", "navigation_mesh", "source_geometry_data", "root_node", "callback"), &NavigationServer3D::parse_source_geometry_data, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data_async", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data_async, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("is_baking_navigation_mesh", "navigation_mesh"), &NavigationServer3D::is_baking_navigation_mesh);
+#endif // _3D_DISABLED
ClassDB::bind_method(D_METHOD("simplify_path", "path", "epsilon"), &NavigationServer3D::simplify_path);
diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h
index c23da78299..5a93c662b2 100644
--- a/servers/navigation_server_3d.h
+++ b/servers/navigation_server_3d.h
@@ -34,8 +34,8 @@
#include "core/object/class_db.h"
#include "core/templates/rid.h"
+#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "scene/resources/navigation_mesh.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
#include "servers/navigation/navigation_path_query_parameters_3d.h"
#include "servers/navigation/navigation_path_query_result_3d.h"
@@ -344,10 +344,12 @@ public:
virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const = 0;
+#ifndef _3D_DISABLED
virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0;
virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
virtual bool is_baking_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) const = 0;
+#endif // _3D_DISABLED
virtual Vector<Vector3> simplify_path(const Vector<Vector3> &p_path, real_t p_epsilon) = 0;
diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h
index d98a0edb01..7079aa66be 100644
--- a/servers/navigation_server_3d_dummy.h
+++ b/servers/navigation_server_3d_dummy.h
@@ -175,10 +175,12 @@ public:
void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {}
uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const override { return 0; }
+#ifndef _3D_DISABLED
void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {}
void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
bool is_baking_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) const override { return false; }
+#endif // _3D_DISABLED
Vector<Vector3> simplify_path(const Vector<Vector3> &p_path, real_t p_epsilon) override { return Vector<Vector3>(); }
diff --git a/servers/physics_2d/godot_shape_2d.cpp b/servers/physics_2d/godot_shape_2d.cpp
index db5e6b2353..d77b1a77e3 100644
--- a/servers/physics_2d/godot_shape_2d.cpp
+++ b/servers/physics_2d/godot_shape_2d.cpp
@@ -123,7 +123,7 @@ void GodotWorldBoundaryShape2D::set_data(const Variant &p_data) {
ERR_FAIL_COND(arr.size() != 2);
normal = arr[0];
d = arr[1];
- configure(Rect2(Vector2(-1e4, -1e4), Vector2(1e4 * 2, 1e4 * 2)));
+ configure(Rect2(Vector2(-1e15, -1e15), Vector2(1e15 * 2, 1e15 * 2)));
}
Variant GodotWorldBoundaryShape2D::get_data() const {
diff --git a/servers/physics_3d/godot_shape_3d.cpp b/servers/physics_3d/godot_shape_3d.cpp
index ea389ff59c..6eb983d5e0 100644
--- a/servers/physics_3d/godot_shape_3d.cpp
+++ b/servers/physics_3d/godot_shape_3d.cpp
@@ -152,7 +152,7 @@ Vector3 GodotWorldBoundaryShape3D::get_moment_of_inertia(real_t p_mass) const {
void GodotWorldBoundaryShape3D::_setup(const Plane &p_plane) {
plane = p_plane;
- configure(AABB(Vector3(-1e4, -1e4, -1e4), Vector3(1e4 * 2, 1e4 * 2, 1e4 * 2)));
+ configure(AABB(Vector3(-1e15, -1e15, -1e15), Vector3(1e15 * 2, 1e15 * 2, 1e15 * 2)));
}
void GodotWorldBoundaryShape3D::set_data(const Variant &p_data) {
diff --git a/servers/physics_server_2d_wrap_mt.cpp b/servers/physics_server_2d_wrap_mt.cpp
index a23bb5e701..4548bb91cb 100644
--- a/servers/physics_server_2d_wrap_mt.cpp
+++ b/servers/physics_server_2d_wrap_mt.cpp
@@ -33,7 +33,7 @@
#include "core/os/os.h"
void PhysicsServer2DWrapMT::thread_exit() {
- exit.set();
+ exit = true;
}
void PhysicsServer2DWrapMT::thread_step(real_t p_delta) {
@@ -41,25 +41,18 @@ void PhysicsServer2DWrapMT::thread_step(real_t p_delta) {
step_sem.post();
}
-void PhysicsServer2DWrapMT::_thread_callback(void *_instance) {
- PhysicsServer2DWrapMT *vsmt = reinterpret_cast<PhysicsServer2DWrapMT *>(_instance);
-
- vsmt->thread_loop();
-}
-
void PhysicsServer2DWrapMT::thread_loop() {
server_thread = Thread::get_caller_id();
physics_server_2d->init();
- exit.clear();
- step_thread_up.set();
- while (!exit.is_set()) {
- // flush commands one by one, until exit is requested
- command_queue.wait_and_flush();
+ command_queue.set_pump_task_id(server_task_id);
+ while (!exit) {
+ WorkerThreadPool::get_singleton()->yield();
+ command_queue.flush_all();
}
- command_queue.flush_all(); // flush all
+ command_queue.flush_all();
physics_server_2d->finish();
}
@@ -70,18 +63,14 @@ void PhysicsServer2DWrapMT::step(real_t p_step) {
if (create_thread) {
command_queue.push(this, &PhysicsServer2DWrapMT::thread_step, p_step);
} else {
- command_queue.flush_all(); //flush all pending from other threads
+ command_queue.flush_all(); // Flush all pending from other threads.
physics_server_2d->step(p_step);
}
}
void PhysicsServer2DWrapMT::sync() {
if (create_thread) {
- if (first_frame) {
- first_frame = false;
- } else {
- step_sem.wait(); //must not wait if a step was not issued
- }
+ step_sem.wait();
}
physics_server_2d->sync();
}
@@ -96,40 +85,34 @@ void PhysicsServer2DWrapMT::end_sync() {
void PhysicsServer2DWrapMT::init() {
if (create_thread) {
- //OS::get_singleton()->release_rendering_thread();
- thread.start(_thread_callback, this);
- while (!step_thread_up.is_set()) {
- OS::get_singleton()->delay_usec(1000);
- }
+ exit = false;
+ server_task_id = WorkerThreadPool::get_singleton()->add_task(callable_mp(this, &PhysicsServer2DWrapMT::thread_loop), true);
+ step_sem.post();
} else {
physics_server_2d->init();
}
}
void PhysicsServer2DWrapMT::finish() {
- if (thread.is_started()) {
+ if (create_thread) {
command_queue.push(this, &PhysicsServer2DWrapMT::thread_exit);
- thread.wait_to_finish();
+ if (server_task_id != WorkerThreadPool::INVALID_TASK_ID) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(server_task_id);
+ server_task_id = WorkerThreadPool::INVALID_TASK_ID;
+ }
} else {
physics_server_2d->finish();
}
}
-PhysicsServer2DWrapMT::PhysicsServer2DWrapMT(PhysicsServer2D *p_contained, bool p_create_thread) :
- command_queue(p_create_thread) {
+PhysicsServer2DWrapMT::PhysicsServer2DWrapMT(PhysicsServer2D *p_contained, bool p_create_thread) {
physics_server_2d = p_contained;
create_thread = p_create_thread;
-
- if (!p_create_thread) {
- server_thread = Thread::get_caller_id();
- } else {
- server_thread = 0;
+ if (!create_thread) {
+ server_thread = Thread::MAIN_ID;
}
-
- main_thread = Thread::get_caller_id();
}
PhysicsServer2DWrapMT::~PhysicsServer2DWrapMT() {
memdelete(physics_server_2d);
- //finish();
}
diff --git a/servers/physics_server_2d_wrap_mt.h b/servers/physics_server_2d_wrap_mt.h
index 3bebe5df85..5e2b3b4086 100644
--- a/servers/physics_server_2d_wrap_mt.h
+++ b/servers/physics_server_2d_wrap_mt.h
@@ -32,6 +32,7 @@
#define PHYSICS_SERVER_2D_WRAP_MT_H
#include "core/config/project_settings.h"
+#include "core/object/worker_thread_pool.h"
#include "core/os/thread.h"
#include "core/templates/command_queue_mt.h"
#include "core/templates/safe_refcount.h"
@@ -43,30 +44,27 @@
#define SYNC_DEBUG
#endif
+#ifdef DEBUG_ENABLED
+#define MAIN_THREAD_SYNC_WARN WARN_PRINT("Call to " + String(__FUNCTION__) + " causing PhysicsServer2D synchronizations on every frame. This significantly affects performance.");
+#endif
+
class PhysicsServer2DWrapMT : public PhysicsServer2D {
- mutable PhysicsServer2D *physics_server_2d;
+ mutable PhysicsServer2D *physics_server_2d = nullptr;
mutable CommandQueueMT command_queue;
- static void _thread_callback(void *_instance);
void thread_loop();
- Thread::ID server_thread;
- Thread::ID main_thread;
- SafeFlag exit;
- Thread thread;
- SafeFlag step_thread_up;
+ Thread::ID server_thread = Thread::UNASSIGNED_ID;
+ WorkerThreadPool::TaskID server_task_id = WorkerThreadPool::INVALID_TASK_ID;
+ bool exit = false;
+ Semaphore step_sem;
bool create_thread = false;
- Semaphore step_sem;
void thread_step(real_t p_delta);
void thread_exit();
- bool first_frame = true;
-
- Mutex alloc_mutex;
-
public:
#define ServerName PhysicsServer2D
#define ServerNameWrapMT PhysicsServer2DWrapMT
@@ -94,7 +92,7 @@ public:
//these work well, but should be used from the main thread only
bool shape_collide(RID p_shape_A, const Transform2D &p_xform_A, const Vector2 &p_motion_A, RID p_shape_B, const Transform2D &p_xform_B, const Vector2 &p_motion_B, Vector2 *r_results, int p_result_max, int &r_result_count) override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), false);
return physics_server_2d->shape_collide(p_shape_A, p_xform_A, p_motion_A, p_shape_B, p_xform_B, p_motion_B, r_results, p_result_max, r_result_count);
}
@@ -109,18 +107,18 @@ public:
// this function only works on physics process, errors and returns null otherwise
PhysicsDirectSpaceState2D *space_get_direct_state(RID p_space) override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), nullptr);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), nullptr);
return physics_server_2d->space_get_direct_state(p_space);
}
FUNC2(space_set_debug_contacts, RID, int);
virtual Vector<Vector2> space_get_contacts(RID p_space) const override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), Vector<Vector2>());
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), Vector<Vector2>());
return physics_server_2d->space_get_contacts(p_space);
}
virtual int space_get_contact_count(RID p_space) const override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), 0);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), 0);
return physics_server_2d->space_get_contact_count(p_space);
}
@@ -261,13 +259,13 @@ public:
FUNC2(body_set_pickable, RID, bool);
bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), false);
return physics_server_2d->body_test_motion(p_body, p_parameters, r_result);
}
// this function only works on physics process, errors and returns null otherwise
PhysicsDirectBodyState2D *body_get_direct_state(RID p_body) override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), nullptr);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), nullptr);
return physics_server_2d->body_get_direct_state(p_body);
}
@@ -338,4 +336,8 @@ public:
#endif
#undef SYNC_DEBUG
+#ifdef DEBUG_ENABLED
+#undef MAIN_THREAD_SYNC_WARN
+#endif
+
#endif // PHYSICS_SERVER_2D_WRAP_MT_H
diff --git a/servers/physics_server_3d_wrap_mt.cpp b/servers/physics_server_3d_wrap_mt.cpp
index feb17cad84..f8f60281a7 100644
--- a/servers/physics_server_3d_wrap_mt.cpp
+++ b/servers/physics_server_3d_wrap_mt.cpp
@@ -41,22 +41,15 @@ void PhysicsServer3DWrapMT::thread_step(real_t p_delta) {
step_sem.post();
}
-void PhysicsServer3DWrapMT::_thread_callback(void *_instance) {
- PhysicsServer3DWrapMT *vsmt = reinterpret_cast<PhysicsServer3DWrapMT *>(_instance);
-
- vsmt->thread_loop();
-}
-
void PhysicsServer3DWrapMT::thread_loop() {
server_thread = Thread::get_caller_id();
physics_server_3d->init();
- exit = false;
- step_thread_up = true;
+ command_queue.set_pump_task_id(server_task_id);
while (!exit) {
- // flush commands one by one, until exit is requested
- command_queue.wait_and_flush();
+ WorkerThreadPool::get_singleton()->yield();
+ command_queue.flush_all();
}
command_queue.flush_all(); // flush all
@@ -70,18 +63,14 @@ void PhysicsServer3DWrapMT::step(real_t p_step) {
if (create_thread) {
command_queue.push(this, &PhysicsServer3DWrapMT::thread_step, p_step);
} else {
- command_queue.flush_all(); //flush all pending from other threads
+ command_queue.flush_all(); // Flush all pending from other threads.
physics_server_3d->step(p_step);
}
}
void PhysicsServer3DWrapMT::sync() {
if (create_thread) {
- if (first_frame) {
- first_frame = false;
- } else {
- step_sem.wait(); //must not wait if a step was not issued
- }
+ step_sem.wait();
}
physics_server_3d->sync();
}
@@ -96,40 +85,34 @@ void PhysicsServer3DWrapMT::end_sync() {
void PhysicsServer3DWrapMT::init() {
if (create_thread) {
- //OS::get_singleton()->release_rendering_thread();
- thread.start(_thread_callback, this);
- while (!step_thread_up) {
- OS::get_singleton()->delay_usec(1000);
- }
+ exit = false;
+ server_task_id = WorkerThreadPool::get_singleton()->add_task(callable_mp(this, &PhysicsServer3DWrapMT::thread_loop), true);
+ step_sem.post();
} else {
physics_server_3d->init();
}
}
void PhysicsServer3DWrapMT::finish() {
- if (thread.is_started()) {
+ if (create_thread) {
command_queue.push(this, &PhysicsServer3DWrapMT::thread_exit);
- thread.wait_to_finish();
+ if (server_task_id != WorkerThreadPool::INVALID_TASK_ID) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(server_task_id);
+ server_task_id = WorkerThreadPool::INVALID_TASK_ID;
+ }
} else {
physics_server_3d->finish();
}
}
-PhysicsServer3DWrapMT::PhysicsServer3DWrapMT(PhysicsServer3D *p_contained, bool p_create_thread) :
- command_queue(p_create_thread) {
+PhysicsServer3DWrapMT::PhysicsServer3DWrapMT(PhysicsServer3D *p_contained, bool p_create_thread) {
physics_server_3d = p_contained;
create_thread = p_create_thread;
-
- if (!p_create_thread) {
- server_thread = Thread::get_caller_id();
- } else {
- server_thread = 0;
+ if (!create_thread) {
+ server_thread = Thread::MAIN_ID;
}
-
- main_thread = Thread::get_caller_id();
}
PhysicsServer3DWrapMT::~PhysicsServer3DWrapMT() {
memdelete(physics_server_3d);
- //finish();
}
diff --git a/servers/physics_server_3d_wrap_mt.h b/servers/physics_server_3d_wrap_mt.h
index fc8930977d..22f3ee0e45 100644
--- a/servers/physics_server_3d_wrap_mt.h
+++ b/servers/physics_server_3d_wrap_mt.h
@@ -32,6 +32,7 @@
#define PHYSICS_SERVER_3D_WRAP_MT_H
#include "core/config/project_settings.h"
+#include "core/object/worker_thread_pool.h"
#include "core/os/thread.h"
#include "core/templates/command_queue_mt.h"
#include "servers/physics_server_3d.h"
@@ -42,30 +43,27 @@
#define SYNC_DEBUG
#endif
+#ifdef DEBUG_ENABLED
+#define MAIN_THREAD_SYNC_WARN WARN_PRINT("Call to " + String(__FUNCTION__) + " causing PhysicsServer3D synchronizations on every frame. This significantly affects performance.");
+#endif
+
class PhysicsServer3DWrapMT : public PhysicsServer3D {
- mutable PhysicsServer3D *physics_server_3d;
+ mutable PhysicsServer3D *physics_server_3d = nullptr;
mutable CommandQueueMT command_queue;
- static void _thread_callback(void *_instance);
void thread_loop();
- Thread::ID server_thread;
- Thread::ID main_thread;
- volatile bool exit = false;
- Thread thread;
- volatile bool step_thread_up = false;
+ Thread::ID server_thread = Thread::UNASSIGNED_ID;
+ WorkerThreadPool::TaskID server_task_id = WorkerThreadPool::INVALID_TASK_ID;
+ bool exit = false;
+ Semaphore step_sem;
bool create_thread = false;
- Semaphore step_sem;
void thread_step(real_t p_delta);
void thread_exit();
- bool first_frame = true;
-
- Mutex alloc_mutex;
-
public:
#define ServerName PhysicsServer3D
#define ServerNameWrapMT PhysicsServer3DWrapMT
@@ -98,7 +96,7 @@ public:
#if 0
//these work well, but should be used from the main thread only
bool shape_collide(RID p_shape_A, const Transform &p_xform_A, const Vector3 &p_motion_A, RID p_shape_B, const Transform &p_xform_B, const Vector3 &p_motion_B, Vector3 *r_results, int p_result_max, int &r_result_count) {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), false);
return physics_server_3d->shape_collide(p_shape_A, p_xform_A, p_motion_A, p_shape_B, p_xform_B, p_motion_B, r_results, p_result_max, r_result_count);
}
#endif
@@ -113,18 +111,18 @@ public:
// this function only works on physics process, errors and returns null otherwise
PhysicsDirectSpaceState3D *space_get_direct_state(RID p_space) override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), nullptr);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), nullptr);
return physics_server_3d->space_get_direct_state(p_space);
}
FUNC2(space_set_debug_contacts, RID, int);
virtual Vector<Vector3> space_get_contacts(RID p_space) const override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), Vector<Vector3>());
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), Vector<Vector3>());
return physics_server_3d->space_get_contacts(p_space);
}
virtual int space_get_contact_count(RID p_space) const override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), 0);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), 0);
return physics_server_3d->space_get_contact_count(p_space);
}
@@ -260,13 +258,13 @@ public:
FUNC2(body_set_ray_pickable, RID, bool);
bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), false);
return physics_server_3d->body_test_motion(p_body, p_parameters, r_result);
}
// this function only works on physics process, errors and returns null otherwise
PhysicsDirectBodyState3D *body_get_direct_state(RID p_body) override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), nullptr);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), nullptr);
return physics_server_3d->body_get_direct_state(p_body);
}
@@ -411,4 +409,8 @@ public:
#endif
#undef SYNC_DEBUG
+#ifdef DEBUG_ENABLED
+#undef MAIN_THREAD_SYNC_WARN
+#endif
+
#endif // PHYSICS_SERVER_3D_WRAP_MT_H
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index e32164ea98..46c84fd230 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -332,7 +332,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
child_item_count = ci->ysort_children_count + 1;
child_items = (Item **)alloca(child_item_count * sizeof(Item *));
- ci->ysort_xform = final_xform.affine_inverse();
+ ci->ysort_xform = ci->xform_curr.affine_inverse();
ci->ysort_pos = Vector2();
ci->ysort_modulate = Color(1, 1, 1, 1);
ci->ysort_index = 0;
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index e61bb9eae8..6cb03871c9 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -1157,6 +1157,7 @@ void RendererSceneRenderRD::render_scene(const Ref<RenderSceneBuffers> &p_render
render_data.lights = &empty;
render_data.reflection_probes = &empty;
render_data.voxel_gi_instances = &empty;
+ render_data.lightmaps = &empty;
}
if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_UNSHADED ||
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
index c9c7c53d04..f7b28e7a1e 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
@@ -257,7 +257,6 @@ void ParticlesStorage::particles_set_emitting(RID p_particles, bool p_emitting)
}
bool ParticlesStorage::particles_get_emitting(RID p_particles) {
- ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer.");
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, false);
@@ -608,10 +607,6 @@ void ParticlesStorage::particles_request_process(RID p_particles) {
}
AABB ParticlesStorage::particles_get_current_aabb(RID p_particles) {
- if (RSG::threaded) {
- WARN_PRINT_ONCE("Calling this function with threaded rendering enabled stalls the renderer, use with care.");
- }
-
const Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, AABB());
@@ -1642,7 +1637,6 @@ Dependency *ParticlesStorage::particles_get_dependency(RID p_particles) const {
}
bool ParticlesStorage::particles_is_inactive(RID p_particles) const {
- ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer.");
const Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, false);
return !particles->emitting && particles->inactive;
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index 5bf0ab0ba6..7e5ccee0e3 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -69,9 +69,6 @@ void RenderingServerDefault::request_frame_drawn_callback(const Callable &p_call
}
void RenderingServerDefault::_draw(bool p_swap_buffers, double frame_step) {
- //needs to be done before changes is reset to 0, to not force the editor to redraw
- RS::get_singleton()->emit_signal(SNAME("frame_pre_draw"));
-
changes = 0;
RSG::rasterizer->begin_frame(frame_step);
@@ -220,16 +217,9 @@ void RenderingServerDefault::_finish() {
void RenderingServerDefault::init() {
if (create_thread) {
- print_verbose("RenderingServerWrapMT: Creating render thread");
+ print_verbose("RenderingServerWrapMT: Starting render thread");
DisplayServer::get_singleton()->release_rendering_thread();
- if (create_thread) {
- thread.start(_thread_callback, this);
- print_verbose("RenderingServerWrapMT: Starting render thread");
- }
- while (!draw_thread_up.is_set()) {
- OS::get_singleton()->delay_usec(1000);
- }
- print_verbose("RenderingServerWrapMT: Finished render thread");
+ server_task_id = WorkerThreadPool::get_singleton()->add_task(callable_mp(this, &RenderingServerDefault::_thread_loop), true);
} else {
_init();
}
@@ -238,8 +228,9 @@ void RenderingServerDefault::init() {
void RenderingServerDefault::finish() {
if (create_thread) {
command_queue.push(this, &RenderingServerDefault::_thread_exit);
- if (thread.is_started()) {
- thread.wait_to_finish();
+ if (server_task_id != WorkerThreadPool::INVALID_TASK_ID) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(server_task_id);
+ server_task_id = WorkerThreadPool::INVALID_TASK_ID;
}
} else {
_finish();
@@ -337,38 +328,29 @@ Size2i RenderingServerDefault::get_maximum_viewport_size() const {
}
void RenderingServerDefault::_thread_exit() {
- exit.set();
+ exit = true;
}
void RenderingServerDefault::_thread_draw(bool p_swap_buffers, double frame_step) {
_draw(p_swap_buffers, frame_step);
}
-void RenderingServerDefault::_thread_flush() {
-}
-
-void RenderingServerDefault::_thread_callback(void *_instance) {
- RenderingServerDefault *vsmt = reinterpret_cast<RenderingServerDefault *>(_instance);
-
- vsmt->_thread_loop();
-}
-
void RenderingServerDefault::_thread_loop() {
server_thread = Thread::get_caller_id();
- DisplayServer::get_singleton()->make_rendering_thread();
-
+ DisplayServer::get_singleton()->gl_window_make_current(DisplayServer::MAIN_WINDOW_ID); // Move GL to this thread.
_init();
- draw_thread_up.set();
- while (!exit.is_set()) {
- // flush commands one by one, until exit is requested
- command_queue.wait_and_flush();
+ command_queue.set_pump_task_id(server_task_id);
+ while (!exit) {
+ WorkerThreadPool::get_singleton()->yield();
+ command_queue.flush_all();
}
- command_queue.flush_all(); // flush all
+ command_queue.flush_all();
_finish();
+ DisplayServer::get_singleton()->release_rendering_thread();
}
/* INTERPOLATION */
@@ -384,15 +366,15 @@ void RenderingServerDefault::set_physics_interpolation_enabled(bool p_enabled) {
/* EVENT QUEUING */
void RenderingServerDefault::sync() {
- if (create_thread) {
- command_queue.push_and_sync(this, &RenderingServerDefault::_thread_flush);
- } else {
- command_queue.flush_all(); //flush all pending from other threads
+ if (!create_thread) {
+ command_queue.flush_all(); // Flush all pending from other threads.
}
}
void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Manually triggering the draw function from the RenderingServer can only be done on the main thread. Call this function from the main thread or use call_deferred().");
+ // Needs to be done before changes is reset to 0, to not force the editor to redraw.
+ RS::get_singleton()->emit_signal(SNAME("frame_pre_draw"));
if (create_thread) {
command_queue.push(this, &RenderingServerDefault::_thread_draw, p_swap_buffers, frame_step);
} else {
@@ -404,21 +386,14 @@ void RenderingServerDefault::_call_on_render_thread(const Callable &p_callable)
p_callable.call();
}
-RenderingServerDefault::RenderingServerDefault(bool p_create_thread) :
- command_queue(p_create_thread) {
+RenderingServerDefault::RenderingServerDefault(bool p_create_thread) {
RenderingServer::init();
-#ifdef THREADS_ENABLED
create_thread = p_create_thread;
if (!create_thread) {
- server_thread = Thread::get_caller_id();
- } else {
- server_thread = 0;
+ server_thread = Thread::MAIN_ID;
}
-#else
- create_thread = false;
- server_thread = Thread::get_main_id();
-#endif
+
RSG::threaded = create_thread;
RSG::canvas = memnew(RendererCanvasCull);
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index c50472c0cd..f94323f198 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -31,6 +31,7 @@
#ifndef RENDERING_SERVER_DEFAULT_H
#define RENDERING_SERVER_DEFAULT_H
+#include "core/object/worker_thread_pool.h"
#include "core/os/thread.h"
#include "core/templates/command_queue_mt.h"
#include "core/templates/hash_map.h"
@@ -75,22 +76,17 @@ class RenderingServerDefault : public RenderingServer {
mutable CommandQueueMT command_queue;
- static void _thread_callback(void *_instance);
void _thread_loop();
- Thread::ID server_thread = 0;
- SafeFlag exit;
- Thread thread;
- SafeFlag draw_thread_up;
- bool create_thread;
+ Thread::ID server_thread = Thread::UNASSIGNED_ID;
+ WorkerThreadPool::TaskID server_task_id = WorkerThreadPool::INVALID_TASK_ID;
+ bool exit = false;
+ bool create_thread = false;
void _thread_draw(bool p_swap_buffers, double frame_step);
- void _thread_flush();
void _thread_exit();
- Mutex alloc_mutex;
-
void _draw(bool p_swap_buffers, double frame_step);
void _init();
void _finish();
@@ -127,6 +123,10 @@ public:
#define SYNC_DEBUG
#endif
+#ifdef DEBUG_ENABLED
+#define MAIN_THREAD_SYNC_WARN WARN_PRINT("Call to " + String(__FUNCTION__) + " causing RenderingServer synchronizations on every frame. This significantly affects performance.");
+#endif
+
#include "servers/server_wrap_mt_common.h"
/* TEXTURE API */
@@ -1013,6 +1013,9 @@ public:
#undef ServerName
#undef WRITE_ACTION
#undef SYNC_DEBUG
+#ifdef DEBUG_ENABLED
+#undef MAIN_THREAD_SYNC_WARN
+#endif
virtual uint64_t get_rendering_info(RenderingInfo p_info) override;
virtual RenderingDevice::DeviceType get_video_adapter_type() const override;
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 8fbad346a4..99b3f54379 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -5029,6 +5029,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
Vector<Expression> expression;
//Vector<TokenType> operators;
+#ifdef DEBUG_ENABLED
+ bool check_position_write = check_warnings && HAS_WARNING(ShaderWarning::MAGIC_POSITION_WRITE_FLAG);
+ check_position_write = check_position_write && String(shader_type_identifier) == "spatial" && current_function == "vertex";
+#endif
while (true) {
Node *expr = nullptr;
@@ -5589,6 +5593,24 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
_set_error(vformat(RTR("Can't use function as identifier: '%s'."), String(identifier)));
return nullptr;
}
+#ifdef DEBUG_ENABLED
+ if (check_position_write && ident_type == IDENTIFIER_BUILTIN_VAR) {
+ if (String(identifier) == "POSITION") {
+ // Check if the user wrote "POSITION = vec4(VERTEX," and warn if they did.
+ TkPos prev_pos = _get_tkpos();
+ if (_get_token().type == TK_OP_ASSIGN &&
+ _get_token().type == TK_TYPE_VEC4 &&
+ _get_token().type == TK_PARENTHESIS_OPEN &&
+ _get_token().text == "VERTEX" &&
+ _get_token().type == TK_COMMA) {
+ _add_line_warning(ShaderWarning::MAGIC_POSITION_WRITE);
+ }
+
+ // Reset the position so compiling can continue as normal.
+ _set_tkpos(prev_pos);
+ }
+ }
+#endif
if (is_const) {
last_type = IDENTIFIER_CONSTANT;
} else {
diff --git a/servers/rendering/shader_warnings.cpp b/servers/rendering/shader_warnings.cpp
index dce8f6cff1..3b99f6c2bf 100644
--- a/servers/rendering/shader_warnings.cpp
+++ b/servers/rendering/shader_warnings.cpp
@@ -65,6 +65,8 @@ String ShaderWarning::get_message() const {
return subject;
case DEVICE_LIMIT_EXCEEDED:
return vformat(RTR("The total size of the %s for this shader on this device has been exceeded (%d/%d). The shader may not work correctly."), subject, (int)extra_args[0], (int)extra_args[1]);
+ case MAGIC_POSITION_WRITE:
+ return vformat(RTR("You are attempting to assign the VERTEX position in model space to the vertex POSITION in clip space. The definition of clip space changed in version 4.3, so if this code was written prior to 4.3, it will not continue to work. Consider specifying the clip space z-component directly i.e. use `vec4(VERTEX.xy, 1.0, 1.0)`."));
default:
break;
}
@@ -92,6 +94,7 @@ String ShaderWarning::get_name_from_code(Code p_code) {
"UNUSED_LOCAL_VARIABLE",
"FORMATTING_ERROR",
"DEVICE_LIMIT_EXCEEDED",
+ "MAGIC_POSITION_WRITE",
};
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
@@ -122,6 +125,7 @@ static void init_code_to_flags_map() {
code_to_flags_map->insert(ShaderWarning::UNUSED_LOCAL_VARIABLE, ShaderWarning::UNUSED_LOCAL_VARIABLE_FLAG);
code_to_flags_map->insert(ShaderWarning::FORMATTING_ERROR, ShaderWarning::FORMATTING_ERROR_FLAG);
code_to_flags_map->insert(ShaderWarning::DEVICE_LIMIT_EXCEEDED, ShaderWarning::DEVICE_LIMIT_EXCEEDED_FLAG);
+ code_to_flags_map->insert(ShaderWarning::MAGIC_POSITION_WRITE, ShaderWarning::MAGIC_POSITION_WRITE_FLAG);
}
ShaderWarning::CodeFlags ShaderWarning::get_flags_from_codemap(const HashMap<Code, bool> &p_map) {
diff --git a/servers/rendering/shader_warnings.h b/servers/rendering/shader_warnings.h
index ed28ebdd2b..69d684850f 100644
--- a/servers/rendering/shader_warnings.h
+++ b/servers/rendering/shader_warnings.h
@@ -51,6 +51,7 @@ public:
UNUSED_LOCAL_VARIABLE,
FORMATTING_ERROR,
DEVICE_LIMIT_EXCEEDED,
+ MAGIC_POSITION_WRITE,
WARNING_MAX,
};
@@ -65,6 +66,7 @@ public:
UNUSED_LOCAL_VARIABLE_FLAG = 64U,
FORMATTING_ERROR_FLAG = 128U,
DEVICE_LIMIT_EXCEEDED_FLAG = 256U,
+ MAGIC_POSITION_WRITE_FLAG = 512U,
};
private:
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 96d317ebd3..bbe6b1ad0d 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -83,25 +83,16 @@ static PackedInt64Array to_int_array(const Vector<ObjectID> &ids) {
}
PackedInt64Array RenderingServer::_instances_cull_aabb_bind(const AABB &p_aabb, RID p_scenario) const {
- if (RSG::threaded) {
- WARN_PRINT_ONCE("Using this function with a threaded renderer hurts performance, as it causes a server stall.");
- }
Vector<ObjectID> ids = instances_cull_aabb(p_aabb, p_scenario);
return to_int_array(ids);
}
PackedInt64Array RenderingServer::_instances_cull_ray_bind(const Vector3 &p_from, const Vector3 &p_to, RID p_scenario) const {
- if (RSG::threaded) {
- WARN_PRINT_ONCE("Using this function with a threaded renderer hurts performance, as it causes a server stall.");
- }
Vector<ObjectID> ids = instances_cull_ray(p_from, p_to, p_scenario);
return to_int_array(ids);
}
PackedInt64Array RenderingServer::_instances_cull_convex_bind(const TypedArray<Plane> &p_convex, RID p_scenario) const {
- if (RSG::threaded) {
- WARN_PRINT_ONCE("Using this function with a threaded renderer hurts performance, as it causes a server stall.");
- }
Vector<Plane> planes;
for (int i = 0; i < p_convex.size(); ++i) {
const Variant &v = p_convex[i];
diff --git a/servers/server_wrap_mt_common.h b/servers/server_wrap_mt_common.h
index 1a73c97fc7..40867490ca 100644
--- a/servers/server_wrap_mt_common.h
+++ b/servers/server_wrap_mt_common.h
@@ -31,12 +31,22 @@
#ifndef SERVER_WRAP_MT_COMMON_H
#define SERVER_WRAP_MT_COMMON_H
+#ifdef DEBIG_ENABLED
+#define MAIN_THREAD_SYNC_CHECK \
+ if (unlikely(Thread::is_main_thread() && Engine::get_singleton()->notify_frame_server_synced())) { \
+ MAIN_THREAD_SYNC_WARN \
+ }
+#else
+#define MAIN_THREAD_SYNC_CHECK
+#endif
+
#define FUNC0R(m_r, m_type) \
virtual m_r m_type() override { \
if (Thread::get_caller_id() != server_thread) { \
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -68,6 +78,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -102,6 +113,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(); \
@@ -113,6 +125,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(); \
@@ -128,6 +141,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -141,6 +155,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -154,6 +169,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1); \
@@ -165,6 +181,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1); \
@@ -199,6 +216,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -212,6 +230,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -225,6 +244,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2); \
@@ -236,6 +256,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2); \
@@ -270,6 +291,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -283,6 +305,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -296,6 +319,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3); \
@@ -307,6 +331,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3); \
@@ -341,6 +366,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -354,6 +380,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -367,6 +394,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4); \
@@ -378,6 +406,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4); \
@@ -412,6 +441,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -425,6 +455,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -438,6 +469,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5); \
@@ -449,6 +481,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5); \
@@ -483,6 +516,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -496,6 +530,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -509,6 +544,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5, p6); \
@@ -520,6 +556,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5, p6); \
@@ -554,6 +591,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -567,6 +605,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -580,6 +619,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5, p6, p7); \
@@ -591,6 +631,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5, p6, p7); \
@@ -625,6 +666,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, p8, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -638,6 +680,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, p8, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -651,6 +694,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, p8); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5, p6, p7, p8); \
@@ -662,6 +706,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, p8); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5, p6, p7, p8); \
diff --git a/tests/core/templates/test_command_queue.h b/tests/core/templates/test_command_queue.h
index e94c108694..d2957b5c40 100644
--- a/tests/core/templates/test_command_queue.h
+++ b/tests/core/templates/test_command_queue.h
@@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "core/math/random_number_generator.h"
+#include "core/object/worker_thread_pool.h"
#include "core/os/os.h"
#include "core/os/thread.h"
#include "core/templates/command_queue_mt.h"
@@ -100,7 +101,7 @@ public:
ThreadWork reader_threadwork;
ThreadWork writer_threadwork;
- CommandQueueMT command_queue = CommandQueueMT(true);
+ CommandQueueMT command_queue;
enum TestMsgType {
TEST_MSG_FUNC1_TRANSFORM,
@@ -119,6 +120,7 @@ public:
bool exit_threads = false;
Thread reader_thread;
+ WorkerThreadPool::TaskID reader_task_id = WorkerThreadPool::INVALID_TASK_ID;
Thread writer_thread;
int func1_count = 0;
@@ -148,11 +150,16 @@ public:
void reader_thread_loop() {
reader_threadwork.thread_wait_for_work();
while (!exit_threads) {
- if (message_count_to_read < 0) {
+ if (reader_task_id == WorkerThreadPool::INVALID_TASK_ID) {
command_queue.flush_all();
- }
- for (int i = 0; i < message_count_to_read; i++) {
- command_queue.wait_and_flush();
+ } else {
+ if (message_count_to_read < 0) {
+ command_queue.flush_all();
+ }
+ for (int i = 0; i < message_count_to_read; i++) {
+ WorkerThreadPool::get_singleton()->yield();
+ command_queue.wait_and_flush();
+ }
}
message_count_to_read = 0;
@@ -216,8 +223,13 @@ public:
sts->writer_thread_loop();
}
- void init_threads() {
- reader_thread.start(&SharedThreadState::static_reader_thread_loop, this);
+ void init_threads(bool p_use_thread_pool_sync = false) {
+ if (p_use_thread_pool_sync) {
+ reader_task_id = WorkerThreadPool::get_singleton()->add_native_task(&SharedThreadState::static_reader_thread_loop, this, true);
+ command_queue.set_pump_task_id(reader_task_id);
+ } else {
+ reader_thread.start(&SharedThreadState::static_reader_thread_loop, this);
+ }
writer_thread.start(&SharedThreadState::static_writer_thread_loop, this);
}
void destroy_threads() {
@@ -225,16 +237,20 @@ public:
reader_threadwork.main_start_work();
writer_threadwork.main_start_work();
- reader_thread.wait_to_finish();
+ if (reader_task_id != WorkerThreadPool::INVALID_TASK_ID) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(reader_task_id);
+ } else {
+ reader_thread.wait_to_finish();
+ }
writer_thread.wait_to_finish();
}
};
-TEST_CASE("[CommandQueue] Test Queue Basics") {
+static void test_command_queue_basic(bool p_use_thread_pool_sync) {
const char *COMMAND_QUEUE_SETTING = "memory/limits/command_queue/multithreading_queue_size_kb";
ProjectSettings::get_singleton()->set_setting(COMMAND_QUEUE_SETTING, 1);
SharedThreadState sts;
- sts.init_threads();
+ sts.init_threads(p_use_thread_pool_sync);
sts.add_msg_to_write(SharedThreadState::TEST_MSG_FUNC1_TRANSFORM);
sts.writer_threadwork.main_start_work();
@@ -272,6 +288,14 @@ TEST_CASE("[CommandQueue] Test Queue Basics") {
ProjectSettings::get_singleton()->property_get_revert(COMMAND_QUEUE_SETTING));
}
+TEST_CASE("[CommandQueue] Test Queue Basics") {
+ test_command_queue_basic(false);
+}
+
+TEST_CASE("[CommandQueue] Test Queue Basics with WorkerThreadPool sync.") {
+ test_command_queue_basic(true);
+}
+
TEST_CASE("[CommandQueue] Test Queue Wrapping to same spot.") {
const char *COMMAND_QUEUE_SETTING = "memory/limits/command_queue/multithreading_queue_size_kb";
ProjectSettings::get_singleton()->set_setting(COMMAND_QUEUE_SETTING, 1);
diff --git a/tests/core/threads/test_worker_thread_pool.h b/tests/core/threads/test_worker_thread_pool.h
index e9a762b57b..0a0291d11b 100644
--- a/tests/core/threads/test_worker_thread_pool.h
+++ b/tests/core/threads/test_worker_thread_pool.h
@@ -38,6 +38,7 @@
namespace TestWorkerThreadPool {
static LocalVector<SafeNumeric<int>> counter;
+static SafeFlag exit;
static void static_test(void *p_arg) {
counter[(uint64_t)p_arg].increment();
@@ -106,6 +107,72 @@ TEST_CASE("[WorkerThreadPool] Process elements using group tasks") {
}
}
+static void static_test_daemon(void *p_arg) {
+ while (!exit.is_set()) {
+ counter[0].add(1);
+ WorkerThreadPool::get_singleton()->yield();
+ }
+}
+
+static void static_busy_task(void *p_arg) {
+ while (!exit.is_set()) {
+ OS::get_singleton()->delay_usec(1);
+ }
+}
+
+static void static_legit_task(void *p_arg) {
+ *((bool *)p_arg) = counter[0].get() > 0;
+ counter[1].add(1);
+}
+
+TEST_CASE("[WorkerThreadPool] Run a yielding daemon as the only hope for other tasks to run") {
+ exit.clear();
+ counter.clear();
+ counter.resize(2);
+
+ WorkerThreadPool::TaskID daemon_task_id = WorkerThreadPool::get_singleton()->add_native_task(static_test_daemon, nullptr, true);
+
+ int num_threads = WorkerThreadPool::get_singleton()->get_thread_count();
+
+ // Keep all the other threads busy.
+ LocalVector<WorkerThreadPool::TaskID> task_ids;
+ for (int i = 0; i < num_threads - 1; i++) {
+ task_ids.push_back(WorkerThreadPool::get_singleton()->add_native_task(static_busy_task, nullptr, true));
+ }
+
+ LocalVector<WorkerThreadPool::TaskID> legit_task_ids;
+ LocalVector<bool> legit_task_needed_yield;
+ int legit_tasks_count = num_threads * 4;
+ legit_task_needed_yield.resize(legit_tasks_count);
+ for (int i = 0; i < legit_tasks_count; i++) {
+ legit_task_needed_yield[i] = false;
+ task_ids.push_back(WorkerThreadPool::get_singleton()->add_native_task(static_legit_task, &legit_task_needed_yield[i], i >= legit_tasks_count / 2));
+ }
+
+ while (counter[1].get() != legit_tasks_count) {
+ OS::get_singleton()->delay_usec(1);
+ }
+
+ exit.set();
+ for (uint32_t i = 0; i < task_ids.size(); i++) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(task_ids[i]);
+ }
+ WorkerThreadPool::get_singleton()->notify_yield_over(daemon_task_id);
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(daemon_task_id);
+
+ CHECK_MESSAGE(counter[0].get() > 0, "Daemon task should have looped at least once.");
+ CHECK_MESSAGE(counter[1].get() == legit_tasks_count, "All legit tasks should have been able to run.");
+
+ bool all_needed_yield = true;
+ for (int i = 0; i < legit_tasks_count; i++) {
+ if (!legit_task_needed_yield[i]) {
+ all_needed_yield = false;
+ break;
+ }
+ }
+ CHECK_MESSAGE(all_needed_yield, "All legit tasks should have needed the daemon yielding to run.");
+}
+
} // namespace TestWorkerThreadPool
#endif // TEST_WORKER_THREAD_POOL_H
diff --git a/tests/core/variant/test_array.h b/tests/core/variant/test_array.h
index 287345e831..c54854e4d7 100644
--- a/tests/core/variant/test_array.h
+++ b/tests/core/variant/test_array.h
@@ -555,6 +555,8 @@ TEST_CASE("[Array] Iteration") {
idx++;
}
+ CHECK_EQ(idx, a1.size());
+
idx = 0;
for (const Variant &E : (const Array &)a1) {
@@ -562,6 +564,8 @@ TEST_CASE("[Array] Iteration") {
idx++;
}
+ CHECK_EQ(idx, a1.size());
+
a1.clear();
}
diff --git a/tests/scene/test_navigation_region_2d.h b/tests/scene/test_navigation_region_2d.h
index 4574893c8d..fcb5aeacbe 100644
--- a/tests/scene/test_navigation_region_2d.h
+++ b/tests/scene/test_navigation_region_2d.h
@@ -41,7 +41,7 @@ namespace TestNavigationRegion2D {
TEST_SUITE("[Navigation]") {
TEST_CASE("[SceneTree][NavigationRegion2D] New region should have valid RID") {
NavigationRegion2D *region_node = memnew(NavigationRegion2D);
- CHECK(region_node->get_region_rid().is_valid());
+ CHECK(region_node->get_rid().is_valid());
memdelete(region_node);
}
}
diff --git a/tests/scene/test_navigation_region_3d.h b/tests/scene/test_navigation_region_3d.h
index 372f6dc505..0b20b3a1b2 100644
--- a/tests/scene/test_navigation_region_3d.h
+++ b/tests/scene/test_navigation_region_3d.h
@@ -43,7 +43,7 @@ namespace TestNavigationRegion3D {
TEST_SUITE("[Navigation]") {
TEST_CASE("[SceneTree][NavigationRegion3D] New region should have valid RID") {
NavigationRegion3D *region_node = memnew(NavigationRegion3D);
- CHECK(region_node->get_region_rid().is_valid());
+ CHECK(region_node->get_rid().is_valid());
memdelete(region_node);
}
@@ -71,15 +71,6 @@ TEST_SUITE("[Navigation]") {
CHECK_NE(navigation_mesh->get_vertices().size(), 0);
}
- // Race condition is present in the below subcase, but baking should take many
- // orders of magnitude longer than basic checks on the main thread, so it's fine.
- SUBCASE("Asynchronous bake should not be immediate") {
- navigation_region->bake_navigation_mesh(true);
- CHECK(navigation_region->is_baking());
- CHECK_EQ(navigation_mesh->get_polygon_count(), 0);
- CHECK_EQ(navigation_mesh->get_vertices().size(), 0);
- }
-
memdelete(mesh_instance);
memdelete(navigation_region);
memdelete(node_3d);
diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h
index 827a1bed17..8778ea86a6 100644
--- a/tests/servers/test_navigation_server_3d.h
+++ b/tests/servers/test_navigation_server_3d.h
@@ -764,6 +764,8 @@ TEST_SUITE("[Navigation]") {
navigation_server->process(0.0); // Give server some cycles to commit.
}
+ // FIXME: The race condition mentioned below is actually a problem and fails on CI (GH-90613).
+ /*
TEST_CASE("[NavigationServer3D] Server should be able to bake asynchronously") {
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
@@ -781,6 +783,7 @@ TEST_SUITE("[Navigation]") {
CHECK_EQ(navigation_mesh->get_polygon_count(), 0);
CHECK_EQ(navigation_mesh->get_vertices().size(), 0);
}
+ */
}
} //namespace TestNavigationServer3D
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index bb6837c965..56bd8739c6 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -100,7 +100,6 @@
#include "tests/core/variant/test_variant.h"
#include "tests/core/variant/test_variant_utility.h"
#include "tests/scene/test_animation.h"
-#include "tests/scene/test_arraymesh.h"
#include "tests/scene/test_audio_stream_wav.h"
#include "tests/scene/test_bit_map.h"
#include "tests/scene/test_camera_2d.h"
@@ -127,6 +126,7 @@
#include "tests/test_validate_testing.h"
#ifndef _3D_DISABLED
+#include "tests/scene/test_arraymesh.h"
#include "tests/scene/test_camera_3d.h"
#include "tests/scene/test_navigation_agent_2d.h"
#include "tests/scene/test_navigation_agent_3d.h"
diff --git a/thirdparty/mbedtls/include/godot_core_mbedtls_config.h b/thirdparty/mbedtls/include/godot_core_mbedtls_config.h
index d27bf608fb..f22ef3aa08 100644
--- a/thirdparty/mbedtls/include/godot_core_mbedtls_config.h
+++ b/thirdparty/mbedtls/include/godot_core_mbedtls_config.h
@@ -50,4 +50,11 @@
#define MBEDTLS_PLATFORM_ZEROIZE_ALT
#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
+// This is only to pass a check in the mbedtls check_config.h header, none of
+// the files we include as part of the core build uses it anyway, we already
+// define MBEDTLS_PLATFORM_ZEROIZE_ALT which is the only relevant function.
+#if defined(__MINGW32__)
+#define MBEDTLS_PLATFORM_C
+#endif
+
#endif // GODOT_CORE_MBEDTLS_CONFIG_H