Compare commits

...

89 Commits

Author SHA1 Message Date
Yuri Sizov bd6af8e0ea
Merge pull request #79576 from YuriSizov/4.1-cherrypicks
Cherry-picks for the 4.1 branch (future 4.1.1) - 2nd batch
2023-07-17 14:17:04 +07:00
Yuri Sizov e94d355366 Bump version to 4.1.1-stable 2023-07-17 12:15:23 +07:00
Yuri Sizov 80aef72ef3 Update the changelog for 4.1.1 2023-07-17 12:12:37 +07:00
kobewi b45e7f0f63 Emit history_changed on merged UndoRedo actions
(cherry picked from commit b6bb0d505d)
2023-07-17 12:05:43 +07:00
Septian a6b1c0edbb Fix `tween_property` on "Basis" to properly update its value
(cherry picked from commit eb7f87e9a8)
2023-07-17 12:05:43 +07:00
WiseNoodle 085b16b1bb Add missing word to text of the alert dialog
(cherry picked from commit d1c70cf7d4)
2023-07-17 12:05:42 +07:00
stmSi 74efa063cc Fix: ESC (ui_cancel) not closing FindReplaceBar
(cherry picked from commit 7eb517c27f)
2023-07-17 12:05:42 +07:00
Septian c1ddd0485b Fix dragged nodes have icon size
(cherry picked from commit 837df886f3)
2023-07-17 12:05:42 +07:00
kobewi 0a4067bdbf Clarify return value of get_dependencies()
(cherry picked from commit f567af413a)
2023-07-17 12:05:42 +07:00
Yuri Sizov e709ad4d64
Merge pull request #79297 from YuriSizov/4.1-cherrypicks
Cherry-picks for the 4.1 branch (future 4.1.1) - 1st batch
2023-07-11 15:55:57 +07:00
Yuri Sizov 92cf616f63 Add changelog for 4.1.1 2023-07-11 13:45:48 +07:00
kleonc 2796b629e9 Hide/show AcceptDialog's button spacer on button visibility changed
(cherry picked from commit 30a9c90785)
2023-07-11 13:21:07 +07:00
Rémi Verschelde 30ab0e2cf4 Fix Linux `move_to_trash` wrongly reporting files as not found
We can't rely on the error code from `gio` or `kioclient5`, in my
rudimentary testing they return `1` for both missing files and other
situations like not having a Trash can on the mounted volume.

Fixes #79108.

(cherry picked from commit a6e75f3971)
2023-07-11 12:21:28 +07:00
Rémi Verschelde 6ec2d6d7d0 Linux: Fix build with `use_sowrap=no` and various warnings/errors
(cherry picked from commit dcd16a5750)
2023-07-11 12:21:28 +07:00
Aaron Franke e3cfc023b4 Improve text in popup warning, remove "upgrade or downgrade" text
(cherry picked from commit 95ccbdea4b)
2023-07-11 12:21:28 +07:00
Ninni Pipping 1e2bfdc9bb Improve error message for `Node.set_owner`
(cherry picked from commit 0183340012)
2023-07-11 12:21:27 +07:00
Haoyu Qiu 167b02d942 Clarify EditorExportPlugin::add_file only remaps in _export_file
(cherry picked from commit df1d354d36)
2023-07-11 12:21:27 +07:00
HolonProduction 438a598713 Update FileDialog button activity when file_mode is changed.
(cherry picked from commit cfac7c9a9a)
2023-07-10 18:09:59 +07:00
Aaron Franke 5729e40035 Improve user-friendliness of project version mismatch message
(cherry picked from commit 3b8a81d361)
2023-07-10 18:09:58 +07:00
Aaron Franke ec6d258db0 Fix property hint class name type string restriction and replace mode
(cherry picked from commit 58a65591f5)
2023-07-10 18:09:58 +07:00
kobewi a79160ebd2 Collapse bottom panel if there is no active tab
(cherry picked from commit 59ae7e2445)
2023-07-10 18:09:58 +07:00
Pedro J. Estébanez b3a56228b1 Fix zero-sized WorkerThreadPool not processing group tasks
(cherry picked from commit 28d0d56a69)
2023-07-10 18:09:58 +07:00
Emmanouil Papadeas 2f32a3454c Fix `Camera2D.rotating` not being converted and reversed properly
Godot 3's Camera2D `rotating = true` and `rotating = false` are supposed to be converted and reversed to `ignore_rotation = false` and `ignore_rotation = true` respectively, but this wasn't the case before this PR, as the project converted was failing to properly read the `true` and `false` strings, thus resulting in `ignore_rotation = true` in all cases.

(cherry picked from commit 256b99ca40)
2023-07-10 18:09:58 +07:00
Rémi Verschelde 29b4ee3d97 Change explicit 'Godot 4.0' references to 'Godot 4'
Fixes #79276.

(cherry picked from commit 8a06ec979e)
2023-07-10 18:09:58 +07:00
Chinmay Awale 4f4052581b add 3.x compatibility for animation loop mode
(cherry picked from commit 1686a7a1a2)
2023-07-10 18:09:58 +07:00
NiskashY 36ce14a826 Fix disabled slider highlighting
(cherry picked from commit 4394936392)
2023-07-10 18:09:58 +07:00
Rindbee 1c1d4f6264 Fix `PackedScene::get_last_modified_time()` always returns `0`
The variables operated by `PackedScene::set_last_modified_time()`
and `PackedScene::get_last_modified_time()` are different.

(cherry picked from commit 22edef14c3)
2023-07-10 18:09:58 +07:00
clayjohn d6d8f6a637 Unify error condition for particles trail lifetime
(cherry picked from commit 78ecdb17f9)
2023-07-10 18:09:57 +07:00
Chris Bradfield 55ae2a3297 Fix rigid body property description
(cherry picked from commit 6d85481670)
2023-07-10 18:09:57 +07:00
smix8 0d51fec22b Fix closest possible navigation path position
Fixes closest possible navigation path position.

(cherry picked from commit e5c24f7118)
2023-07-10 18:09:57 +07:00
smix8 bbfdfab748 Add performance note for parsing source geometry
Adds performance note for parsing source geometry.

(cherry picked from commit 976f5338c5)
2023-07-10 18:09:57 +07:00
Kamil Brzoskowski 6becf94f49 Fix formatting of dlopen error message on Windows
And harmonize the format for all platforms.

Co-authored-by: Rémi Verschelde <rverschelde@gmail.com>
(cherry picked from commit 3cd865dbe8)
2023-07-10 18:09:57 +07:00
clayjohn 7447946dd1 Avoid freeze when interacting with menus on Wayland by re-aquiring next swapchain image after updating swapchain
(cherry picked from commit df021b5063)
2023-07-10 18:09:57 +07:00
Septian 5576f5ab81 Fix various typos in documentation
(cherry picked from commit 486609eccf)
2023-07-10 17:46:09 +07:00
RedworkDE 3f334cb144 C#: Fix command line exporting
(cherry picked from commit f3f3365abd)
2023-07-10 17:46:09 +07:00
Markus Sauermann 42b8ae50d2 Fix `Node::add_sibling` parent check
Replace `data.blocked > 0` by `data.parent->data.blocked > 0` in order
to check if the parent is busy.

(cherry picked from commit b02dff6e1c)
2023-07-10 17:46:09 +07:00
Septian add7c218d1 fix typo on TLSOptions.xml and library_godot_display.js
(cherry picked from commit b88b6b4f48)
2023-07-10 17:46:09 +07:00
Arman Elgudzhyan 361c0d53f3 Clear specular buffer if bg mode is canvas and ss effects are used
Explicitly clear the separate specular buffer when the background mode is canvas and screen space effects (and thus a separate specular buffer) are used.

(cherry picked from commit af9d1743f3)
2023-07-10 17:46:09 +07:00
Aaron Franke c8b50871fe Fix incorrect documentation for `Engine.get_architecture_name()`
(cherry picked from commit ebc6ec1692)
2023-07-10 17:46:09 +07:00
MewPurPur a4547db15b Fix erroneous pad_zeros warning
(cherry picked from commit cc5500f7de)
2023-07-10 17:46:09 +07:00
Ninni Pipping fb8e21bd6d Fix `rpc` calls with binds
(cherry picked from commit 7d174c8dfe)
2023-07-10 17:46:09 +07:00
Ninni Pipping 90b4a3fa75 Add compatibility properties to `TouchScreenButton`
Added support for `3.x` properties:
* `normal` -> `texture_normal`
* `pressed` -> `texture_pressed`

(cherry picked from commit c7e4b3bf5f)
2023-07-10 17:46:08 +07:00
Yadnesh Kulkarni 09c245fd74 Fixed grid disappearance
(cherry picked from commit a8dde286ce)
2023-07-10 17:46:08 +07:00
Joe Marshall fa6fb0ac70 fix threading bug in vulkan rendering device
(cherry picked from commit c52fadbe75)
2023-07-10 17:46:08 +07:00
Angad Kambli cdeddffee7 Check parameter validity in `Object::set_script`
Fixes #46120.

(cherry picked from commit 9c6c2f09e0)
2023-07-10 17:46:08 +07:00
Rémi Verschelde 4c1c26979b Linux: Link libsquish directly when unbundling, .pc file unreliable
(cherry picked from commit b3b4f4c1c9)
2023-07-10 17:46:08 +07:00
bruvzg fa45bb63c6 [Windows] Fix setting initial non-exclusive window mode.
(cherry picked from commit db0109b237)
2023-07-10 17:46:08 +07:00
bruvzg 393076a4b3 [macOS/iOS] Set MoltenVK logging level based on `--verbose` flag.
(cherry picked from commit 75d0fcea16)
2023-07-10 17:46:08 +07:00
Ivan Shakhov c9b1d99cae Update the RiderPathLocator to support the JetBrains Toolbox 2.0
(cherry picked from commit bf3af9fd48)
2023-07-10 17:46:08 +07:00
Florian Kothmeier 240701f95a Fix invalid minimum size for translated messages in option button
(cherry picked from commit c33748d954)
2023-07-10 17:46:08 +07:00
Hugo Locurcio ac87b5df75 Mention Xbox menu button by name in Start button description
Microsoft officially calls it the Menu button:

https://support.xbox.com/en-US/help/hardware-network/controller/get-to-know-your-xbox-series-x-s-controller
(cherry picked from commit 1621b4e2b1)
2023-07-10 17:46:08 +07:00
Hugo Locurcio fe8e7a0b22 Use bullet points in shader editor creation dialog
This is consistent with the script creation dialog.

(cherry picked from commit bce2985615)
2023-07-10 17:46:07 +07:00
Hugo Locurcio e32330473a Remove uses of `vformat()` with no placeholders
This is identical to passing the string directly.

(cherry picked from commit dcc92c174e)
2023-07-10 17:46:07 +07:00
lewiji a084f0568f Return shader parse error when using 'hint_normal_roughness_texture' and not using the Forward+ backend
(cherry picked from commit 2a93681334)
2023-07-10 17:46:07 +07:00
Dawid Marzec 87b4143f3b Fix cursor behaviour in Tree while holding CTRL
(cherry picked from commit 9abbdea95e)
2023-07-10 17:26:15 +07:00
Rémi Verschelde 1875ecb776 Project converter: Use same rendering driver as Project Manager
Which means by default OpenGL 3, but it can still be overridden from the command line.
Fixes #76303.

(cherry picked from commit 53c78b2cac)
2023-07-10 17:26:15 +07:00
jpcerrone 2ba192e803 Fix comments and indentation in .gdshaderinc files
Fixes #78205
The handling of comments and indentation in the shader editor
wasn't considering shader include files.

(cherry picked from commit 71b8a9d274)
2023-07-10 17:26:15 +07:00
MewPurPur 8cefce591a Improve string printing in the tiledata editor
(cherry picked from commit 1649dcad0b)
2023-07-10 17:26:15 +07:00
MewPurPur ab14aa9f16 Fix enum tooltip with no description
(cherry picked from commit c0453a544d)
2023-07-10 17:26:15 +07:00
Aaron Franke 4cec4bd32f Sort project tags before saving
(cherry picked from commit d667402461)
2023-07-10 17:26:15 +07:00
kobewi ffc87b2bb1 Focus current node after connecting
(cherry picked from commit d17c522991)
2023-07-10 17:26:15 +07:00
Bauke Conijn 3fd5fecfc1 Fix Camera3D project_* methods not accounting for frustum offset
This does not fix Camera3D::project_ray_normal().
Adds Camera3D::get_camera_projection() and exposes it to GDScript

(cherry picked from commit 47e63bc55f)
2023-07-10 17:26:15 +07:00
bruvzg 221535c33c [Windows] Flash both the window caption and taskbar button on `request_attention`.
(cherry picked from commit 49af2582c4)
2023-07-10 17:26:14 +07:00
Markus Sauermann 4e84660b50 Fix that `_drop_physics_mouseover` only happens when necessary
Previously the call was executed every time, because in the
`_drop_mouse_over();` a few lines above, `gui.mouse_over = nullptr;`
was set.

(cherry picked from commit 37a96d3957)
2023-07-10 17:26:14 +07:00
RedworkDE 69948f7489 C#: Add null check before calling `UnregisterGodotObject`
(cherry picked from commit 693e6e036b)
2023-07-10 17:26:14 +07:00
mb4c 6018ff49d6 Add tooltip description wrapping in scene tree and plugin settings
(cherry picked from commit d007be2d14)
2023-07-10 17:26:14 +07:00
Alfonso J. Ramos 8cea540eba Do not change a node unique name to the same name
(cherry picked from commit b2bef8c47b)
2023-07-10 17:26:14 +07:00
Silc Lizard (Tokage) Renew ed9c091a92 Fix infinity loop state can't break
(cherry picked from commit fc40ba21cd)
2023-07-10 17:26:14 +07:00
bitsawer f5addd583d Make shader preprocessor keyword colors consistent
(cherry picked from commit a5d6152949)
2023-07-10 17:26:14 +07:00
bitsawer 0dec3d6485 Fix shader language float literal precision truncation
(cherry picked from commit 356297f909)
2023-07-10 17:26:14 +07:00
ocean (they/them) a0366f1cea Fix regression with enum descriptions now showing up in documentation.
(cherry picked from commit d48636c3bf)
2023-07-10 17:26:14 +07:00
kobewi 705c1d6bdf Fix dropping files from res:// to res://
(cherry picked from commit 1d970cd6ca)
2023-07-10 17:26:14 +07:00
Rindbee 836913ce7a Make sure the shortcut key respects the context in `TileSetAtlasSourceEditor`
(cherry picked from commit fec731bf33)
2023-07-10 17:26:13 +07:00
Raul Santos 80105226c2 C#: Compare symbol names without null flow state
(cherry picked from commit 671a5b4ea5)
2023-07-10 17:26:13 +07:00
nklbdev 2bfeb29bc6 Potencially fix nan's on octahedral tangents in RenderingServer
(cherry picked from commit 1d16704faf)
2023-07-10 17:26:13 +07:00
nklbdev 92040e85e2 Fix wrong type casting for octahedral tangents
(cherry picked from commit c022f52f11)
2023-07-10 17:26:13 +07:00
Haoyu Qiu 545e37cf77 Translate "No match" message in FindReplaceBar
(cherry picked from commit ac454ce2a7)
2023-07-10 17:26:13 +07:00
Haoyu Qiu a8bfdd8bea Fix error when non-ASCII characters in resource pack path
(cherry picked from commit df5c68af99)
2023-07-10 17:11:22 +07:00
Ben Rog-Wilhelm 371b31c85f Fix: Incorrect property names in FontFile::_get_property_list().
(cherry picked from commit 7ee916a259)
2023-07-10 17:11:22 +07:00
Amir-Rasteg 7a8ac69862 Fix a typo in the `String.to_float` description
(cherry picked from commit 9744657bb8)
2023-07-10 17:11:22 +07:00
RedworkDE 03e82be503 Fix export options of scripted EditorExportPlugins
(cherry picked from commit fa84d09542)
2023-07-10 17:11:22 +07:00
Bastiaan Olij 89e64da028 Take eye offset into account for depth in StandardMaterial3D
(cherry picked from commit 581d081ded)
2023-07-10 17:11:22 +07:00
Rémi Verschelde da1e511f11 Linux: Allow unbundling brotli to use system library
(cherry picked from commit 153c4a4c4f)
2023-07-10 17:11:22 +07:00
Yuri Roubinski 6255a64e03 Fix using uint suffix at the hex number declaration in shaders
(cherry picked from commit 1994c25701)
2023-07-10 17:11:22 +07:00
clayjohn bc9bc236c0 Initialize particles instance buffer in case it is used before being updated
(cherry picked from commit 35ed7c770b)
2023-07-10 17:11:22 +07:00
Markus Sauermann 0ec599473d Revert "Fix focusloss of non-exclusive `AcceptDialog` with `close_on_escape`"
This reverts commit 7f547fcf09.

(cherry picked from commit bfa7497c1b)
2023-07-10 17:11:22 +07:00
Daylily-Zeleen 71d5827228 Fixed the fallback logic of OS::shell_show_in_file_manager
(cherry picked from commit 9dd9818c88)
2023-07-10 17:11:21 +07:00
RedworkDE ce5c6151fc
C#: Automatically generate version defines
(cherry picked from commit fe7c27b086)
2023-07-05 22:16:02 +07:00
Rémi Verschelde 529a55bab2
Bump version to 4.1.1-rc 2023-07-05 22:14:15 +07:00
135 changed files with 931 additions and 859 deletions

@ -5,7 +5,7 @@ on:
# Global Settings
env:
# Used for the cache key. Add version suffix to force clean build.
GODOT_BASE_BRANCH: master
GODOT_BASE_BRANCH: '4.1'
SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no module_text_server_fb_enabled=yes
concurrency:

@ -5,7 +5,7 @@ on:
# Global Settings
env:
# Used for the cache key. Add version suffix to force clean build.
GODOT_BASE_BRANCH: master
GODOT_BASE_BRANCH: '4.1'
SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no module_text_server_fb_enabled=yes
concurrency:

@ -5,7 +5,7 @@ on:
# Global Settings
env:
# Used for the cache key, and godot-cpp checkout. Add version suffix to force clean build.
GODOT_BASE_BRANCH: master
GODOT_BASE_BRANCH: '4.1'
SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true

@ -5,7 +5,7 @@ on:
# Global Settings
env:
# Used for the cache key. Add version suffix to force clean build.
GODOT_BASE_BRANCH: master
GODOT_BASE_BRANCH: '4.1'
SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes
concurrency:

@ -5,7 +5,7 @@ on:
# Global Settings
env:
# Used for the cache key. Add version suffix to force clean build.
GODOT_BASE_BRANCH: master
GODOT_BASE_BRANCH: '4.1'
SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no
EM_VERSION: 3.1.18
EM_CACHE_FOLDER: "emsdk-cache"

@ -6,7 +6,7 @@ on:
# SCONS_CACHE for windows must be set in the build environment
env:
# Used for the cache key. Add version suffix to force clean build.
GODOT_BASE_BRANCH: master
GODOT_BASE_BRANCH: '4.1'
SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes
SCONS_CACHE_MSVC_CONFIG: true

@ -4,6 +4,167 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [4.1.1] - 2023-07-17
See the [release announcement](https://godotengine.org/article/maintenance-release-godot-4-1-1) for details.
### Added
#### Animation
- Add 3.x compatibility for animation loop mode ([GH-79155](https://github.com/godotengine/godot/pull/79155)).
#### GUI
- Add compatibility properties to `TouchScreenButton` ([GH-78940](https://github.com/godotengine/godot/pull/78940)).
### Changed
#### 2D
- Improve string drawing in the tiledata editor ([GH-78522](https://github.com/godotengine/godot/pull/78522)).
- Make sure the shortcut key respects the context in `TileSetAtlasSourceEditor` ([GH-78920](https://github.com/godotengine/godot/pull/78920)).
#### Buildsystem
- Linux: Allow unbundling brotli to use system library ([GH-79101](https://github.com/godotengine/godot/pull/79101)).
- Linux: Link libsquish directly when unbundling, .pc file unreliable ([GH-79105](https://github.com/godotengine/godot/pull/79105)).
#### C#/.NET
- Update the RiderPathLocator to support the JetBrains Toolbox 2.0 ([GH-78832](https://github.com/godotengine/godot/pull/78832)).
- Compare symbol names without null flow state ([GH-79094](https://github.com/godotengine/godot/pull/79094)).
- Add null check before calling `UnregisterGodotObject` ([GH-79151](https://github.com/godotengine/godot/pull/79151)).
#### Core
- Check parameter validity in `Object::set_script` ([GH-46125](https://github.com/godotengine/godot/pull/46125)).
- Improve error message for `Node.set_owner` ([GH-79000](https://github.com/godotengine/godot/pull/79000)).
#### Editor
- Focus current node after connecting ([GH-54071](https://github.com/godotengine/godot/pull/54071)).
- Fix tooltip of enum value without description ([GH-78524](https://github.com/godotengine/godot/pull/78524)).
- Use bullet points in shader editor creation dialog ([GH-78631](https://github.com/godotengine/godot/pull/78631)).
- Sort project tags before saving ([GH-78775](https://github.com/godotengine/godot/pull/78775)).
- Project converter: Use same rendering driver as Project Manager ([GH-78795](https://github.com/godotengine/godot/pull/78795)).
- Add tooltip description wrapping in scene tree and plugin settings ([GH-79090](https://github.com/godotengine/godot/pull/79090)).
- Improve user-friendliness of project version mismatch messages ([GH-79118](https://github.com/godotengine/godot/pull/79118), [GH-79299](https://github.com/godotengine/godot/pull/79299)).
#### GUI
- Ensure that `_drop_physics_mouseover` only happens when necessary ([GH-78078](https://github.com/godotengine/godot/pull/78078)).
- Update FileDialog button activity when `file_mode` is changed ([GH-79211](https://github.com/godotengine/godot/pull/79211)).
- Hide/show `AcceptDialog`'s button spacer on button visibility changed ([GH-79274](https://github.com/godotengine/godot/pull/79274)).
#### Porting
- macOS/iOS: Set MoltenVK logging level based on `--verbose` flag ([GH-79061](https://github.com/godotengine/godot/pull/79061)).
- Windows: Flash both the window caption and taskbar button on `request_attention` ([GH-78263](https://github.com/godotengine/godot/pull/78263)).
#### Rendering
- Clear specular buffer if sky mode is canvas and screen space effects are used ([GH-78624](https://github.com/godotengine/godot/pull/78624)).
- Take eye offset into account for depth in StandardMaterial3D ([GH-79049](https://github.com/godotengine/godot/pull/79049)).
### Fixed
#### 2D
- Fix `Camera2D.rotating` not being converted and reversed properly ([GH-79264](https://github.com/godotengine/godot/pull/79264)).
#### 3D
- Fix Camera3D `project_*` methods not accounting for frustum offset ([GH-75806](https://github.com/godotengine/godot/pull/75806)).
- Fix 3D viewport grid disappearing on scene tab changes ([GH-78694](https://github.com/godotengine/godot/pull/78694)).
#### Animation
- Fix infinite loop state check in `AnimationStateMachine` ([GH-79141](https://github.com/godotengine/godot/pull/79141)).
- Fix `tween_property` on `Basis` to properly update its value ([GH-79426](https://github.com/godotengine/godot/pull/79426)).
#### Buildsystem
- Linux: Fix build with `use_sowrap=no` and various warnings/errors ([GH-79097](https://github.com/godotengine/godot/pull/79097)).
#### C#/.NET
- Fix command line exporting ([GH-79173](https://github.com/godotengine/godot/pull/79173)).
#### Core
- Fix zero-sized WorkerThreadPool not processing group tasks ([GH-78845](https://github.com/godotengine/godot/pull/78845)).
- Fix `Node::add_sibling` parent check ([GH-78847](https://github.com/godotengine/godot/pull/78847)).
- Fix error when non-ASCII characters in resource pack path ([GH-78935](https://github.com/godotengine/godot/pull/78935)).
- Fix erroneous `pad_zeros()` warning ([GH-79202](https://github.com/godotengine/godot/pull/79202)).
- Fix `PackedScene::get_last_modified_time()` always returns `0` ([GH-79237](https://github.com/godotengine/godot/pull/79237)).
#### Editor
- Fix dropping files from `res://` to `res://` ([GH-78914](https://github.com/godotengine/godot/pull/78914)).
- Do not change a node unique name to the same name ([GH-78925](https://github.com/godotengine/godot/pull/78925)).
- Collapse bottom panel if there is no active tab ([GH-79078](https://github.com/godotengine/godot/pull/79078)).
- Fix `ui_cancel` action not closing `FindReplaceBar` ([GH-79079](https://github.com/godotengine/godot/pull/79079)).
- Emit `history_changed` on merged UndoRedo actions ([GH-79484](https://github.com/godotengine/godot/pull/79484)).
#### Export
- Fix export options of scripted `EditorExportPlugin`s ([GH-79025](https://github.com/godotengine/godot/pull/79025)).
#### GDScript
- Fix regression with GDScript enum descriptions now showing up in documentation ([GH-78953](https://github.com/godotengine/godot/pull/78953)).
#### GUI
- Fix cursor behavior for multiselect in Tree while holding CTRL ([GH-71024](https://github.com/godotengine/godot/pull/71024)).
- Fix disabled slider highlighting ([GH-78776](https://github.com/godotengine/godot/pull/78776)).
- Fix invalid minimum size for translated messages in option button ([GH-78835](https://github.com/godotengine/godot/pull/78835)).
- Fix incorrect property names in `FontFile::_get_property_list()` ([GH-78907](https://github.com/godotengine/godot/pull/78907)).
- Revert "Fix focusloss of non-exclusive `AcceptDialog` with `close_on_escape`" ([GH-79084](https://github.com/godotengine/godot/pull/79084)).
#### Import
- Fix property hint class name type string restriction and replace mode ([GH-79139](https://github.com/godotengine/godot/pull/79139)).
#### Navigation
- Fix closest possible navigation path position ([GH-79004](https://github.com/godotengine/godot/pull/79004)).
#### Networking
- Fix `rpc` calls with binds ([GH-78551](https://github.com/godotengine/godot/pull/78551)).
#### Particles
- Initialize particles instance buffer in case it is used before being updated ([GH-78852](https://github.com/godotengine/godot/pull/78852)).
- Unify error condition for particles trail lifetime ([GH-79270](https://github.com/godotengine/godot/pull/79270)).
#### Physics
- Fix rigid body `contact_monitor` property description ([GH-79250](https://github.com/godotengine/godot/pull/79250)).
#### Porting
- Fix formatting of `dlopen` error messages ([GH-78802](https://github.com/godotengine/godot/pull/78802)).
- Fix the fallback logic of `OS::shell_show_in_file_manager` ([GH-79087](https://github.com/godotengine/godot/pull/79087)).
- Linux/BSD: Avoid freeze when interacting with menus on Wayland ([GH-79143](https://github.com/godotengine/godot/pull/79143)).
- Linux/BSD: Fix `move_to_trash` wrongly reporting files as not found ([GH-79284](https://github.com/godotengine/godot/pull/79284)).
- Windows: Fix setting initial non-exclusive window mode ([GH-79016](https://github.com/godotengine/godot/pull/79016)).
#### Rendering
- Fix threading bug in Vulkan rendering device ([GH-78794](https://github.com/godotengine/godot/pull/78794)).
- Fix sanitizers reports about octahedral tangents in RenderingServer ([GH-78902](https://github.com/godotengine/godot/pull/78902)).
#### Shaders
- Fix invalid shader compilation when using `hint_normal_roughness_texture` in mobile backend ([GH-78839](https://github.com/godotengine/godot/pull/78839)).
- Fix using uint suffix at the hex number declaration in shaders ([GH-78906](https://github.com/godotengine/godot/pull/78906)).
- Fix shader language float literal precision truncation ([GH-78972](https://github.com/godotengine/godot/pull/78972)).
- Fix comments and indentation in `.gdshaderinc` files ([GH-79158](https://github.com/godotengine/godot/pull/79158)).
## [4.1] - 2023-07-06
See the [release announcement](https://godotengine.org/article/godot-4-1-is-here) for details.
@ -161,6 +322,7 @@ See the [release announcement](https://godotengine.org/article/godot-4-1-is-here
#### Core
- The strings returned by `ResourceLoader::get_dependencies()` now include paths in addition to UIDs ([GH-73131](https://github.com/godotengine/godot/pull/73131)).
- Optimize Node children management ([GH-75627](https://github.com/godotengine/godot/pull/75627)).
- Deprecate `NOTIFICATION_MOVED_IN_PARENT` for `NOTIFICATION_CHILD_ORDER_CHANGED` ([GH-75701](https://github.com/godotengine/godot/pull/75701)).
- Optimize `Node::add_child` validation ([GH-75760](https://github.com/godotengine/godot/pull/75760)).
@ -2876,6 +3038,7 @@ See the [release announcement](https://godotengine.org/article/godot-3-3-has-arr
- Only WebAssembly is supported now, since all browsers supporting WebGL 2.0 also support WebAssembly.
[4.1.1]: https://github.com/godotengine/godot/compare/4.1-stable...4.1.1-stable
[4.1]: https://github.com/godotengine/godot/compare/4.0-stable...4.1-stable
[4.0]: https://github.com/godotengine/godot/compare/3.2-stable...4.0-stable
[3.5]: https://github.com/godotengine/godot/compare/3.4-stable...3.5-stable

@ -222,6 +222,7 @@ opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise e
opts.Add(BoolVariable("scu_build", "Use single compilation unit build", False))
# Thirdparty libraries
opts.Add(BoolVariable("builtin_brotli", "Use the built-in Brotli library", True))
opts.Add(BoolVariable("builtin_certs", "Use the built-in SSL certificates bundles", True))
opts.Add(BoolVariable("builtin_embree", "Use the built-in Embree library", True))
opts.Add(BoolVariable("builtin_enet", "Use the built-in ENet library", True))
@ -296,21 +297,21 @@ else:
if selected_platform in ["macos", "osx"]:
if selected_platform == "osx":
# Deprecated alias kept for compatibility.
print('Platform "osx" has been renamed to "macos" in Godot 4.0. Building for platform "macos".')
print('Platform "osx" has been renamed to "macos" in Godot 4. Building for platform "macos".')
# Alias for convenience.
selected_platform = "macos"
if selected_platform in ["ios", "iphone"]:
if selected_platform == "iphone":
# Deprecated alias kept for compatibility.
print('Platform "iphone" has been renamed to "ios" in Godot 4.0. Building for platform "ios".')
print('Platform "iphone" has been renamed to "ios" in Godot 4. Building for platform "ios".')
# Alias for convenience.
selected_platform = "ios"
if selected_platform in ["linux", "bsd", "x11"]:
if selected_platform == "x11":
# Deprecated alias kept for compatibility.
print('Platform "x11" has been renamed to "linuxbsd" in Godot 4.0. Building for platform "linuxbsd".')
print('Platform "x11" has been renamed to "linuxbsd" in Godot 4. Building for platform "linuxbsd".')
# Alias for convenience.
selected_platform = "linuxbsd"

@ -65,7 +65,7 @@ thirdparty_misc_sources = [thirdparty_misc_dir + file for file in thirdparty_mis
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_misc_sources)
# Brotli
if env["brotli"]:
if env["brotli"] and env["builtin_brotli"]:
thirdparty_brotli_dir = "#thirdparty/brotli/"
thirdparty_brotli_sources = [
"common/constants.c",

@ -1192,7 +1192,7 @@ static const char *_joy_button_descriptions[(size_t)JoyButton::SDL_MAX] = {
TTRC("Top Action, Sony Triangle, Xbox Y, Nintendo X"),
TTRC("Back, Sony Select, Xbox Back, Nintendo -"),
TTRC("Guide, Sony PS, Xbox Home"),
TTRC("Start, Nintendo +"),
TTRC("Start, Xbox Menu, Nintendo +"),
TTRC("Left Stick, Sony L3, Xbox L/LS"),
TTRC("Right Stick, Sony R3, Xbox R/RS"),
TTRC("Left Shoulder, Sony L1, Xbox LB"),

@ -35,13 +35,13 @@
#include "thirdparty/misc/fastlz.h"
#ifdef BROTLI_ENABLED
#include "thirdparty/brotli/include/brotli/decode.h"
#endif
#include <zlib.h>
#include <zstd.h>
#ifdef BROTLI_ENABLED
#include <brotli/decode.h>
#endif
int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, Mode p_mode) {
switch (p_mode) {
case MODE_BROTLI: {

@ -47,7 +47,7 @@ static void *godot_open(voidpf opaque, const char *p_fname, int mode) {
return nullptr;
}
Ref<FileAccess> f = FileAccess::open(p_fname, FileAccess::READ);
Ref<FileAccess> f = FileAccess::open(String::utf8(p_fname), FileAccess::READ);
ERR_FAIL_COND_V(f.is_null(), nullptr);
ZipData *zd = memnew(ZipData);

@ -836,14 +836,16 @@ void Object::set_script(const Variant &p_script) {
return;
}
Ref<Script> s = p_script;
ERR_FAIL_COND_MSG(s.is_null() && !p_script.is_null(), "Invalid parameter, it should be a reference to a valid script (or null).");
script = p_script;
if (script_instance) {
memdelete(script_instance);
script_instance = nullptr;
}
script = p_script;
Ref<Script> s = script;
if (!s.is_null()) {
if (s->can_instantiate()) {
OBJ_DEBUG_LOCK

@ -416,7 +416,7 @@ Error WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) {
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) {
ERR_FAIL_COND_V(p_elements < 0, INVALID_TASK_ID);
if (p_tasks < 0) {
p_tasks = threads.size();
p_tasks = MAX(1u, threads.size());
}
task_mutex.lock();

@ -295,12 +295,14 @@ Error OS::shell_open(String p_uri) {
}
Error OS::shell_show_in_file_manager(String p_path, bool p_open_folder) {
if (!p_path.begins_with("file://")) {
p_path = String("file://") + p_path;
}
if (!p_path.ends_with("/")) {
p_path = p_path.trim_prefix("file://");
if (!DirAccess::dir_exists_absolute(p_path)) {
p_path = p_path.get_base_dir();
}
p_path = String("file://") + p_path;
return shell_open(p_path);
}
// implement these with the canvas?

@ -4281,12 +4281,13 @@ String String::pad_zeros(int p_digits) const {
begin++;
}
if (begin >= end) {
int zeros_to_add = p_digits - (end - begin);
if (zeros_to_add <= 0) {
return s;
} else {
return s.insert(begin, String("0").repeat(zeros_to_add));
}
int zeros_to_add = p_digits - (end - begin);
return s.insert(begin, String("0").repeat(zeros_to_add));
}
String String::trim_prefix(const String &p_prefix) const {

@ -144,6 +144,18 @@ void CallableCustomBind::call(const Variant **p_arguments, int p_argcount, Varia
callable.callp(args, p_argcount + binds.size(), r_return_value, r_call_error);
}
Error CallableCustomBind::rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const {
const Variant **args = (const Variant **)alloca(sizeof(const Variant **) * (binds.size() + p_argcount));
for (int i = 0; i < p_argcount; i++) {
args[i] = (const Variant *)p_arguments[i];
}
for (int i = 0; i < binds.size(); i++) {
args[i + p_argcount] = (const Variant *)&binds[i];
}
return callable.rpcp(p_peer_id, args, p_argcount + binds.size(), r_call_error);
}
CallableCustomBind::CallableCustomBind(const Callable &p_callable, const Vector<Variant> &p_binds) {
callable = p_callable;
binds = p_binds;
@ -242,6 +254,16 @@ void CallableCustomUnbind::call(const Variant **p_arguments, int p_argcount, Var
callable.callp(p_arguments, p_argcount - argcount, r_return_value, r_call_error);
}
Error CallableCustomUnbind::rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const {
if (argcount > p_argcount) {
r_call_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_call_error.argument = 0;
r_call_error.expected = argcount;
return ERR_UNCONFIGURED;
}
return callable.rpcp(p_peer_id, p_arguments, p_argcount - argcount, r_call_error);
}
CallableCustomUnbind::CallableCustomUnbind(const Callable &p_callable, int p_argcount) {
callable = p_callable;
argcount = p_argcount;

@ -51,6 +51,7 @@ public:
virtual StringName get_method() const override;
virtual ObjectID get_object() const override;
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
virtual Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const override;
virtual const Callable *get_base_comparator() const override;
virtual int get_bound_arguments_count() const override;
virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override;
@ -78,6 +79,7 @@ public:
virtual StringName get_method() const override;
virtual ObjectID get_object() const override;
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
virtual Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const override;
virtual const Callable *get_base_comparator() const override;
virtual int get_bound_arguments_count() const override;
virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override;

@ -4,7 +4,7 @@
Blends two of three animations additively inside of an [AnimationNodeBlendTree].
</brief_description>
<description>
A resource to add to an [AnimationNodeBlendTree]. Blends two animations out of three additively out of three based on the amounmt value.
A resource to add to an [AnimationNodeBlendTree]. Blends two animations out of three additively out of three based on the amount value.
This animation node has three inputs:
- The base animation to add to
- A "-add" animation to blend with when the blend amount is negative

@ -4,7 +4,7 @@
Blends two of three animations linearly inside of an [AnimationNodeBlendTree].
</brief_description>
<description>
A resource to add to an [AnimationNodeBlendTree]. Blends two animations out of three linearly out of three based on the amounmt value.
A resource to add to an [AnimationNodeBlendTree]. Blends two animations out of three linearly out of three based on the amount value.
This animation node has three inputs:
- The base animation to blend with
- A "-blend" animation to blend with when the blend amount is negative value

@ -39,7 +39,7 @@
<return type="void" />
<param index="0" name="use_external_skeleton" type="bool" />
<description>
Sets whether the BoneAttachment3D node will use an extenral [Skeleton3D] node rather than attenpting to use its parent node as the [Skeleton3D]. When set to [code]true[/code], the BoneAttachment3D node will use the external [Skeleton3D] node set in [method set_external_skeleton].
Sets whether the BoneAttachment3D node will use an external [Skeleton3D] node rather than attempting to use its parent node as the [Skeleton3D]. When set to [code]true[/code], the BoneAttachment3D node will use the external [Skeleton3D] node set in [method set_external_skeleton].
</description>
</method>
</methods>

@ -17,6 +17,12 @@
If this is the current camera, remove it from being current. If [param enable_next] is [code]true[/code], request to make the next camera current, if any.
</description>
</method>
<method name="get_camera_projection" qualifiers="const">
<return type="Projection" />
<description>
Returns the projection matrix that this camera uses to render to its associated viewport. The camera must be part of the scene tree to function.
</description>
</method>
<method name="get_camera_rid" qualifiers="const">
<return type="RID" />
<description>

@ -130,7 +130,8 @@
<param index="1" name="file" type="PackedByteArray" />
<param index="2" name="remap" type="bool" />
<description>
Adds a custom file to be exported. [param path] is the virtual path that can be used to load the file, [param file] is the binary data of the file. If [param remap] is [code]true[/code], file will not be exported, but instead remapped to the given [param path].
Adds a custom file to be exported. [param path] is the virtual path that can be used to load the file, [param file] is the binary data of the file.
When called inside [method _export_file] and [param remap] is [code]true[/code], the current file will not be exported, but instead remapped to this custom file. [param remap] is ignored when called in other places.
</description>
</method>
<method name="add_ios_bundle_file">

@ -12,20 +12,20 @@
<method name="get_architecture_name" qualifiers="const">
<return type="String" />
<description>
Returns the name of the CPU architecture the Godot binary was built for. Possible return values are [code]x86_64[/code], [code]x86_32[/code], [code]arm64[/code], [code]armv7[/code], [code]rv64[/code], [code]riscv[/code], [code]ppc64[/code], [code]ppc[/code], [code]wasm64[/code] and [code]wasm32[/code].
Returns the name of the CPU architecture the Godot binary was built for. Possible return values are [code]x86_64[/code], [code]x86_32[/code], [code]arm64[/code], [code]arm32[/code], [code]rv64[/code], [code]riscv[/code], [code]ppc64[/code], [code]ppc[/code], [code]wasm64[/code] and [code]wasm32[/code].
To detect whether the current CPU architecture is 64-bit, you can use the fact that all 64-bit architecture names have [code]64[/code] in their name:
[codeblocks]
[gdscript]
if "64" in Engine.get_architecture_name():
print("Running on 64-bit CPU.")
print("Running a 64-bit build of Godot.")
else:
print("Running on 32-bit CPU.")
print("Running a 32-bit build of Godot.")
[/gdscript]
[csharp]
if (Engine.GetArchitectureName().Contains("64"))
GD.Print("Running on 64-bit CPU.");
GD.Print("Running a 64-bit build of Godot.");
else
GD.Print("Running on 32-bit CPU.");
GD.Print("Running a 32-bit build of Godot.");
[/csharp]
[/codeblocks]
[b]Note:[/b] [method get_architecture_name] does [i]not[/i] return the name of the host CPU architecture. For example, if running an x86_32 Godot binary on a x86_64 system, the returned value will be [code]x86_32[/code].

@ -103,7 +103,7 @@
The maximum distance the detail mesh surface should deviate from heightfield, in cell unit.
</member>
<member name="edge_max_error" type="float" setter="set_edge_max_error" getter="get_edge_max_error" default="1.3">
The maximum distance a simplfied contour's border edges should deviate the original raw contour.
The maximum distance a simplified contour's border edges should deviate the original raw contour.
</member>
<member name="edge_max_length" type="float" setter="set_edge_max_length" getter="get_edge_max_length" default="12.0">
The maximum allowed length for contour edges along the border of the mesh.

@ -47,6 +47,7 @@
<description>
Parses the [SceneTree] for source geometry according to the properties of [param navigation_mesh]. Updates the provided [param source_geometry_data] resource with the resulting data. The resource can then be used to bake a navigation mesh with [method bake_from_source_geometry_data]. After the process is finished the optional [param callback] will be called.
[b]Note:[/b] This function needs to run on the main thread or with a deferred call as the SceneTree is not thread-safe.
[b]Performance:[/b] While convenient, reading data arrays from [Mesh] resources can affect the frame rate negatively. The data needs to be received from the GPU, stalling the [RenderingServer] in the process. For performance prefer the use of e.g. collision shapes or creating the data arrays entirely in code.
</description>
</method>
</methods>

@ -685,6 +685,7 @@
<description>
Parses the [SceneTree] for source geometry according to the properties of [param navigation_mesh]. Updates the provided [param source_geometry_data] resource with the resulting data. The resource can then be used to bake a navigation mesh with [method bake_from_source_geometry_data]. After the process is finished the optional [param callback] will be called.
[b]Note:[/b] This function needs to run on the main thread or with a deferred call as the SceneTree is not thread-safe.
[b]Performance:[/b] While convenient, reading data arrays from [Mesh] resources can affect the frame rate negatively. The data needs to be received from the GPU, stalling the [RenderingServer] in the process. For performance prefer the use of e.g. collision shapes or creating the data arrays entirely in code.
</description>
</method>
<method name="query_path" qualifiers="const">

@ -858,8 +858,8 @@
[b]Note:[/b] Auto-generated names might include the [code]@[/code] character, which is reserved for unique names when using [method add_child]. When setting the name manually, any [code]@[/code] will be removed.
</member>
<member name="owner" type="Node" setter="set_owner" getter="get_owner">
The node owner. A node can have any other node as owner (as long as it is a valid parent, grandparent, etc. ascending in the tree). When saving a node (using [PackedScene]), all the nodes it owns will be saved with it. This allows for the creation of complex [SceneTree]s, with instancing and subinstancing.
[b]Note:[/b] If you want a child to be persisted to a [PackedScene], you must set [member owner] in addition to calling [method add_child]. This is typically relevant for [url=$DOCS_URL/tutorials/plugins/running_code_in_the_editor.html]tool scripts[/url] and [url=$DOCS_URL/tutorials/plugins/editor/index.html]editor plugins[/url]. If [method add_child] is called without setting [member owner], the newly added [Node] will not be visible in the scene tree, though it will be visible in the 2D/3D view.
The node owner. A node can have any ancestor node as owner (i.e. a parent, grandparent, etc. node ascending in the tree). This implies that [method add_child] should be called before setting the owner, so that this relationship of parenting exists. When saving a node (using [PackedScene]), all the nodes it owns will be saved with it. This allows for the creation of complex scene trees, with instancing and subinstancing.
[b]Note:[/b] If you want a child to be persisted to a [PackedScene], you must set [member owner] in addition to calling [method add_child]. This is typically relevant for [url=$DOCS_URL/tutorials/plugins/running_code_in_the_editor.html]tool scripts[/url] and [url=$DOCS_URL/tutorials/plugins/editor/index.html]editor plugins[/url]. If a new node is added to the tree without setting its owner as an ancestor in that tree, it will be visible in the 2D/3D view, but not in the scene tree (and not persisted when packing or saving).
</member>
<member name="process_mode" type="int" setter="set_process_mode" getter="get_process_mode" enum="Node.ProcessMode" default="0">
Can be used to pause or unpause the node, or make the node paused based on the [SceneTree], or make it inherit the process mode from its parent (default).

@ -657,7 +657,7 @@
Requests the OS to open the file manager, then navigate to the given [param file_or_dir_path] and select the target file or folder.
If [param file_or_dir_path] is a valid directory path, and [param open_folder] is [code]true[/code], the method will open the file manager and enter the target folder without selecting anything.
Use [method ProjectSettings.globalize_path] to convert a [code]res://[/code] or [code]user://[/code] path into a system path for use with this method.
[b]Note:[/b] Currently this method is only implemented on Windows. On other platforms, it will fallback to [method shell_open] with a directory path for [param file_or_dir_path].
[b]Note:[/b] Currently this method is only implemented on Windows and macOS. On other platforms, it will fallback to [method shell_open] with a directory path of [param file_or_dir_path] with prefix [code]file://[/code].
</description>
</method>
<method name="unset_environment" qualifiers="const">

@ -36,7 +36,7 @@
</member>
<member name="bake_simplification_distance" type="float" setter="set_bake_simplification_distance" getter="get_bake_simplification_distance" default="0.1">
The simplification distance to use for simplifying the generated occluder polygon (in 3D units). Higher values result in a less detailed occluder mesh, which improves performance but reduces culling accuracy.
The occluder geometry is rendered on the CPU, so it is important to keep its geometry as simple as possible. Since the buffer is rendered at a low resolution, less detailed occluder meshes generally still work well. The default value is fairly aggressive, so you may have to decrase it if you run into false negatives (objects being occluded even though they are visible by the camera). A value of [code]0.01[/code] will act conservatively, and will keep geometry [i]perceptually[/i] unaffected in the occlusion culling buffer. Depending on the scene, a value of [code]0.01[/code] may still simplify the mesh noticeably compared to disabling simplification entirely.
The occluder geometry is rendered on the CPU, so it is important to keep its geometry as simple as possible. Since the buffer is rendered at a low resolution, less detailed occluder meshes generally still work well. The default value is fairly aggressive, so you may have to decrease it if you run into false negatives (objects being occluded even though they are visible by the camera). A value of [code]0.01[/code] will act conservatively, and will keep geometry [i]perceptually[/i] unaffected in the occlusion culling buffer. Depending on the scene, a value of [code]0.01[/code] may still simplify the mesh noticeably compared to disabling simplification entirely.
Setting this to [code]0.0[/code] disables simplification entirely, but vertices in the exact same position will still be merged. The mesh will also be re-indexed to reduce both the number of vertices and indices.
[b]Note:[/b] This uses the [url=https://meshoptimizer.org/]meshoptimizer[/url] library under the hood, similar to LOD generation.
</member>

@ -32,7 +32,7 @@
The index of the [Bone2D] that this [PhysicalBone2D] should simulate.
</member>
<member name="bone2d_nodepath" type="NodePath" setter="set_bone2d_nodepath" getter="get_bone2d_nodepath" default="NodePath(&quot;&quot;)">
The [NodePath] to the [Bone2D] that this [PhysicalBone2D] isshould simulate.
The [NodePath] to the [Bone2D] that this [PhysicalBone2D] should simulate.
</member>
<member name="follow_bone_when_simulating" type="bool" setter="set_follow_bone_when_simulating" getter="get_follow_bone_when_simulating" default="false">
If [code]true[/code], the [PhysicalBone2D] will keep the transform of the bone it is bound to when simulating physics.

@ -35,6 +35,12 @@
<param index="0" name="path" type="String" />
<description>
Returns the dependencies for the resource at the given [param path].
[b]Note:[/b] The dependencies are returned with slices separated by [code]::[/code]. You can use [method String.get_slice] to get their components.
[codeblock]
for dep in ResourceLoader.get_dependencies(path):
print(dep.get_slice("::", 0)) # Prints UID.
print(dep.get_slice("::", 2)) # Prints path.
[/codeblock]
</description>
</method>
<method name="get_recognized_extensions_for_type">

@ -151,7 +151,7 @@
See [method add_constant_torque].
</member>
<member name="contact_monitor" type="bool" setter="set_contact_monitor" getter="is_contact_monitor_enabled" default="false">
If [code]true[/code], the RigidBody2D will emit signals when it collides with another RigidBody2D.
If [code]true[/code], the RigidBody2D will emit signals when it collides with another body.
[b]Note:[/b] By default the maximum contacts reported is set to 0, meaning nothing will be recorded, see [member max_contacts_reported].
</member>
<member name="continuous_cd" type="int" setter="set_continuous_collision_detection_mode" getter="get_continuous_collision_detection_mode" enum="RigidBody2D.CCDMode" default="0">

@ -158,7 +158,7 @@
See [method add_constant_torque].
</member>
<member name="contact_monitor" type="bool" setter="set_contact_monitor" getter="is_contact_monitor_enabled" default="false">
If [code]true[/code], the RigidBody3D will emit signals when it collides with another RigidBody3D.
If [code]true[/code], the RigidBody3D will emit signals when it collides with another body.
[b]Note:[/b] By default the maximum contacts reported is set to 0, meaning nothing will be recorded, see [member max_contacts_reported].
</member>
<member name="continuous_cd" type="bool" setter="set_use_continuous_collision_detection" getter="is_using_continuous_collision_detection" default="false">

@ -920,11 +920,11 @@
<description>
Converts the string representing a decimal number into a [float]. This method stops on the first non-number character, except the first decimal point ([code].[/code]) and the exponent letter ([code]e[/code]). See also [method is_valid_float].
[codeblock]
var a = "12.35".to_float() # a is 12.35
var b = "1.2.3".to_float() # b is 1.2
var c = "12xy3".to_float() # c is 12.0
var d = "1e3".to_float() # d is 1000.0
var e = "Hello!".to_int() # e is 0.0
var a = "12.35".to_float() # a is 12.35
var b = "1.2.3".to_float() # b is 1.2
var c = "12xy3".to_float() # c is 12.0
var d = "1e3".to_float() # d is 1000.0
var e = "Hello!".to_float() # e is 0.0
[/codeblock]
</description>
</method>

@ -29,15 +29,15 @@
<description>
Creates a TLS client configuration which validates certificates and their common names (fully qualified domain names).
You can specify a custom [param trusted_chain] of certification authorities (the default CA list will be used if [code]null[/code]), and optionally provide a [param common_name_override] if you expect the certificate to have a common name other then the server FQDN.
Note: On the Web plafrom, TLS verification is always enforced against the CA list of the web browser. This is considered a security feature.
[b]Note:[/b] On the Web platform, TLS verification is always enforced against the CA list of the web browser. This is considered a security feature.
</description>
</method>
<method name="client_unsafe" qualifiers="static">
<return type="TLSOptions" />
<param index="0" name="trusted_chain" type="X509Certificate" default="null" />
<description>
Creates an [b]unsafe[/b] TLS client configuration where certificate validation is optional. You can optionally provide a valid [param trusted_chain], but the common name of the certififcates will never be checked. Using this configuration for purposes other than testing [b]is not recommended[/b].
Note: On the Web plafrom, TLS verification is always enforced against the CA list of the web browser. This is considered a security feature.
Creates an [b]unsafe[/b] TLS client configuration where certificate validation is optional. You can optionally provide a valid [param trusted_chain], but the common name of the certificates will never be checked. Using this configuration for purposes other than testing [b]is not recommended[/b].
[b]Note:[/b] On the Web platform, TLS verification is always enforced against the CA list of the web browser. This is considered a security feature.
</description>
</method>
<method name="server" qualifiers="static">
@ -46,7 +46,7 @@
<param index="1" name="certificate" type="X509Certificate" />
<description>
Creates a TLS server configuration using the provided [param key] and [param certificate].
Note: The [param certificate] should include the full certificate chain up to the signing CA (certificates file can be concatenated using a general purpose text editor).
[b]Note:[/b] The [param certificate] should include the full certificate chain up to the signing CA (certificates file can be concatenated using a general purpose text editor).
</description>
</method>
</methods>

@ -820,12 +820,11 @@ void ParticlesStorage::_particles_update_buffers(Particles *particles) {
particles->num_attrib_arrays_cache = 5 + userdata_count + (xform_size - 2);
particles->process_buffer_stride_cache = sizeof(float) * 4 * particles->num_attrib_arrays_cache;
int process_data_amount = 4 * particles->num_attrib_arrays_cache * total_amount;
float *data = memnew_arr(float, process_data_amount);
PackedByteArray data;
data.resize_zeroed(particles->process_buffer_stride_cache * total_amount);
for (int i = 0; i < process_data_amount; i++) {
data[i] = 0;
}
PackedByteArray instance_data;
instance_data.resize_zeroed(particles->instance_buffer_size_cache);
{
glGenVertexArrays(1, &particles->front_vertex_array);
@ -834,7 +833,7 @@ void ParticlesStorage::_particles_update_buffers(Particles *particles) {
glGenBuffers(1, &particles->front_instance_buffer);
glBindBuffer(GL_ARRAY_BUFFER, particles->front_process_buffer);
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, particles->front_process_buffer, particles->process_buffer_stride_cache * total_amount, data, GL_DYNAMIC_COPY, "Particles front process buffer");
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, particles->front_process_buffer, particles->process_buffer_stride_cache * total_amount, data.ptr(), GL_DYNAMIC_COPY, "Particles front process buffer");
for (uint32_t j = 0; j < particles->num_attrib_arrays_cache; j++) {
glEnableVertexAttribArray(j);
@ -843,7 +842,7 @@ void ParticlesStorage::_particles_update_buffers(Particles *particles) {
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, particles->front_instance_buffer);
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, particles->front_instance_buffer, particles->instance_buffer_size_cache, nullptr, GL_DYNAMIC_COPY, "Particles front instance buffer");
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, particles->front_instance_buffer, particles->instance_buffer_size_cache, instance_data.ptr(), GL_DYNAMIC_COPY, "Particles front instance buffer");
}
{
@ -853,7 +852,7 @@ void ParticlesStorage::_particles_update_buffers(Particles *particles) {
glGenBuffers(1, &particles->back_instance_buffer);
glBindBuffer(GL_ARRAY_BUFFER, particles->back_process_buffer);
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, particles->back_process_buffer, particles->process_buffer_stride_cache * total_amount, data, GL_DYNAMIC_COPY, "Particles back process buffer");
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, particles->back_process_buffer, particles->process_buffer_stride_cache * total_amount, data.ptr(), GL_DYNAMIC_COPY, "Particles back process buffer");
for (uint32_t j = 0; j < particles->num_attrib_arrays_cache; j++) {
glEnableVertexAttribArray(j);
@ -862,11 +861,9 @@ void ParticlesStorage::_particles_update_buffers(Particles *particles) {
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, particles->back_instance_buffer);
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, particles->back_instance_buffer, particles->instance_buffer_size_cache, nullptr, GL_DYNAMIC_COPY, "Particles back instance buffer");
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, particles->back_instance_buffer, particles->instance_buffer_size_cache, instance_data.ptr(), GL_DYNAMIC_COPY, "Particles back instance buffer");
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
memdelete_arr(data);
}
}

@ -315,7 +315,7 @@ Dictionary OS_Unix::get_memory_info() const {
mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
vm_statistics64_data_t vmstat;
if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t)&vmstat, &count) != KERN_SUCCESS) {
ERR_PRINT(vformat("Could not get host vm statistics."));
ERR_PRINT("Could not get host vm statistics.");
}
struct xsw_usage swap_used;
len = sizeof(swap_used);
@ -647,7 +647,7 @@ Error OS_Unix::open_dynamic_library(const String p_path, void *&p_library_handle
}
p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ". Error: " + dlerror());
ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror()));
if (r_resolved_path != nullptr) {
*r_resolved_path = path;

@ -7470,6 +7470,7 @@ uint32_t RenderingDeviceVulkan::draw_list_get_current_pass() {
}
RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_switch_to_next_pass() {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(draw_list == nullptr, INVALID_ID);
ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, INVALID_FORMAT_ID);
@ -7485,6 +7486,7 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_switch_to_next_pass
return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT;
}
Error RenderingDeviceVulkan::draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(draw_list == nullptr, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, ERR_INVALID_PARAMETER);

@ -1818,7 +1818,7 @@ Error VulkanContext::_update_swap_chain(Window *window) {
if (window->width == 0 || window->height == 0) {
free(presentModes);
// Likely window minimized, no swapchain created.
return OK;
return ERR_SKIP;
}
// The FIFO present mode is guaranteed by the spec to be supported
// and to have no tearing. It's a great default present mode to use.
@ -2275,8 +2275,10 @@ Error VulkanContext::prepare_buffers() {
// Swapchain is not as optimal as it could be, but the platform's
// presentation engine will still present the image correctly.
print_verbose("Vulkan: Early suboptimal swapchain, recreating.");
_update_swap_chain(w);
break;
Error swap_chain_err = _update_swap_chain(w);
if (swap_chain_err == ERR_SKIP) {
break;
}
} else if (err != VK_SUCCESS) {
ERR_BREAK_MSG(err != VK_SUCCESS, "Vulkan: Did not create swapchain successfully. Error code: " + String(string_VkResult(err)));
} else {

@ -126,7 +126,7 @@ void FindReplaceBar::unhandled_input(const Ref<InputEvent> &p_event) {
if (k.is_valid() && k->is_action_pressed(SNAME("ui_cancel"), false, true)) {
Control *focus_owner = get_viewport()->gui_get_focus_owner();
if (text_editor->has_focus() || (focus_owner && vbc_lineedit->is_ancestor_of(focus_owner))) {
if (text_editor->has_focus() || (focus_owner && is_ancestor_of(focus_owner))) {
_hide_bar();
accept_event();
}
@ -410,7 +410,7 @@ void FindReplaceBar::_update_matches_label() {
matches_label->add_theme_color_override("font_color", results_count > 0 ? get_theme_color(SNAME("font_color"), SNAME("Label")) : get_theme_color(SNAME("error_color"), SNAME("Editor")));
if (results_count == 0) {
matches_label->set_text("No match");
matches_label->set_text(TTR("No match"));
} else if (results_count_to_current == -1) {
matches_label->set_text(vformat(TTRN("%d match", "%d matches", results_count), results_count));
} else {

@ -39,6 +39,7 @@
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/scene_tree_editor.h"
#include "editor/node_dock.h"
#include "editor/scene_tree_dock.h"
#include "plugins/script_editor_plugin.h"
#include "scene/gui/button.h"
@ -1440,6 +1441,7 @@ ConnectionsDock::ConnectionsDock() {
connect_button->connect("pressed", callable_mp(this, &ConnectionsDock::_connect_pressed));
connect_dialog = memnew(ConnectDialog);
connect_dialog->connect("connected", callable_mp(NodeDock::get_singleton(), &NodeDock::restore_last_valid_node), CONNECT_DEFERRED);
add_child(connect_dialog);
disconnect_all_dialog = memnew(ConfirmationDialog);

@ -1635,6 +1635,7 @@ void EditorFileSystem::_queue_update_script_class(const String &p_path) {
}
void EditorFileSystem::update_file(const String &p_file) {
ERR_FAIL_COND(p_file.is_empty());
EditorFileSystemDirectory *fs = nullptr;
int cpos = -1;

@ -1367,7 +1367,7 @@ void EditorHelp::_update_doc() {
class_desc->add_newline();
// Enum description.
if (e != "@unnamed_enums" && cd.enums.has(e)) {
if (e != "@unnamed_enums" && cd.enums.has(e) && !cd.enums[e].strip_edges().is_empty()) {
class_desc->push_color(theme_cache.text_color);
_push_normal_font();
class_desc->push_indent(1);

@ -3184,10 +3184,11 @@ void EditorInspector::update_tree() {
if (val.enumeration == enum_name && !val.name.ends_with("_MAX")) {
const String enum_value = EditorPropertyNameProcessor::get_singleton()->process_name(val.name, EditorPropertyNameProcessor::STYLE_CAPITALIZED);
// Prettify the enum value display, so that "<ENUM NAME>_<VALUE>" becomes "Value".
String desc = DTR(val.description).trim_prefix("\n");
doc_info.description += vformat(
"\n[b]%s:[/b] %s",
enum_value.trim_prefix(EditorPropertyNameProcessor::get_singleton()->process_name(enum_name, EditorPropertyNameProcessor::STYLE_CAPITALIZED) + " "),
DTR(val.description).trim_prefix("\n"));
desc.is_empty() ? ("[i]" + TTR("No description.") + "[/i]") : desc);
}
}
}

@ -1411,7 +1411,7 @@ void EditorNode::_dialog_display_load_error(String p_file, Error p_error) {
show_accept(vformat(TTR("Scene file '%s' appears to be invalid/corrupt."), p_file.get_file()), TTR("OK"));
} break;
case ERR_FILE_NOT_FOUND: {
show_accept(vformat(TTR("Missing file '%s' or one its dependencies."), p_file.get_file()), TTR("OK"));
show_accept(vformat(TTR("Missing file '%s' or one of its dependencies."), p_file.get_file()), TTR("OK"));
} break;
default: {
show_accept(vformat(TTR("Error while loading file '%s'."), p_file.get_file()), TTR("OK"));
@ -5275,21 +5275,29 @@ void EditorNode::_save_central_editor_layout_to_config(Ref<ConfigFile> p_config_
void EditorNode::_load_central_editor_layout_from_config(Ref<ConfigFile> p_config_file) {
// Bottom panel.
if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "center_split_offset")) {
int center_split_offset = p_config_file->get_value(EDITOR_NODE_CONFIG_SECTION, "center_split_offset");
center_split->set_split_offset(center_split_offset);
}
bool has_active_tab = false;
if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "selected_bottom_panel_item")) {
int selected_bottom_panel_item_idx = p_config_file->get_value(EDITOR_NODE_CONFIG_SECTION, "selected_bottom_panel_item");
if (selected_bottom_panel_item_idx >= 0 && selected_bottom_panel_item_idx < bottom_panel_items.size()) {
// Make sure we don't try to open contextual editors which are not enabled in the current context.
if (bottom_panel_items[selected_bottom_panel_item_idx].button->is_visible()) {
_bottom_panel_switch(true, selected_bottom_panel_item_idx);
has_active_tab = true;
}
}
}
if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "center_split_offset")) {
int center_split_offset = p_config_file->get_value(EDITOR_NODE_CONFIG_SECTION, "center_split_offset");
center_split->set_split_offset(center_split_offset);
// If there is no active tab we need to collapse the panel.
if (!has_active_tab) {
bottom_panel_items[0].control->show(); // _bottom_panel_switch() can collapse only visible tabs.
_bottom_panel_switch(false, 0);
}
}
// Debugger tab.
if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "selected_default_debugger_tab_idx")) {

@ -101,9 +101,18 @@ void EditorPluginSettings::update_plugins() {
String description = cf->get_value("plugin", "description");
String scr = cf->get_value("plugin", "script");
const PackedInt32Array boundaries = TS->string_get_word_breaks(description, "", 80);
String wrapped_description;
for (int j = 0; j < boundaries.size(); j += 2) {
const int start = boundaries[j];
const int end = boundaries[j + 1];
wrapped_description += "\n" + description.substr(start, end - start + 1).rstrip("\n");
}
TreeItem *item = plugin_list->create_item(root);
item->set_text(0, name);
item->set_tooltip_text(0, TTR("Name:") + " " + name + "\n" + TTR("Path:") + " " + path + "\n" + TTR("Main Script:") + " " + scr + "\n" + TTR("Description:") + " " + description);
item->set_tooltip_text(0, TTR("Name:") + " " + name + "\n" + TTR("Path:") + " " + path + "\n" + TTR("Main Script:") + " " + scr + "\n" + TTR("Description:") + " " + wrapped_description);
item->set_metadata(0, path);
item->set_text(1, version);
item->set_metadata(1, scr);

@ -621,7 +621,7 @@ void EditorPropertyClassName::update_property() {
}
void EditorPropertyClassName::_property_selected() {
dialog->popup_create(true);
dialog->popup_create(true, true, get_edited_property_value(), get_edited_property());
}
void EditorPropertyClassName::_dialog_created() {
@ -3622,7 +3622,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
return editor;
} else if (p_hint == PROPERTY_HINT_TYPE_STRING) {
EditorPropertyClassName *editor = memnew(EditorPropertyClassName);
editor->setup("Object", p_hint_text);
editor->setup(p_hint_text, p_hint_text);
return editor;
} else if (p_hint == PROPERTY_HINT_LOCALE_ID) {
EditorPropertyLocale *editor = memnew(EditorPropertyLocale);

@ -264,6 +264,7 @@ void EditorUndoRedoManager::commit_action(bool p_execute) {
pending_action.action_name == prev_action.action_name && pending_action.action_name == pre_prev_action.action_name) {
pending_action = Action();
is_committing = false;
emit_signal(SNAME("history_changed"));
return;
}
} break;
@ -272,6 +273,7 @@ void EditorUndoRedoManager::commit_action(bool p_execute) {
if (pending_action.merge_mode == prev_action.merge_mode && pending_action.action_name == prev_action.action_name) {
pending_action = Action();
is_committing = false;
emit_signal(SNAME("history_changed"));
return;
}
} break;

@ -90,11 +90,12 @@ void EditorExport::_save() {
String option_section = "preset." + itos(i) + ".options";
for (const PropertyInfo &E : preset->get_properties()) {
if (E.usage & PROPERTY_USAGE_SECRET) {
credentials->set_value(option_section, E.name, preset->get(E.name));
for (const KeyValue<StringName, Variant> &E : preset->values) {
PropertyInfo *prop = preset->properties.getptr(E.key);
if (prop && prop->usage & PROPERTY_USAGE_SECRET) {
credentials->set_value(option_section, E.key, E.value);
} else {
config->set_value(option_section, E.name, preset->get(E.name));
config->set_value(option_section, E.key, E.value);
}
}
}
@ -116,6 +117,7 @@ void EditorExport::_bind_methods() {
void EditorExport::add_export_platform(const Ref<EditorExportPlatform> &p_platform) {
export_platforms.push_back(p_platform);
should_update_presets = true;
}
int EditorExport::get_export_platform_count() {
@ -169,11 +171,13 @@ void EditorExport::remove_export_preset(int p_idx) {
void EditorExport::add_export_plugin(const Ref<EditorExportPlugin> &p_plugin) {
if (!export_plugins.has(p_plugin)) {
export_plugins.push_back(p_plugin);
should_update_presets = true;
}
}
void EditorExport::remove_export_plugin(const Ref<EditorExportPlugin> &p_plugin) {
export_plugins.erase(p_plugin);
should_update_presets = true;
}
Vector<Ref<EditorExportPlugin>> EditorExport::get_export_plugins() {
@ -314,8 +318,11 @@ void EditorExport::load_config() {
credentials->get_section_keys(option_section, &options);
for (const String &E : options) {
Variant value = credentials->get_value(option_section, E);
preset->set(E, value);
// Drop values for secret properties that no longer exist, or during the next save they would end up in the regular config file.
if (preset->get_properties().has(E)) {
Variant value = credentials->get_value(option_section, E);
preset->set(E, value);
}
}
}
@ -332,7 +339,8 @@ void EditorExport::update_export_presets() {
for (int i = 0; i < export_platforms.size(); i++) {
Ref<EditorExportPlatform> platform = export_platforms[i];
bool should_update = platform->should_update_export_options();
bool should_update = should_update_presets;
should_update |= platform->should_update_export_options();
for (int j = 0; j < export_plugins.size(); j++) {
should_update |= export_plugins.write[j]->_should_update_export_options(platform);
}
@ -342,12 +350,13 @@ void EditorExport::update_export_presets() {
platform->get_export_options(&options);
for (int j = 0; j < export_plugins.size(); j++) {
export_plugins.write[j]->_get_export_options(platform, &options);
export_plugins[j]->_get_export_options(platform, &options);
}
platform_options[platform->get_name()] = options;
}
}
should_update_presets = false;
bool export_presets_updated = false;
for (int i = 0; i < export_presets.size(); i++) {
@ -357,19 +366,16 @@ void EditorExport::update_export_presets() {
List<EditorExportPlatform::ExportOption> options = platform_options[preset->get_platform()->get_name()];
// Copy the previous preset values
HashMap<StringName, Variant> previous_values = preset->values;
// Clear the preset properties and values prior to reloading
// Clear the preset properties prior to reloading, keep the values to preserve options from plugins that may be currently disabled.
preset->properties.clear();
preset->values.clear();
preset->update_visibility.clear();
for (const EditorExportPlatform::ExportOption &E : options) {
preset->properties.push_back(E.option);
StringName option_name = E.option.name;
preset->values[option_name] = previous_values.has(option_name) ? previous_values[option_name] : E.default_value;
preset->properties[option_name] = E.option;
if (!preset->has(option_name)) {
preset->values[option_name] = E.default_value;
}
preset->update_visibility[option_name] = E.update_visibility;
}
}

@ -45,6 +45,7 @@ class EditorExport : public Node {
Timer *save_timer = nullptr;
bool block_save = false;
bool should_update_presets = false;
static EditorExport *singleton;

@ -329,9 +329,10 @@ Ref<EditorExportPreset> EditorExportPlatform::create_preset() {
}
for (const ExportOption &E : options) {
preset->properties.push_back(E.option);
preset->values[E.option.name] = E.default_value;
preset->update_visibility[E.option.name] = E.update_visibility;
StringName option_name = E.option.name;
preset->properties[option_name] = E.option;
preset->values[option_name] = E.default_value;
preset->update_visibility[option_name] = E.update_visibility;
}
return preset;

@ -31,9 +31,9 @@
#include "editor_export.h"
bool EditorExportPreset::_set(const StringName &p_name, const Variant &p_value) {
if (values.has(p_name)) {
values[p_name] = p_value;
EditorExport::singleton->save_presets();
values[p_name] = p_value;
EditorExport::singleton->save_presets();
if (update_visibility.has(p_name)) {
if (update_visibility[p_name]) {
notify_property_list_changed();
}
@ -61,9 +61,9 @@ String EditorExportPreset::_get_property_warning(const StringName &p_name) const
}
void EditorExportPreset::_get_property_list(List<PropertyInfo> *p_list) const {
for (const PropertyInfo &E : properties) {
if (platform->get_export_option_visibility(this, E.name)) {
p_list->push_back(E);
for (const KeyValue<StringName, PropertyInfo> &E : properties) {
if (platform->get_export_option_visibility(this, E.key)) {
p_list->push_back(E.value);
}
}
}

@ -70,7 +70,7 @@ private:
friend class EditorExport;
friend class EditorExportPlatform;
List<PropertyInfo> properties;
HashMap<StringName, PropertyInfo> properties;
HashMap<StringName, Variant> values;
HashMap<StringName, bool> update_visibility;
@ -154,7 +154,8 @@ public:
Variant get_or_env(const StringName &p_name, const String &p_env_var, bool *r_valid = nullptr) const;
const List<PropertyInfo> &get_properties() const { return properties; }
const HashMap<StringName, PropertyInfo> &get_properties() const { return properties; }
const HashMap<StringName, Variant> &get_values() const { return values; }
EditorExportPreset();
};

@ -577,8 +577,8 @@ void ProjectExportDialog::_duplicate_preset() {
preset->set_exclude_filter(current->get_exclude_filter());
preset->set_custom_features(current->get_custom_features());
for (const PropertyInfo &E : current->get_properties()) {
preset->set(E.name, current->get(E.name));
for (const KeyValue<StringName, Variant> &E : current->get_values()) {
preset->set(E.key, E.value);
}
EditorExport::get_singleton()->add_export_preset(preset);

@ -2617,8 +2617,10 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data,
if (!to_dir.is_empty()) {
Vector<String> fnames = drag_data["files"];
to_move.clear();
String target_dir = to_dir == "res://" ? to_dir : to_dir.trim_suffix("/");
for (int i = 0; i < fnames.size(); i++) {
if (fnames[i].trim_suffix("/").get_base_dir() != to_dir.trim_suffix("/")) {
if (fnames[i].trim_suffix("/").get_base_dir() != target_dir) {
to_move.push_back(FileOrFolder(fnames[i], !fnames[i].ends_with("/")));
}
}

@ -374,7 +374,14 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
tooltip += String("\n" + TTR("Type:") + " " + (custom_type != StringName() ? String(custom_type) : p_node->get_class()));
if (!p_node->get_editor_description().is_empty()) {
tooltip += "\n\n" + p_node->get_editor_description();
const PackedInt32Array boundaries = TS->string_get_word_breaks(p_node->get_editor_description(), "", 80);
tooltip += "\n";
for (int i = 0; i < boundaries.size(); i += 2) {
const int start = boundaries[i];
const int end = boundaries[i + 1];
tooltip += "\n" + p_node->get_editor_description().substr(start, end - start + 1).rstrip("\n");
}
}
item->set_tooltip_text(0, tooltip);
@ -1019,11 +1026,18 @@ void SceneTreeEditor::_renamed() {
}
}
if (n->is_unique_name_in_owner() && get_tree()->get_edited_scene_root()->get_node_or_null("%" + new_name) != nullptr) {
error->set_text(TTR("Another node already uses this unique name in the scene."));
error->popup_centered();
which->set_text(0, n->get_name());
return;
if (n->is_unique_name_in_owner()) {
Node *existing = get_tree()->get_edited_scene_root()->get_node_or_null("%" + new_name);
if (existing == n) {
which->set_text(0, n->get_name());
return;
}
if (existing != nullptr) {
error->set_text(TTR("Another node already uses this unique name in the scene."));
error->popup_centered();
which->set_text(0, n->get_name());
return;
}
}
_rename_node(n, new_name);
@ -1100,9 +1114,15 @@ void SceneTreeEditor::_update_selection(TreeItem *item) {
}
if (editor_selection->is_selected(n)) {
item->select(0);
if (!item->is_selected(0)) {
item->select(0);
}
} else {
item->deselect(0);
if (item->is_selected(0)) {
TreeItem *previous_cursor_item = tree->get_selected();
item->deselect(0);
previous_cursor_item->set_as_cursor(0);
}
}
TreeItem *c = item->get_first_child();
@ -1188,8 +1208,11 @@ Variant SceneTreeEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from
if (i < list_max) {
HBoxContainer *hb = memnew(HBoxContainer);
TextureRect *tf = memnew(TextureRect);
int icon_size = get_theme_constant(SNAME("class_icon_size"), SNAME("Editor"));
tf->set_custom_minimum_size(Size2(icon_size, icon_size));
tf->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
tf->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
tf->set_texture(icons[i]);
tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
hb->add_child(tf);
Label *label = memnew(Label(selected_nodes[i]->get_name()));
hb->add_child(label);

@ -70,6 +70,9 @@ void NodeDock::update_lists() {
void NodeDock::set_node(Node *p_node) {
connections->set_node(p_node);
groups->set_current(p_node);
if (p_node) {
last_valid_node = p_node;
}
if (p_node) {
if (connections_button->is_pressed()) {
@ -88,6 +91,10 @@ void NodeDock::set_node(Node *p_node) {
}
}
void NodeDock::restore_last_valid_node() {
set_node(last_valid_node);
}
NodeDock::NodeDock() {
singleton = this;

@ -47,6 +47,7 @@ class NodeDock : public VBoxContainer {
HBoxContainer *mode_hb = nullptr;
Label *select_a_node = nullptr;
Node *last_valid_node = nullptr;
private:
static NodeDock *singleton;
@ -60,6 +61,7 @@ protected:
public:
void set_node(Node *p_node);
void restore_last_valid_node();
void show_groups();
void show_connections();

@ -7925,6 +7925,8 @@ void Node3DEditor::clear() {
}
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_GRID), true);
grid_enabled = true;
grid_init_draw = false;
}
void Node3DEditor::_sun_direction_draw() {

@ -238,7 +238,7 @@ void ShaderTextEditor::_load_theme_settings() {
ShaderPreprocessor::get_keyword_list(&pp_keywords, false);
for (const String &E : pp_keywords) {
syntax_highlighter->add_keyword_color(E, keyword_color);
syntax_highlighter->add_keyword_color(E, control_flow_keyword_color);
}
// Colorize built-ins like `COLOR` differently to make them easier
@ -646,13 +646,13 @@ void TextShaderEditor::_menu_option(int p_option) {
shader_editor->move_lines_down();
} break;
case EDIT_INDENT: {
if (shader.is_null()) {
if (shader.is_null() && shader_inc.is_null()) {
return;
}
shader_editor->get_text_editor()->indent_lines();
} break;
case EDIT_UNINDENT: {
if (shader.is_null()) {
if (shader.is_null() && shader_inc.is_null()) {
return;
}
shader_editor->get_text_editor()->unindent_lines();
@ -668,12 +668,10 @@ void TextShaderEditor::_menu_option(int p_option) {
shader_editor->get_text_editor()->set_line_wrapping_mode(wrap == TextEdit::LINE_WRAPPING_BOUNDARY ? TextEdit::LINE_WRAPPING_NONE : TextEdit::LINE_WRAPPING_BOUNDARY);
} break;
case EDIT_TOGGLE_COMMENT: {
if (shader.is_null()) {
if (shader.is_null() && shader_inc.is_null()) {
return;
}
shader_editor->toggle_inline_comment("//");
} break;
case EDIT_COMPLETE: {
shader_editor->get_text_editor()->request_code_completion();

@ -1185,19 +1185,18 @@ void TileDataDefaultEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2
Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold"), SNAME("EditorFonts"));
int font_size = TileSetEditor::get_singleton()->get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts"));
String text;
// Round floating point precision to 2 digits, as tiles don't have that much space.
switch (value.get_type()) {
case Variant::INT:
text = vformat("%d", value);
break;
case Variant::FLOAT:
text = vformat("%.2f", value);
break;
case Variant::STRING:
case Variant::STRING_NAME:
text = value;
case Variant::VECTOR2:
case Variant::VECTOR3:
case Variant::VECTOR4:
text = vformat("%.2v", value);
break;
default:
return;
text = value.stringify();
break;
}
@ -1216,8 +1215,8 @@ void TileDataDefaultEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2
}
Vector2 string_size = font->get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size);
p_canvas_item->draw_string_outline(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, 1, Color(0, 0, 0, 1));
p_canvas_item->draw_string(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color);
p_canvas_item->draw_string_outline(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 4), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, 3, Color(0, 0, 0));
p_canvas_item->draw_string(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 4), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color);
}
}

@ -1693,7 +1693,7 @@ void TileSetAtlasSourceEditor::_menu_option(int p_option) {
}
}
void TileSetAtlasSourceEditor::_unhandled_key_input(const Ref<InputEvent> &p_event) {
void TileSetAtlasSourceEditor::shortcut_input(const Ref<InputEvent> &p_event) {
// Check for shortcuts.
if (ED_IS_SHORTCUT("tiles_editor/delete_tile", p_event)) {
if (tools_button_group->get_pressed_button() == tool_select_button && !selection.is_empty()) {
@ -2418,14 +2418,14 @@ void TileSetAtlasSourceEditor::_notification(int p_what) {
}
void TileSetAtlasSourceEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &TileSetAtlasSourceEditor::_unhandled_key_input);
ClassDB::bind_method(D_METHOD("_set_selection_from_array"), &TileSetAtlasSourceEditor::_set_selection_from_array);
ADD_SIGNAL(MethodInfo("source_id_changed", PropertyInfo(Variant::INT, "source_id")));
}
TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
set_process_unhandled_key_input(true);
set_shortcut_context(this);
set_process_shortcut_input(true);
set_process_internal(true);
// Middle panel.

@ -257,9 +257,6 @@ private:
void _update_atlas_view();
void _update_toolbar();
// -- input events --
void _unhandled_key_input(const Ref<InputEvent> &p_event);
// -- Misc --
void _auto_create_tiles();
void _auto_remove_tiles();
@ -275,6 +272,9 @@ protected:
void _notification(int p_what);
static void _bind_methods();
// -- input events --
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
public:
void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_source, int p_source_id);
void init_source();

@ -336,7 +336,7 @@ bool ProjectConverter3To4::convert() {
// Checking if folder contains valid Godot 3 project.
// Project should not be converted more than once.
{
String converter_text = "; Project was converted by built-in tool to Godot 4.0";
String converter_text = "; Project was converted by built-in tool to Godot 4";
ERR_FAIL_COND_V_MSG(!FileAccess::exists("project.godot"), false, "Current working directory doesn't contain a \"project.godot\" file for a Godot 3 project.");
@ -536,7 +536,7 @@ bool ProjectConverter3To4::validate_conversion() {
// Checking if folder contains valid Godot 3 project.
// Project should not be converted more than once.
{
String conventer_text = "; Project was converted by built-in tool to Godot 4.0";
String conventer_text = "; Project was converted by built-in tool to Godot 4";
ERR_FAIL_COND_V_MSG(!FileAccess::exists("project.godot"), false, "Current directory doesn't contain any Godot 3 project");
@ -857,16 +857,16 @@ bool ProjectConverter3To4::test_conversion(RegExContainer &reg_container) {
valid = valid && test_conversion_gdscript_builtin("\tif OS.get_borderless_window(): pass", "\tif get_window().borderless: pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\tOS.set_borderless_window(Settings.borderless)", "\tget_window().borderless = (Settings.borderless)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\tvar aa = roman(r.move_and_slide( a, b, c, d, e, f )) # Roman", "\tr.set_velocity(a)\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tr.move_and_slide()\n\tvar aa = roman(r.velocity) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\tmove_and_slide( a, b, c, d, e, f ) # Roman", "\tset_velocity(a)\n\tset_up_direction(b)\n\tset_floor_stop_on_slope_enabled(c)\n\tset_max_slides(d)\n\tset_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tmove_and_slide() # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\tvar aa = roman(r.move_and_slide_with_snap( a, g, b, c, d, e, f )) # Roman", "\tr.set_velocity(a)\n\t# TODOConverter40 looks that snap in Godot 4.0 is float, not vector like in Godot 3 - previous value `g`\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tr.move_and_slide()\n\tvar aa = roman(r.velocity) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\tmove_and_slide_with_snap( a, g, b, c, d, e, f ) # Roman", "\tset_velocity(a)\n\t# TODOConverter40 looks that snap in Godot 4.0 is float, not vector like in Godot 3 - previous value `g`\n\tset_up_direction(b)\n\tset_floor_stop_on_slope_enabled(c)\n\tset_max_slides(d)\n\tset_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tmove_and_slide() # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\tvar aa = roman(r.move_and_slide( a, b, c, d, e, f )) # Roman", "\tr.set_velocity(a)\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `f`\n\tr.move_and_slide()\n\tvar aa = roman(r.velocity) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\tmove_and_slide( a, b, c, d, e, f ) # Roman", "\tset_velocity(a)\n\tset_up_direction(b)\n\tset_floor_stop_on_slope_enabled(c)\n\tset_max_slides(d)\n\tset_floor_max_angle(e)\n\t# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `f`\n\tmove_and_slide() # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\tvar aa = roman(r.move_and_slide_with_snap( a, g, b, c, d, e, f )) # Roman", "\tr.set_velocity(a)\n\t# TODOConverter3To4 looks that snap in Godot 4 is float, not vector like in Godot 3 - previous value `g`\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `f`\n\tr.move_and_slide()\n\tvar aa = roman(r.velocity) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\tmove_and_slide_with_snap( a, g, b, c, d, e, f ) # Roman", "\tset_velocity(a)\n\t# TODOConverter3To4 looks that snap in Godot 4 is float, not vector like in Godot 3 - previous value `g`\n\tset_up_direction(b)\n\tset_floor_stop_on_slope_enabled(c)\n\tset_max_slides(d)\n\tset_floor_max_angle(e)\n\t# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `f`\n\tmove_and_slide() # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("remove_and_slide(a,b,c,d,e,f)", "remove_and_slide(a,b,c,d,e,f)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("list_dir_begin( a , b )", "list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("list_dir_begin( a )", "list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("list_dir_begin( )", "list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("list_dir_begin( a , b )", "list_dir_begin() # TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("list_dir_begin( a )", "list_dir_begin() # TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("list_dir_begin( )", "list_dir_begin() # TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("sort_custom( a , b )", "sort_custom(Callable(a, b))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
@ -874,9 +874,9 @@ bool ProjectConverter3To4::test_conversion(RegExContainer &reg_container) {
valid = valid && test_conversion_gdscript_builtin("draw_line(1, 2, 3, 4, 5)", "draw_line(1, 2, 3, 4)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\timage.lock()", "\tfalse # image.lock() # TODOConverter40, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\timage.unlock()", "\tfalse # image.unlock() # TODOConverter40, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\troman.image.unlock()", "\tfalse # roman.image.unlock() # TODOConverter40, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\timage.lock()", "\tfalse # image.lock() # TODOConverter3To4, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\timage.unlock()", "\tfalse # image.unlock() # TODOConverter3To4, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\troman.image.unlock()", "\tfalse # roman.image.unlock() # TODOConverter3To4, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\tmtx.lock()", "\tmtx.lock()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\tmutex.unlock()", "\tmutex.unlock()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
@ -998,7 +998,7 @@ bool ProjectConverter3To4::test_conversion(RegExContainer &reg_container) {
valid = valid && test_conversion_gdscript_builtin("apply_force(position, impulse)", "apply_force(impulse, position)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("apply_impulse(position, impulse)", "apply_impulse(impulse, position)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("draw_rect(a,b,c,d,e).abc", "draw_rect(a, b, c, d).abc# e) TODOGODOT4 Antialiasing argument is missing", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("draw_rect(a,b,c,d,e).abc", "draw_rect(a, b, c, d).abc# e) TODOConverter3To4 Antialiasing argument is missing", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("get_focus_owner()", "get_viewport().gui_get_focus_owner()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("button.pressed = 1", "button.button_pressed = 1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("button.pressed=1", "button.button_pressed=1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
@ -1149,13 +1149,13 @@ bool ProjectConverter3To4::test_array_names() {
// Light2D, Texture, Viewport are special classes(probably virtual ones).
if (ClassDB::class_exists(StringName(old_class)) && old_class != "Light2D" && old_class != "Texture" && old_class != "Viewport") {
ERR_PRINT(vformat("Class \"%s\" exists in Godot 4.0, so it cannot be renamed to something else.", old_class));
ERR_PRINT(vformat("Class \"%s\" exists in Godot 4, so it cannot be renamed to something else.", old_class));
valid = false; // This probably should be only a warning, but not 100% sure - this would need to be added to CI.
}
// Callable is special class, to which normal classes may be renamed.
if (!ClassDB::class_exists(StringName(new_class)) && new_class != "Callable") {
ERR_PRINT(vformat("Class \"%s\" does not exist in Godot 4.0, so it cannot be used in the conversion.", old_class));
ERR_PRINT(vformat("Class \"%s\" does not exist in Godot 4, so it cannot be used in the conversion.", old_class));
valid = false; // This probably should be only a warning, but not 100% sure - this would need to be added to CI.
}
}
@ -1630,8 +1630,8 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
// With longer lines, doing so can sometimes be significantly faster.
if ((line.contains(".lock") || line.contains(".unlock")) && !line.contains("mtx") && !line.contains("mutex") && !line.contains("Mutex")) {
line = reg_container.reg_image_lock.sub(line, "false # $1.lock() # TODOConverter40, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", true);
line = reg_container.reg_image_unlock.sub(line, "false # $1.unlock() # TODOConverter40, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", true);
line = reg_container.reg_image_lock.sub(line, "false # $1.lock() # TODOConverter3To4, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", true);
line = reg_container.reg_image_unlock.sub(line, "false # $1.unlock() # TODOConverter3To4, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", true);
}
// PackedStringArray(req_godot).join('.') -> '.'.join(PackedStringArray(req_godot)) PoolStringArray
@ -1832,7 +1832,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
// infiinite_interia
if (parts.size() >= 6) {
line_new += starting_space + "# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `" + parts[5] + "`\n";
line_new += starting_space + "# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `" + parts[5] + "`\n";
}
line_new += starting_space + base_obj + "move_and_slide()";
@ -1863,7 +1863,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
// snap
if (parts.size() >= 2) {
line_new += starting_space + "# TODOConverter40 looks that snap in Godot 4.0 is float, not vector like in Godot 3 - previous value `" + parts[1] + "`\n";
line_new += starting_space + "# TODOConverter3To4 looks that snap in Godot 4 is float, not vector like in Godot 3 - previous value `" + parts[1] + "`\n";
}
// up_direction
@ -1888,7 +1888,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
// infiinite_interia
if (parts.size() >= 7) {
line_new += starting_space + "# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `" + parts[6] + "`\n";
line_new += starting_space + "# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `" + parts[6] + "`\n";
}
line_new += starting_space + base_obj + "move_and_slide()";
@ -1919,7 +1919,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
int start = line.find("list_dir_begin(");
int end = get_end_parenthesis(line.substr(start)) + 1;
if (end > -1) {
line = line.substr(0, start) + "list_dir_begin() " + line.substr(end + start) + "# TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547";
line = line.substr(0, start) + "list_dir_begin() " + line.substr(end + start) + "# TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547";
}
}
@ -2203,14 +2203,14 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
}
}
}
// draw_rect(a,b,c,d,e) -> draw_rect(a,b,c,d)#e) TODOGODOT4 Antialiasing argument is missing
// draw_rect(a,b,c,d,e) -> draw_rect(a,b,c,d)#e) TODOConverter3To4 Antialiasing argument is missing
if (contains_function_call(line, "draw_rect(")) {
int start = line.find("draw_rect(");
int end = get_end_parenthesis(line.substr(start)) + 1;
if (end > -1) {
Vector<String> parts = parse_arguments(line.substr(start, end));
if (parts.size() == 5) {
line = line.substr(0, start) + "draw_rect(" + parts[0] + ", " + parts[1] + ", " + parts[2] + ", " + parts[3] + ")" + line.substr(end + start) + "# " + parts[4] + ") TODOGODOT4 Antialiasing argument is missing";
line = line.substr(0, start) + "draw_rect(" + parts[0] + ", " + parts[1] + ", " + parts[2] + ", " + parts[3] + ")" + line.substr(end + start) + "# " + parts[4] + ") TODOConverter3To4 Antialiasing argument is missing";
}
}
}
@ -2241,9 +2241,10 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
// rotating = true -> ignore_rotation = false # reversed "rotating" for Camera2D
if (contains_function_call(line, "rotating")) {
int start = line.find("rotating");
String function_name = "rotating";
int start = line.find(function_name);
bool foundNextEqual = false;
String line_to_check = line.substr(start + String("rotating").length());
String line_to_check = line.substr(start + function_name.length());
String assigned_value;
for (int current_index = 0; line_to_check.length() > current_index; current_index++) {
char32_t chr = line_to_check.get(current_index);
@ -2251,14 +2252,14 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
continue;
} else if (chr == '=') {
foundNextEqual = true;
assigned_value = line.right(current_index).strip_edges();
assigned_value = line.substr(start + function_name.length() + current_index + 1).strip_edges();
assigned_value = assigned_value == "true" ? "false" : "true";
} else {
break;
}
}
if (foundNextEqual) {
line = line.substr(0, start) + "ignore_rotation =" + assigned_value + " # reversed \"rotating\" for Camera2D";
line = line.substr(0, start) + "ignore_rotation = " + assigned_value + " # reversed \"rotating\" for Camera2D";
}
}

@ -1042,10 +1042,25 @@ void ProjectListItemControl::set_project_icon(const Ref<Texture2D> &p_icon) {
project_icon->set_texture(p_icon);
}
void ProjectListItemControl::set_unsupported_features(const PackedStringArray &p_features) {
bool _project_feature_looks_like_version(const String &p_feature) {
return p_feature.contains(".") && p_feature.substr(0, 3).is_numeric();
}
void ProjectListItemControl::set_unsupported_features(PackedStringArray p_features) {
if (p_features.size() > 0) {
String unsupported_features_str = String(", ").join(p_features);
project_unsupported_features->set_tooltip_text(TTR("The project uses features unsupported by the current build:") + "\n" + unsupported_features_str);
String tooltip_text = "";
for (int i = 0; i < p_features.size(); i++) {
if (_project_feature_looks_like_version(p_features[i])) {
tooltip_text += TTR("This project was last edited in a different Godot version: ") + p_features[i] + "\n";
p_features.remove_at(i);
i--;
}
}
if (p_features.size() > 0) {
String unsupported_features_str = String(", ").join(p_features);
tooltip_text += TTR("This project uses features unsupported by the current build:") + "\n" + unsupported_features_str;
}
project_unsupported_features->set_tooltip_text(tooltip_text);
project_unsupported_features->show();
} else {
project_unsupported_features->hide();
@ -1450,7 +1465,7 @@ void ProjectList::_create_project_item_control(int p_index) {
hb->set_project_path(item.path);
hb->set_tooltip_text(item.description);
hb->set_tags(item.tags, this);
hb->set_unsupported_features(item.unsupported_features);
hb->set_unsupported_features(item.unsupported_features.duplicate());
hb->set_is_favorite(item.favorite);
hb->set_is_missing(item.missing);
@ -2297,8 +2312,8 @@ void ProjectManager::_open_selected_projects_ask() {
warning_message += TTR("Warning: This project uses C#, but this build of Godot does not have\nthe Mono module. If you proceed you will not be able to use any C# scripts.\n\n");
unsupported_features.remove_at(i);
i--;
} else if (feature.substr(0, 3).is_numeric()) {
warning_message += vformat(TTR("Warning: This project was built in Godot %s.\nOpening will upgrade or downgrade the project to Godot %s.\n\n"), Variant(feature), Variant(VERSION_BRANCH));
} else if (_project_feature_looks_like_version(feature)) {
warning_message += vformat(TTR("Warning: This project was last edited in Godot %s. Opening will change it to Godot %s.\n\n"), Variant(feature), Variant(VERSION_BRANCH));
unsupported_features.remove_at(i);
i--;
}
@ -2337,6 +2352,8 @@ void ProjectManager::_perform_full_project_conversion() {
args.push_back("--path");
args.push_back(path);
args.push_back("--convert-3to4");
args.push_back("--rendering-driver");
args.push_back(Main::get_rendering_driver_name());
Error err = OS::get_singleton()->create_instance(args);
ERR_FAIL_COND(err);
@ -2504,6 +2521,7 @@ void ProjectManager::_apply_project_tags() {
callable_mp((Window *)tag_manage_dialog, &Window::show).call_deferred(); // Make sure the dialog does not disappear.
return;
} else {
tags.sort();
cfg.set_value("application", "config/tags", tags);
err = cfg.save(project_godot);
if (err != OK) {
@ -3095,7 +3113,7 @@ ProjectManager::ProjectManager() {
ask_full_convert_dialog = memnew(ConfirmationDialog);
ask_full_convert_dialog->set_autowrap(true);
ask_full_convert_dialog->set_text(TTR("This option will perform full project conversion, updating scenes, resources and scripts from Godot 3.x to work in Godot 4.0.\n\nNote that this is a best-effort conversion, i.e. it makes upgrading the project easier, but it will not open out-of-the-box and will still require manual adjustments.\n\nIMPORTANT: Make sure to backup your project before converting, as this operation makes it impossible to open it in older versions of Godot."));
ask_full_convert_dialog->set_text(TTR("This option will perform full project conversion, updating scenes, resources and scripts from Godot 3 to work in Godot 4.\n\nNote that this is a best-effort conversion, i.e. it makes upgrading the project easier, but it will not open out-of-the-box and will still require manual adjustments.\n\nIMPORTANT: Make sure to backup your project before converting, as this operation makes it impossible to open it in older versions of Godot."));
ask_full_convert_dialog->connect("confirmed", callable_mp(this, &ProjectManager::_perform_full_project_conversion));
add_child(ask_full_convert_dialog);

@ -166,7 +166,7 @@ public:
void set_project_path(const String &p_path);
void set_tags(const PackedStringArray &p_tags, ProjectList *p_parent_list);
void set_project_icon(const Ref<Texture2D> &p_icon);
void set_unsupported_features(const PackedStringArray &p_features);
void set_unsupported_features(PackedStringArray p_features);
bool should_load_project_icon() const;
void set_selected(bool p_selected);

@ -464,7 +464,7 @@ String ShaderCreateDialog::_validate_path(const String &p_path) {
}
void ShaderCreateDialog::_msg_script_valid(bool valid, const String &p_msg) {
error_label->set_text("- " + p_msg);
error_label->set_text(String::utf8("") + p_msg);
if (valid) {
error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("success_color"), SNAME("Editor")));
} else {
@ -473,7 +473,7 @@ void ShaderCreateDialog::_msg_script_valid(bool valid, const String &p_msg) {
}
void ShaderCreateDialog::_msg_path_valid(bool valid, const String &p_msg) {
path_error_label->set_text("- " + p_msg);
path_error_label->set_text(String::utf8("") + p_msg);
if (valid) {
path_error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("success_color"), SNAME("Editor")));
} else {

@ -1956,6 +1956,10 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->_verbose_stdout = GLOBAL_GET("debug/settings/stdout/verbose_stdout");
}
#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
OS::get_singleton()->set_environment("MVK_CONFIG_LOG_LEVEL", OS::get_singleton()->_verbose_stdout ? "3" : "1"); // 1 = Errors only, 3 = Info
#endif
if (frame_delay == 0) {
frame_delay = GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/frame_delay_msec", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), 0);
}

@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.1</string>
<string>4.1.1</string>
<key>CFBundleSignature</key>
<string>godot</string>
<key>CFBundleVersion</key>
<string>4.1</string>
<string>4.1.1</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access is required to capture audio.</string>
<key>NSCameraUsageDescription</key>

@ -1,5 +1,5 @@
#define MyAppName "Godot Engine"
#define MyAppVersion "4.1"
#define MyAppVersion "4.1.1"
#define MyAppPublisher "Godot Engine contributors"
#define MyAppURL "https://godotengine.org/"
#define MyAppExeName "godot.exe"

@ -236,6 +236,8 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
p_script->member_lines[name] = m_enum->start_line;
doc.enums[name] = m_enum->doc_description;
for (const GDP::EnumNode::Value &val : m_enum->values) {
DocData::ConstantDoc const_doc;
const_doc.name = val.identifier->name;
@ -244,7 +246,6 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
const_doc.description = val.doc_description;
const_doc.enumeration = name;
doc.enums[const_doc.name] = const_doc.description;
doc.constants.push_back(const_doc);
}

@ -3229,7 +3229,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_type_test(ExpressionNode *
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_yield(ExpressionNode *p_previous_operand, bool p_can_assign) {
push_error(R"("yield" was removed in Godot 4.0. Use "await" instead.)");
push_error(R"("yield" was removed in Godot 4. Use "await" instead.)");
return nullptr;
}

@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
"yield" was removed in Godot 4.0. Use "await" instead.
"yield" was removed in Godot 4. Use "await" instead.

@ -286,15 +286,29 @@ def generate_sdk_package_versions():
version_status = version_status[:pos] + "." + version_status[pos:]
version_str += "-" + version_status
import version
version_defines = (
[
f"GODOT{version.major}",
f"GODOT{version.major}_{version.minor}",
f"GODOT{version.major}_{version.minor}_{version.patch}",
]
+ [f"GODOT{v}_OR_GREATER" for v in range(4, version.major + 1)]
+ [f"GODOT{version.major}_{v}_OR_GREATER" for v in range(0, version.minor + 1)]
+ [f"GODOT{version.major}_{version.minor}_{v}_OR_GREATER" for v in range(0, version.patch + 1)]
)
props = """<Project>
<PropertyGroup>
<PackageVersion_GodotSharp>{0}</PackageVersion_GodotSharp>
<PackageVersion_Godot_NET_Sdk>{0}</PackageVersion_Godot_NET_Sdk>
<PackageVersion_Godot_SourceGenerators>{0}</PackageVersion_Godot_SourceGenerators>
<GodotVersionConstants>{1}</GodotVersionConstants>
</PropertyGroup>
</Project>
""".format(
version_str
version_str, ";".join(version_defines)
)
# We write in ../SdkPackageVersions.props.

@ -2272,7 +2272,7 @@ void CSharpScript::reload_registered_script(Ref<CSharpScript> p_script) {
// If the EditorFileSystem singleton is available, update the file;
// otherwise, the file will be updated when the singleton becomes available.
EditorFileSystem *efs = EditorFileSystem::get_singleton();
if (efs) {
if (efs && !p_script->get_path().is_empty()) {
efs->update_file(p_script->get_path());
}
#endif

@ -7,7 +7,7 @@
<Authors>Godot Engine contributors</Authors>
<PackageId>Godot.NET.Sdk</PackageId>
<Version>4.1.0</Version>
<Version>4.1.1</Version>
<PackageVersion>$(PackageVersion_Godot_NET_Sdk)</PackageVersion>
<RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk</RepositoryUrl>
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>

@ -74,15 +74,8 @@
<!-- Godot DefineConstants. -->
<PropertyGroup>
<!-- Define constants to identify Godot builds and versions. -->
<GodotDefineConstants>
GODOT;
GODOT4;GODOT4_OR_GREATER;
GODOT4_1;GODOT4_1_OR_GREATER;GODOT4_0_OR_GREATER;
GODOT4_1_0;GODOT4_1_0_OR_GREATER;
</GodotDefineConstants>
<!-- Ensure the define constants don't contain whitespace (see https://github.com/dotnet/roslyn/issues/58391). -->
<GodotDefineConstants>$(GodotDefineConstants.Replace('%0A','').Replace('%0D','').Replace('%09','').Replace(' ',''))</GodotDefineConstants>
<!-- Define constants to identify Godot builds. -->
<GodotDefineConstants>GODOT</GodotDefineConstants>
<!--
Define constant to determine the target Godot platform. This includes the
@ -97,7 +90,7 @@
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'ios' ">GODOT_IPHONE;GODOT_IOS;GODOT_MOBILE</GodotPlatformConstants>
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'web' ">GODOT_JAVASCRIPT;GODOT_HTML5;GODOT_WASM;GODOT_WEB</GodotPlatformConstants>
<GodotDefineConstants>$(GodotDefineConstants);$(GodotPlatformConstants)</GodotDefineConstants>
<GodotDefineConstants>$(GodotDefineConstants);$(GodotPlatformConstants);$(GodotVersionConstants)</GodotDefineConstants>
</PropertyGroup>
<PropertyGroup>

@ -37,7 +37,7 @@ namespace Godot.SourceGenerators
while (symbol != null)
{
if (symbol.ContainingAssembly?.Name == assemblyName &&
symbol.ToString() == typeFullName)
symbol.FullQualifiedNameOmitGlobal() == typeFullName)
{
return true;
}
@ -230,22 +230,22 @@ namespace Godot.SourceGenerators
.Replace(">", ")");
public static bool IsGodotExportAttribute(this INamedTypeSymbol symbol)
=> symbol.ToString() == GodotClasses.ExportAttr;
=> symbol.FullQualifiedNameOmitGlobal() == GodotClasses.ExportAttr;
public static bool IsGodotSignalAttribute(this INamedTypeSymbol symbol)
=> symbol.ToString() == GodotClasses.SignalAttr;
=> symbol.FullQualifiedNameOmitGlobal() == GodotClasses.SignalAttr;
public static bool IsGodotMustBeVariantAttribute(this INamedTypeSymbol symbol)
=> symbol.ToString() == GodotClasses.MustBeVariantAttr;
=> symbol.FullQualifiedNameOmitGlobal() == GodotClasses.MustBeVariantAttr;
public static bool IsGodotClassNameAttribute(this INamedTypeSymbol symbol)
=> symbol.ToString() == GodotClasses.GodotClassNameAttr;
=> symbol.FullQualifiedNameOmitGlobal() == GodotClasses.GodotClassNameAttr;
public static bool IsGodotGlobalClassAttribute(this INamedTypeSymbol symbol)
=> symbol.ToString() == GodotClasses.GlobalClassAttr;
=> symbol.FullQualifiedNameOmitGlobal() == GodotClasses.GlobalClassAttr;
public static bool IsSystemFlagsAttribute(this INamedTypeSymbol symbol)
=> symbol.ToString() == GodotClasses.SystemFlagsAttr;
=> symbol.FullQualifiedNameOmitGlobal() == GodotClasses.SystemFlagsAttr;
public static GodotMethodData? HasGodotCompatibleSignature(
this IMethodSymbol method,

@ -9,7 +9,7 @@
<Authors>Godot Engine contributors</Authors>
<PackageId>Godot.SourceGenerators</PackageId>
<Version>4.1.0</Version>
<Version>4.1.1</Version>
<PackageVersion>$(PackageVersion_Godot_SourceGenerators)</PackageVersion>
<RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators</RepositoryUrl>
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>

@ -362,7 +362,7 @@ namespace Godot.SourceGenerators
{
foreach (var attr in memberSymbol.GetAttributes())
{
PropertyUsageFlags? propertyUsage = attr.AttributeClass?.ToString() switch
PropertyUsageFlags? propertyUsage = attr.AttributeClass?.FullQualifiedNameOmitGlobal() switch
{
GodotClasses.ExportCategoryAttr => PropertyUsageFlags.Category,
GodotClasses.ExportGroupAttr => PropertyUsageFlags.Group,

@ -28,10 +28,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3.0" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="JetBrains.Rider.PathLocator" Version="1.0.1" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<!-- For RiderPathLocator -->
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<Reference Include="GodotSharp">
<HintPath>$(GodotApiAssembliesDir)/GodotSharp.dll</HintPath>
<Private>False</Private>

@ -0,0 +1,51 @@
using System;
using Godot;
using JetBrains.Rider.PathLocator;
using Newtonsoft.Json;
using OS = GodotTools.Utils.OS;
namespace GodotTools.Ides.Rider;
public class RiderLocatorEnvironment : IRiderLocatorEnvironment
{
public JetBrains.Rider.PathLocator.OS CurrentOS
{
get
{
if (OS.IsWindows)
return JetBrains.Rider.PathLocator.OS.Windows;
if (OS.IsMacOS) return JetBrains.Rider.PathLocator.OS.MacOSX;
if (OS.IsUnixLike) return JetBrains.Rider.PathLocator.OS.Linux;
return JetBrains.Rider.PathLocator.OS.Other;
}
}
public T FromJson<T>(string json)
{
return JsonConvert.DeserializeObject<T>(json);
}
public void Info(string message, Exception e = null)
{
if (e == null)
GD.Print(message);
else
GD.Print(message, e);
}
public void Warn(string message, Exception e = null)
{
if (e == null)
GD.PushWarning(message);
else
GD.PushWarning(message, e);
}
public void Error(string message, Exception e = null)
{
if (e == null)
GD.PushError(message);
else
GD.PushError(message, e);
}
}

@ -1,474 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.Versioning;
using Godot;
using Microsoft.Win32;
using Newtonsoft.Json;
using Directory = System.IO.Directory;
using Environment = System.Environment;
using File = System.IO.File;
using Path = System.IO.Path;
using OS = GodotTools.Utils.OS;
// ReSharper disable UnassignedField.Local
// ReSharper disable InconsistentNaming
// ReSharper disable UnassignedField.Global
// ReSharper disable MemberHidesStaticFromOuterClass
namespace GodotTools.Ides.Rider
{
/// <summary>
/// This code is a modified version of the JetBrains resharper-unity plugin listed under Apache License 2.0 license:
/// https://github.com/JetBrains/resharper-unity/blob/master/unity/JetBrains.Rider.Unity.Editor/EditorPlugin/RiderPathLocator.cs
/// </summary>
public static class RiderPathLocator
{
public static RiderInfo[] GetAllRiderPaths()
{
try
{
if (OS.IsWindows)
{
return CollectRiderInfosWindows();
}
if (OS.IsMacOS)
{
return CollectRiderInfosMac();
}
if (OS.IsUnixLike)
{
return CollectAllRiderPathsLinux();
}
throw new InvalidOperationException("Unexpected OS.");
}
catch (Exception e)
{
GD.PushWarning(e.Message);
}
return Array.Empty<RiderInfo>();
}
private static RiderInfo[] CollectAllRiderPathsLinux()
{
var installInfos = new List<RiderInfo>();
string home = Environment.GetEnvironmentVariable("HOME");
if (!string.IsNullOrEmpty(home))
{
string toolboxRiderRootPath = GetToolboxBaseDir();
installInfos.AddRange(CollectPathsFromToolbox(toolboxRiderRootPath, "bin", "rider.sh", false)
.Select(a => new RiderInfo(a, true)).ToList());
//$Home/.local/share/applications/jetbrains-rider.desktop
var shortcut = new FileInfo(Path.Combine(home, @".local/share/applications/jetbrains-rider.desktop"));
if (shortcut.Exists)
{
string[] lines = File.ReadAllLines(shortcut.FullName);
foreach (string line in lines)
{
if (!line.StartsWith("Exec=\""))
continue;
string path = line.Split('"').Where((item, index) => index == 1).SingleOrDefault();
if (string.IsNullOrEmpty(path))
continue;
if (installInfos.Any(a => a.Path == path)) // avoid adding similar build as from toolbox
continue;
installInfos.Add(new RiderInfo(path, false));
}
}
}
// snap install
string snapInstallPath = "/snap/rider/current/bin/rider.sh";
if (new FileInfo(snapInstallPath).Exists)
installInfos.Add(new RiderInfo(snapInstallPath, false));
return installInfos.ToArray();
}
private static RiderInfo[] CollectRiderInfosMac()
{
var installInfos = new List<RiderInfo>();
// "/Applications/*Rider*.app"
// should be combined with "Contents/MacOS/rider"
var folder = new DirectoryInfo("/Applications");
if (folder.Exists)
{
installInfos.AddRange(folder.GetDirectories("*Rider*.app")
.Select(a => new RiderInfo(Path.Combine(a.FullName, "Contents/MacOS/rider"), false))
.ToList());
}
// /Users/user/Library/Application Support/JetBrains/Toolbox/apps/Rider/ch-1/181.3870.267/Rider EAP.app
// should be combined with "Contents/MacOS/rider"
string toolboxRiderRootPath = GetToolboxBaseDir();
var paths = CollectPathsFromToolbox(toolboxRiderRootPath, "", "Rider*.app", true)
.Select(a => new RiderInfo(Path.Combine(a, "Contents/MacOS/rider"), true));
installInfos.AddRange(paths);
return installInfos.ToArray();
}
[SupportedOSPlatform("windows")]
private static RiderInfo[] CollectRiderInfosWindows()
{
var installInfos = new List<RiderInfo>();
var toolboxRiderRootPath = GetToolboxBaseDir();
var installPathsToolbox = CollectPathsFromToolbox(toolboxRiderRootPath, "bin", "rider64.exe", false).ToList();
installInfos.AddRange(installPathsToolbox.Select(a => new RiderInfo(a, true)).ToList());
var installPaths = new List<string>();
const string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
CollectPathsFromRegistry(registryKey, installPaths);
const string wowRegistryKey = @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
CollectPathsFromRegistry(wowRegistryKey, installPaths);
installInfos.AddRange(installPaths.Select(a => new RiderInfo(a, false)).ToList());
return installInfos.ToArray();
}
private static string GetToolboxBaseDir()
{
if (OS.IsWindows)
{
string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
return GetToolboxRiderRootPath(localAppData);
}
if (OS.IsMacOS)
{
var home = Environment.GetEnvironmentVariable("HOME");
if (string.IsNullOrEmpty(home))
return string.Empty;
var localAppData = Path.Combine(home, @"Library/Application Support");
return GetToolboxRiderRootPath(localAppData);
}
if (OS.IsUnixLike)
{
var home = Environment.GetEnvironmentVariable("HOME");
if (string.IsNullOrEmpty(home))
return string.Empty;
var localAppData = Path.Combine(home, @".local/share");
return GetToolboxRiderRootPath(localAppData);
}
return string.Empty;
}
private static string GetToolboxRiderRootPath(string localAppData)
{
var toolboxPath = Path.Combine(localAppData, @"JetBrains/Toolbox");
var settingsJson = Path.Combine(toolboxPath, ".settings.json");
if (File.Exists(settingsJson))
{
var path = SettingsJson.GetInstallLocationFromJson(File.ReadAllText(settingsJson));
if (!string.IsNullOrEmpty(path))
toolboxPath = path;
}
var toolboxRiderRootPath = Path.Combine(toolboxPath, @"apps/Rider");
return toolboxRiderRootPath;
}
internal static ProductInfo GetBuildVersion(string path)
{
var buildTxtFileInfo = new FileInfo(Path.Combine(path, GetRelativePathToBuildTxt()));
var dir = buildTxtFileInfo.DirectoryName;
if (!Directory.Exists(dir))
return null;
var buildVersionFile = new FileInfo(Path.Combine(dir, "product-info.json"));
if (!buildVersionFile.Exists)
return null;
var json = File.ReadAllText(buildVersionFile.FullName);
return ProductInfo.GetProductInfo(json);
}
internal static Version GetBuildNumber(string path)
{
var file = new FileInfo(Path.Combine(path, GetRelativePathToBuildTxt()));
if (!file.Exists)
return null;
var text = File.ReadAllText(file.FullName);
if (text.Length <= 3)
return null;
var versionText = text.Substring(3);
return Version.TryParse(versionText, out var v) ? v : null;
}
internal static bool IsToolbox(string path)
{
return path.StartsWith(GetToolboxBaseDir());
}
private static string GetRelativePathToBuildTxt()
{
if (OS.IsWindows || OS.IsUnixLike)
return "../../build.txt";
if (OS.IsMacOS)
return "Contents/Resources/build.txt";
throw new InvalidOperationException("Unknown OS.");
}
[SupportedOSPlatform("windows")]
private static void CollectPathsFromRegistry(string registryKey, List<string> installPaths)
{
using (var key = Registry.CurrentUser.OpenSubKey(registryKey))
{
CollectPathsFromRegistry(installPaths, key);
}
using (var key = Registry.LocalMachine.OpenSubKey(registryKey))
{
CollectPathsFromRegistry(installPaths, key);
}
}
[SupportedOSPlatform("windows")]
private static void CollectPathsFromRegistry(List<string> installPaths, RegistryKey key)
{
if (key == null) return;
foreach (var subkeyName in key.GetSubKeyNames().Where(a => a.Contains("Rider")))
{
using (var subkey = key.OpenSubKey(subkeyName))
{
var folderObject = subkey?.GetValue("InstallLocation");
if (folderObject == null) continue;
var folder = folderObject.ToString();
var possiblePath = Path.Combine(folder, @"bin\rider64.exe");
if (File.Exists(possiblePath))
installPaths.Add(possiblePath);
}
}
}
private static string[] CollectPathsFromToolbox(string toolboxRiderRootPath, string dirName, string searchPattern,
bool isMac)
{
if (!Directory.Exists(toolboxRiderRootPath))
return Array.Empty<string>();
var channelDirs = Directory.GetDirectories(toolboxRiderRootPath);
var paths = channelDirs.SelectMany(channelDir =>
{
try
{
// use history.json - last entry stands for the active build https://jetbrains.slack.com/archives/C07KNP99D/p1547807024066500?thread_ts=1547731708.057700&cid=C07KNP99D
var historyFile = Path.Combine(channelDir, ".history.json");
if (File.Exists(historyFile))
{
var json = File.ReadAllText(historyFile);
var build = ToolboxHistory.GetLatestBuildFromJson(json);
if (build != null)
{
var buildDir = Path.Combine(channelDir, build);
var executablePaths = GetExecutablePaths(dirName, searchPattern, isMac, buildDir);
if (executablePaths.Any())
return executablePaths;
}
}
var channelFile = Path.Combine(channelDir, ".channel.settings.json");
if (File.Exists(channelFile))
{
var json = File.ReadAllText(channelFile).Replace("active-application", "active_application");
var build = ToolboxInstallData.GetLatestBuildFromJson(json);
if (build != null)
{
var buildDir = Path.Combine(channelDir, build);
var executablePaths = GetExecutablePaths(dirName, searchPattern, isMac, buildDir);
if (executablePaths.Any())
return executablePaths;
}
}
// changes in toolbox json files format may brake the logic above, so return all found Rider installations
return Directory.GetDirectories(channelDir)
.SelectMany(buildDir => GetExecutablePaths(dirName, searchPattern, isMac, buildDir));
}
catch (Exception e)
{
// do not write to Debug.Log, just log it.
Logger.Warn($"Failed to get RiderPath from {channelDir}", e);
}
return Array.Empty<string>();
})
.Where(c => !string.IsNullOrEmpty(c))
.ToArray();
return paths;
}
private static string[] GetExecutablePaths(string dirName, string searchPattern, bool isMac, string buildDir)
{
var folder = new DirectoryInfo(Path.Combine(buildDir, dirName));
if (!folder.Exists)
return Array.Empty<string>();
if (!isMac)
return new[] { Path.Combine(folder.FullName, searchPattern) }.Where(File.Exists).ToArray();
return folder.GetDirectories(searchPattern).Select(f => f.FullName)
.Where(Directory.Exists).ToArray();
}
// Disable the "field is never assigned" compiler warning. We never assign it, but Unity does.
// Note that Unity disable this warning in the generated C# projects
#pragma warning disable 0649
[Serializable]
class SettingsJson
{
public string install_location;
[return: MaybeNull]
public static string GetInstallLocationFromJson(string json)
{
try
{
return JsonConvert.DeserializeObject<SettingsJson>(json).install_location;
}
catch (Exception)
{
Logger.Warn($"Failed to get install_location from json {json}");
}
return null;
}
}
[Serializable]
class ToolboxHistory
{
public List<ItemNode> history;
public static string GetLatestBuildFromJson(string json)
{
try
{
return JsonConvert.DeserializeObject<ToolboxHistory>(json).history.LastOrDefault()?.item.build;
}
catch (Exception)
{
Logger.Warn($"Failed to get latest build from json {json}");
}
return null;
}
}
[Serializable]
class ItemNode
{
public BuildNode item;
}
[Serializable]
class BuildNode
{
public string build;
}
[Serializable]
public class ProductInfo
{
public string version;
public string versionSuffix;
[return: MaybeNull]
internal static ProductInfo GetProductInfo(string json)
{
try
{
var productInfo = JsonConvert.DeserializeObject<ProductInfo>(json);
return productInfo;
}
catch (Exception)
{
Logger.Warn($"Failed to get version from json {json}");
}
return null;
}
}
// ReSharper disable once ClassNeverInstantiated.Global
[Serializable]
class ToolboxInstallData
{
// ReSharper disable once InconsistentNaming
public ActiveApplication active_application;
[return: MaybeNull]
public static string GetLatestBuildFromJson(string json)
{
try
{
var toolbox = JsonConvert.DeserializeObject<ToolboxInstallData>(json);
var builds = toolbox.active_application.builds;
if (builds != null && builds.Any())
return builds.First();
}
catch (Exception)
{
Logger.Warn($"Failed to get latest build from json {json}");
}
return null;
}
}
[Serializable]
class ActiveApplication
{
public List<string> builds;
}
#pragma warning restore 0649
public struct RiderInfo
{
// ReSharper disable once NotAccessedField.Global
public bool IsToolbox;
public string Presentation;
public Version BuildNumber;
public ProductInfo ProductInfo;
public string Path;
public RiderInfo(string path, bool isToolbox)
{
BuildNumber = GetBuildNumber(path);
ProductInfo = GetBuildVersion(path);
Path = new FileInfo(path).FullName; // normalize separators
var presentation = $"Rider {BuildNumber}";
if (ProductInfo != null && !string.IsNullOrEmpty(ProductInfo.version))
{
var suffix = string.IsNullOrEmpty(ProductInfo.versionSuffix) ? "" : $" {ProductInfo.versionSuffix}";
presentation = $"Rider {ProductInfo.version}{suffix}";
}
if (isToolbox)
presentation += " (JetBrains Toolbox)";
Presentation = presentation;
IsToolbox = isToolbox;
}
}
private static class Logger
{
internal static void Warn(string message, Exception e = null)
{
throw new Exception(message, e);
}
}
}
}

@ -4,11 +4,19 @@ using System.IO;
using System.Linq;
using Godot;
using GodotTools.Internals;
using JetBrains.Rider.PathLocator;
namespace GodotTools.Ides.Rider
{
public static class RiderPathManager
{
private static readonly RiderPathLocator RiderPathLocator;
static RiderPathManager()
{
RiderPathLocator = new RiderPathLocator(new RiderLocatorEnvironment());
}
public static readonly string EditorPathSettingName = "dotnet/editor/editor_path_optional";
private static string GetRiderPathFromSettings()

@ -38,7 +38,7 @@ internal static class ExtensionMethods
}
private static bool IsGenerateUnmanagedCallbacksAttribute(this INamedTypeSymbol symbol)
=> symbol.ToString() == GeneratorClasses.GenerateUnmanagedCallbacksAttr;
=> symbol.FullQualifiedNameOmitGlobal() == GeneratorClasses.GenerateUnmanagedCallbacksAttr;
public static IEnumerable<(ClassDeclarationSyntax cds, INamedTypeSymbol symbol)> SelectUnmanagedCallbacksClasses(
this IEnumerable<ClassDeclarationSyntax> source,

@ -125,7 +125,10 @@ namespace Godot
NativePtr = IntPtr.Zero;
}
DisposablesTracker.UnregisterGodotObject(this, _weakReferenceToSelf);
if (_weakReferenceToSelf != null)
{
DisposablesTracker.UnregisterGodotObject(this, _weakReferenceToSelf);
}
}
/// <summary>

@ -20,7 +20,7 @@
<Authors>Godot Engine contributors</Authors>
<PackageId>GodotSharp</PackageId>
<Version>4.1.0</Version>
<Version>4.1.1</Version>
<PackageVersion>$(PackageVersion_GodotSharp)</PackageVersion>
<RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharp</RepositoryUrl>
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>

@ -15,7 +15,7 @@
<Authors>Godot Engine contributors</Authors>
<PackageId>GodotSharpEditor</PackageId>
<Version>4.1.0</Version>
<Version>4.1.1</Version>
<PackageVersion>$(PackageVersion_GodotSharp)</PackageVersion>
<RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharpEditor</RepositoryUrl>
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>

@ -301,6 +301,46 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
}
}
// Search all faces of start polygon as well.
bool closest_point_on_start_poly = false;
for (size_t point_id = 2; point_id < begin_poly->points.size(); point_id++) {
Face3 f(begin_poly->points[0].pos, begin_poly->points[point_id - 1].pos, begin_poly->points[point_id].pos);
Vector3 spoint = f.get_closest_point_to(p_destination);
real_t dpoint = spoint.distance_to(p_destination);
if (dpoint < end_d) {
end_point = spoint;
end_d = dpoint;
closest_point_on_start_poly = true;
}
}
if (closest_point_on_start_poly) {
// No point to run PostProcessing when start and end convex polygon is the same.
if (r_path_types) {
r_path_types->resize(2);
r_path_types->write[0] = begin_poly->owner->get_type();
r_path_types->write[1] = begin_poly->owner->get_type();
}
if (r_path_rids) {
r_path_rids->resize(2);
(*r_path_rids)[0] = begin_poly->owner->get_self();
(*r_path_rids)[1] = begin_poly->owner->get_self();
}
if (r_path_owners) {
r_path_owners->resize(2);
r_path_owners->write[0] = begin_poly->owner->get_owner_id();
r_path_owners->write[1] = begin_poly->owner->get_owner_id();
}
Vector<Vector3> path;
path.resize(2);
path.write[0] = begin_point;
path.write[1] = end_point;
return path;
}
// Reset open and navigation_polys
gd::NavigationPoly np = navigation_polys[0];
navigation_polys.clear();
@ -346,9 +386,44 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
}
}
// If we did not find a route, return an empty path.
// We did not find a route but we have both a start polygon and an end polygon at this point.
// Usually this happens because there was not a single external or internal connected edge, e.g. our start polygon is an isolated, single convex polygon.
if (!found_route) {
return Vector<Vector3>();
end_d = FLT_MAX;
// Search all faces of the start polygon for the closest point to our target position.
for (size_t point_id = 2; point_id < begin_poly->points.size(); point_id++) {
Face3 f(begin_poly->points[0].pos, begin_poly->points[point_id - 1].pos, begin_poly->points[point_id].pos);
Vector3 spoint = f.get_closest_point_to(p_destination);
real_t dpoint = spoint.distance_to(p_destination);
if (dpoint < end_d) {
end_point = spoint;
end_d = dpoint;
}
}
if (r_path_types) {
r_path_types->resize(2);
r_path_types->write[0] = begin_poly->owner->get_type();
r_path_types->write[1] = begin_poly->owner->get_type();
}
if (r_path_rids) {
r_path_rids->resize(2);
(*r_path_rids)[0] = begin_poly->owner->get_self();
(*r_path_rids)[1] = begin_poly->owner->get_self();
}
if (r_path_owners) {
r_path_owners->resize(2);
r_path_owners->write[0] = begin_poly->owner->get_owner_id();
r_path_owners->write[1] = begin_poly->owner->get_owner_id();
}
Vector<Vector3> path;
path.resize(2);
path.write[0] = begin_point;
path.write[1] = end_point;
return path;
}
Vector<Vector3> path;

@ -167,7 +167,7 @@ Error OS_Android::open_dynamic_library(const String p_path, void *&p_library_han
}
p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + ".");
ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror()));
if (r_resolved_path != nullptr) {
*r_resolved_path = path;

@ -257,7 +257,7 @@ Error OS_IOS::open_dynamic_library(const String p_path, void *&p_library_handle,
}
p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + ".");
ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror()));
if (r_resolved_path != nullptr) {
*r_resolved_path = path;

@ -235,11 +235,15 @@ def configure(env: "Environment"):
env.ParseConfig("pkg-config libenet --cflags --libs")
if not env["builtin_squish"]:
env.ParseConfig("pkg-config libsquish --cflags --libs")
# libsquish doesn't reliably install its .pc file, so some distros lack it.
env.Append(LIBS=["libsquish"])
if not env["builtin_zstd"]:
env.ParseConfig("pkg-config libzstd --cflags --libs")
if env["brotli"] and not env["builtin_brotli"]:
env.ParseConfig("pkg-config libbrotlicommon libbrotlidec --cflags --libs")
# Sound and video libraries
# Keep the order as it triggers chained dependencies (ogg needed by others, etc.)

@ -98,19 +98,20 @@ static bool detect_sandbox() {
JoypadLinux::JoypadLinux(Input *in) {
#ifdef UDEV_ENABLED
#ifdef SOWRAP_ENABLED
#ifdef DEBUG_ENABLED
int dylibloader_verbose = 1;
#else
int dylibloader_verbose = 0;
#endif
if (detect_sandbox()) {
// Linux binaries in sandboxes / containers need special handling because
// libudev doesn't work there. So we need to fallback to manual parsing
// of /dev/input in such case.
use_udev = false;
print_verbose("JoypadLinux: udev enabled, but detected incompatible sandboxed mode. Falling back to /dev/input to detect joypads.");
} else {
}
#ifdef SOWRAP_ENABLED
else {
#ifdef DEBUG_ENABLED
int dylibloader_verbose = 1;
#else
int dylibloader_verbose = 0;
#endif
use_udev = initialize_libudev(dylibloader_verbose) == 0;
if (use_udev) {
if (!udev_new || !udev_unref || !udev_enumerate_new || !udev_enumerate_add_match_subsystem || !udev_enumerate_scan_devices || !udev_enumerate_get_list_entry || !udev_list_entry_get_next || !udev_list_entry_get_name || !udev_device_new_from_syspath || !udev_device_get_devnode || !udev_device_get_action || !udev_device_unref || !udev_enumerate_unref || !udev_monitor_new_from_netlink || !udev_monitor_filter_add_match_subsystem_devtype || !udev_monitor_enable_receiving || !udev_monitor_get_fd || !udev_monitor_receive_device || !udev_monitor_unref) {
@ -124,10 +125,11 @@ JoypadLinux::JoypadLinux(Input *in) {
print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. Falling back to /dev/input to detect joypads.");
}
}
#endif
#endif // SOWRAP_ENABLED
#else
print_verbose("JoypadLinux: udev disabled, parsing /dev/input to detect joypads.");
#endif
#endif // UDEV_ENABLED
input = in;
monitor_joypads_thread.start(monitor_joypads_thread_func, this);
joypad_events_thread.start(joypad_events_thread_func, this);

@ -954,45 +954,33 @@ static String get_mountpoint(const String &p_path) {
}
Error OS_LinuxBSD::move_to_trash(const String &p_path) {
String path = p_path.rstrip("/"); // Strip trailing slash when path points to a directory
// We try multiple methods, until we find one that works.
// So we only return on success until we exhausted possibilities.
String path = p_path.rstrip("/"); // Strip trailing slash when path points to a directory.
int err_code;
List<String> args;
args.push_back(path);
args.push_front("trash"); // The command is `gio trash <file_name>` so we need to add it to args.
args.push_front("trash"); // The command is `gio trash <file_name>` so we add it before the path.
Error result = execute("gio", args, nullptr, &err_code); // For GNOME based machines.
if (result == OK) { // The `execute` function has done its job without errors.
if (!err_code) { // The shell command has been executed without errors.
return OK;
} else if (err_code == 1) {
ERR_PRINT("move_to_trash: No such file or directory as " + path + ".");
return ERR_FILE_NOT_FOUND;
}
if (result == OK && err_code == 0) { // Success.
return OK;
}
args.pop_front();
args.push_front("move");
args.push_back("trash:/"); // The command is `kioclient5 move <file_name> trash:/`.
result = execute("kioclient5", args, nullptr, &err_code); // For KDE based machines.
if (result == OK) { // The `execute` function has done its job without errors.
if (!err_code) { // The shell command has been executed without errors.
return OK;
} else if (err_code == 1) {
ERR_PRINT("move_to_trash: No such file or directory as " + path + ".");
return ERR_FILE_NOT_FOUND;
}
if (result == OK && err_code == 0) {
return OK;
}
args.pop_front();
args.pop_back();
result = execute("gvfs-trash", args, nullptr, &err_code); // For older Linux machines.
if (result == OK) { // The `execute` function has done its job without errors.
if (!err_code) { // The shell command has been executed without errors.
return OK;
} else if (err_code == 1) {
ERR_PRINT("move_to_trash: No such file or directory as " + path + ".");
return ERR_FILE_NOT_FOUND;
}
if (result == OK && err_code == 0) {
return OK;
}
// If the commands `kioclient5`, `gio` or `gvfs-trash` don't work on the system we do it manually.

@ -5449,7 +5449,9 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
#else
#ifdef XKB_ENABLED
xkb_loaded = true;
bool xkb_loaded = true;
xkb_loaded_v05p = true;
xkb_loaded_v08p = true;
#endif
#endif
@ -5476,6 +5478,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
r_error = OK;
#ifdef SOWRAP_ENABLED
{
if (!XcursorImageCreate || !XcursorImageLoadCursor || !XcursorImageDestroy || !XcursorGetDefaultSize || !XcursorGetTheme || !XcursorLibraryLoadImage) {
// There's no API to check version, check if functions are available instead.
@ -5484,6 +5487,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
return;
}
}
#endif
for (int i = 0; i < CURSOR_MAX; i++) {
cursors[i] = None;

@ -1211,7 +1211,7 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const
// Read Info.plist.
if (!p_info.is_empty()) {
print_verbose(vformat("CodeSign: Reading bundle info..."));
print_verbose("CodeSign: Reading bundle info...");
PList info_plist;
if (info_plist.load_file(p_info)) {
info_hash1 = file_hash_sha1(p_info);
@ -1262,7 +1262,7 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const
}
}
} else if (MachO::is_macho(main_exe)) {
print_verbose(vformat("CodeSign: Executable is thin..."));
print_verbose("CodeSign: Executable is thin...");
files_to_sign.push_back(main_exe);
} else {
r_error_msg = TTR("Invalid binary format.");
@ -1284,7 +1284,7 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const
// Generate core resources.
if (!p_bundle_path.is_empty()) {
print_verbose(vformat("CodeSign: Generating bundle CodeResources..."));
print_verbose("CodeSign: Generating bundle CodeResources...");
CodeSignCodeResources cr;
if (p_ios_bundle) {
@ -1366,7 +1366,7 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const
CharString uuid_str = id.utf8();
print_verbose(vformat("CodeSign: Used bundle ID: %s", id));
print_verbose(vformat("CodeSign: Processing entitlements..."));
print_verbose("CodeSign: Processing entitlements...");
Ref<CodeSignEntitlementsText> cet;
Ref<CodeSignEntitlementsBinary> ceb;
@ -1381,7 +1381,7 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const
ceb = Ref<CodeSignEntitlementsBinary>(memnew(CodeSignEntitlementsBinary(entitlements)));
}
print_verbose(vformat("CodeSign: Generating requirements..."));
print_verbose("CodeSign: Generating requirements...");
Ref<CodeSignRequirements> rq;
String team_id = "";
rq = Ref<CodeSignRequirements>(memnew(CodeSignRequirements()));
@ -1396,10 +1396,10 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const
}
print_verbose(vformat("CodeSign: Signing executable for cputype: %d ...", mh.get_cputype()));
print_verbose(vformat("CodeSign: Generating CodeDirectory..."));
print_verbose("CodeSign: Generating CodeDirectory...");
Ref<CodeSignCodeDirectory> cd1 = memnew(CodeSignCodeDirectory(0x14, 0x01, true, uuid_str, team_id.utf8(), 12, mh.get_exe_limit(), mh.get_code_limit()));
Ref<CodeSignCodeDirectory> cd2 = memnew(CodeSignCodeDirectory(0x20, 0x02, true, uuid_str, team_id.utf8(), 12, mh.get_exe_limit(), mh.get_code_limit()));
print_verbose(vformat("CodeSign: Calculating special slot hashes..."));
print_verbose("CodeSign: Calculating special slot hashes...");
if (info_hash2.size() == 0x20) {
cd2->set_hash_in_slot(info_hash2, CodeSignCodeDirectory::SLOT_INFO_PLIST);
}
@ -1444,7 +1444,7 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const
ERR_FAIL_V_MSG(FAILED, "CodeSign: Can't resize signature load command.");
}
print_verbose(vformat("CodeSign: Calculating executable code hashes..."));
print_verbose("CodeSign: Calculating executable code hashes...");
// Calculate executable code hashes.
PackedByteArray buffer;
PackedByteArray hash1, hash2;
@ -1481,11 +1481,11 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const
cd1->set_hash_in_slot(hash1, cd1->get_page_count());
}
print_verbose(vformat("CodeSign: Generating signature..."));
print_verbose("CodeSign: Generating signature...");
Ref<CodeSignSignature> cs;
cs = Ref<CodeSignSignature>(memnew(CodeSignSignature()));
print_verbose(vformat("CodeSign: Writing signature superblob..."));
print_verbose("CodeSign: Writing signature superblob...");
// Write signature data to the executable.
CodeSignSuperBlob sb = CodeSignSuperBlob();
sb.add_blob(cd2);
@ -1502,7 +1502,7 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const
sb.write_to_file(mh.get_file());
}
if (files_to_sign.size() > 1) {
print_verbose(vformat("CodeSign: Rebuilding fat executable..."));
print_verbose("CodeSign: Rebuilding fat executable...");
LipO lip;
if (!lip.create_file(main_exe, files_to_sign)) {
CLEANUP();

@ -189,7 +189,7 @@ Error OS_MacOS::open_dynamic_library(const String p_path, void *&p_library_handl
}
p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + ".");
ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror()));
if (r_resolved_path != nullptr) {
*r_resolved_path = path;

@ -131,9 +131,9 @@ void EditorExportPlatformUWP::get_export_options(List<ExportOption> *r_options)
bool EditorExportPlatformUWP::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
#ifndef DEV_ENABLED
// We don't provide export templates for the UWP platform currently as it
// has not been ported for Godot 4.0. This is skipped in DEV_ENABLED so that
// has not been ported for Godot 4. This is skipped in DEV_ENABLED so that
// contributors can still test the pipeline if/when we can build it again.
r_error = "The UWP platform is currently not supported in Godot 4.0.\n";
r_error = "The UWP platform is currently not supported in Godot 4.\n";
return false;
#endif
@ -180,9 +180,9 @@ bool EditorExportPlatformUWP::has_valid_export_configuration(const Ref<EditorExp
bool EditorExportPlatformUWP::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
#ifndef DEV_ENABLED
// We don't provide export templates for the UWP platform currently as it
// has not been ported for Godot 4.0. This is skipped in DEV_ENABLED so that
// has not been ported for Godot 4. This is skipped in DEV_ENABLED so that
// contributors can still test the pipeline if/when we can build it again.
r_error = "The UWP platform is currently not supported in Godot 4.0.\n";
r_error = "The UWP platform is currently not supported in Godot 4.\n";
return false;
#endif

@ -745,7 +745,7 @@ static String format_error_message(DWORD id) {
Error OS_UWP::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) {
String full_path = "game/" + p_path;
p_library_handle = (void *)LoadPackagedLibrary((LPCWSTR)(full_path.utf16().get_data()), 0);
ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + full_path + ", error: " + format_error_message(GetLastError()) + ".");
ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", full_path, format_error_message(GetLastError())));
if (r_resolved_path != nullptr) {
*r_resolved_path = full_path;

Some files were not shown because too many files have changed in this diff Show More