diff --git a/.clang-format b/.clang-format
index 1df6c35bfb5b..eda00dd8ddce 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,29 +1,59 @@
# Commented out parameters are those with the same value as base LLVM style.
# We can uncomment them if we want to change their value, or enforce the
-# chosen value in case the base style changes (last sync: Clang 14.0).
----
-### General config, applies to all languages ###
-BasedOnStyle: LLVM
+# chosen value in case the base style changes (last sync: Clang 18.1.8).
+BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
# AlignArrayOfStructures: None
-# AlignConsecutiveMacros: None
-# AlignConsecutiveAssignments: None
-# AlignConsecutiveBitFields: None
-# AlignConsecutiveDeclarations: None
+# AlignConsecutiveAssignments:
+# Enabled: false
+# AcrossEmptyLines: false
+# AcrossComments: false
+# AlignCompound: false
+# AlignFunctionPointers: false
+# PadOperators: true
+# AlignConsecutiveBitFields:
+# Enabled: false
+# AcrossEmptyLines: false
+# AcrossComments: false
+# AlignCompound: false
+# AlignFunctionPointers: false
+# PadOperators: false
+# AlignConsecutiveDeclarations:
+# Enabled: false
+# AcrossEmptyLines: false
+# AcrossComments: false
+# AlignCompound: false
+# AlignFunctionPointers: false
+# PadOperators: false
+# AlignConsecutiveMacros:
+# Enabled: false
+# AcrossEmptyLines: false
+# AcrossComments: false
+# AlignCompound: false
+# AlignFunctionPointers: false
+# PadOperators: false
+# AlignConsecutiveShortCaseStatements:
+# Enabled: false
+# AcrossEmptyLines: false
+# AcrossComments: false
+# AlignCaseColons: false
# AlignEscapedNewlines: Right
-AlignOperands: DontAlign
-AlignTrailingComments: false
+AlignOperands: DontAlign
+AlignTrailingComments:
+ Kind: Never
+ OverEmptyLines: 0
# AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
-# AllowShortEnumsOnASingleLine: true
+# AllowBreakBeforeNoexceptSpecifier: Never
# AllowShortBlocksOnASingleLine: Never
# AllowShortCaseLabelsOnASingleLine: false
+# AllowShortCompoundRequirementOnASingleLine: true
+# AllowShortEnumsOnASingleLine: true
# AllowShortFunctionsOnASingleLine: All
-# AllowShortLambdasOnASingleLine: All
# AllowShortIfStatementsOnASingleLine: Never
+# AllowShortLambdasOnASingleLine: All
# AllowShortLoopsOnASingleLine: false
-# AlwaysBreakAfterDefinitionReturnType: None
# AlwaysBreakAfterReturnType: None
# AlwaysBreakBeforeMultilineStrings: false
# AlwaysBreakTemplateDeclarations: MultiLine
@@ -31,50 +61,49 @@ AllowAllParametersOfDeclarationOnNextLine: false
# - __capability
# BinPackArguments: true
# BinPackParameters: true
+# BitFieldColonSpacing: Both
# BraceWrapping:
-# AfterCaseLabel: false
-# AfterClass: false
+# AfterCaseLabel: false
+# AfterClass: false
# AfterControlStatement: Never
-# AfterEnum: false
-# AfterFunction: false
-# AfterNamespace: false
+# AfterEnum: false
+# AfterFunction: false
+# AfterNamespace: false
# AfterObjCDeclaration: false
-# AfterStruct: false
-# AfterUnion: false
+# AfterStruct: false
+# AfterUnion: false
# AfterExternBlock: false
-# BeforeCatch: false
-# BeforeElse: false
+# BeforeCatch: false
+# BeforeElse: false
# BeforeLambdaBody: false
-# BeforeWhile: false
-# IndentBraces: false
+# BeforeWhile: false
+# IndentBraces: false
# SplitEmptyFunction: true
# SplitEmptyRecord: true
# SplitEmptyNamespace: true
+# BreakAdjacentStringLiterals: true
+# BreakAfterAttributes: Leave
+# BreakAfterJavaFieldAnnotations: false
+# BreakArrays: true
# BreakBeforeBinaryOperators: None
-# BreakBeforeConceptDeclarations: true
# BreakBeforeBraces: Attach
-# BreakBeforeInheritanceComma: false
-# BreakInheritanceList: BeforeColon
+# BreakBeforeConceptDeclarations: Always
+# BreakBeforeInlineASMColon: OnlyMultiline
# BreakBeforeTernaryOperators: true
-# BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: AfterColon
+# BreakInheritanceList: BeforeColon
# BreakStringLiterals: true
-ColumnLimit: 0
-# CommentPragmas: '^ IWYU pragma:'
-# QualifierAlignment: Leave
+ColumnLimit: 0
+# CommentPragmas: '^ IWYU pragma:'
# CompactNamespaces: false
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
-# DeriveLineEnding: true
# DerivePointerAlignment: false
-# DisableFormat: false
+# DisableFormat: false
# EmptyLineAfterAccessModifier: Never
# EmptyLineBeforeAccessModifier: LogicalBlock
# ExperimentalAutoDetectBinPacking: false
-# PackConstructorInitializers: BinPack
-ConstructorInitializerAllOnOneLineOrOnePerLine: true
-# AllowAllConstructorInitializersOnNextLine: true
# FixNamespaceComments: true
# ForEachMacros:
# - foreach
@@ -82,118 +111,138 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
# - BOOST_FOREACH
# IfMacros:
# - KJ_IF_MAYBE
-# IncludeBlocks: Preserve
+# IncludeBlocks: Preserve
IncludeCategories:
- - Regex: '".*"'
- Priority: 1
- - Regex: '^<.*\.h>'
- Priority: 2
- - Regex: '^<.*'
- Priority: 3
-# IncludeIsMainRegex: '(Test)?$'
+ - Regex: ^".*"$
+ Priority: 1
+ - Regex: ^<.*\.h>$
+ Priority: 2
+ - Regex: ^<.*>$
+ Priority: 3
+# IncludeIsMainRegex: (Test)?$
# IncludeIsMainSourceRegex: ''
# IndentAccessModifiers: false
-IndentCaseLabels: true
# IndentCaseBlocks: false
+IndentCaseLabels: true
+# IndentExternBlock: AfterExternBlock
# IndentGotoLabels: true
# IndentPPDirectives: None
-# IndentExternBlock: AfterExternBlock
-# IndentRequires: false
-IndentWidth: 4
+# IndentRequiresClause: true
+IndentWidth: 4
# IndentWrappedFunctionNames: false
+# InsertBraces: false
+# InsertNewlineAtEOF: false
# InsertTrailingCommas: None
+# IntegerLiteralSeparator:
+# Binary: 0
+# BinaryMinDigits: 0
+# Decimal: 0
+# DecimalMinDigits: 0
+# Hex: 0
+# HexMinDigits: 0
+JavaImportGroups:
+ - org.godotengine
+ - android
+ - androidx
+ - com.android
+ - com.google
+ - java
+ - javax
# JavaScriptQuotes: Leave
# JavaScriptWrapImports: true
+# KeepEmptyLinesAtEOF: false
KeepEmptyLinesAtTheStartOfBlocks: false
# LambdaBodyIndentation: Signature
+# Language: Cpp
+# LineEnding: DeriveLF
# MacroBlockBegin: ''
-# MacroBlockEnd: ''
+# MacroBlockEnd: ''
# MaxEmptyLinesToKeep: 1
# NamespaceIndentation: None
+# ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 4
+# ObjCBreakBeforeNestedBlockParam: true
+# ObjCSpaceAfterProperty: false
+# ObjCSpaceBeforeProtocolList: true
+# PPIndentWidth: -1
+PackConstructorInitializers: NextLine
# PenaltyBreakAssignment: 2
# PenaltyBreakBeforeFirstCallParameter: 19
# PenaltyBreakComment: 300
# PenaltyBreakFirstLessLess: 120
# PenaltyBreakOpenParenthesis: 0
+# PenaltyBreakScopeResolution: 500
# PenaltyBreakString: 1000
# PenaltyBreakTemplateDeclaration: 10
# PenaltyExcessCharacter: 1000000
-# PenaltyReturnTypeOnItsOwnLine: 60
# PenaltyIndentedWhitespace: 0
+# PenaltyReturnTypeOnItsOwnLine: 60
# PointerAlignment: Right
-# PPIndentWidth: -1
+# QualifierAlignment: Leave
# ReferenceAlignment: Pointer
-# ReflowComments: true
+# ReflowComments: true
# RemoveBracesLLVM: false
+# RemoveParentheses: Leave
+# RemoveSemicolon: false
+# RequiresClausePosition: OwnLine
+# RequiresExpressionIndentation: OuterScope
# SeparateDefinitionBlocks: Leave
# ShortNamespaceLines: 1
-# SortIncludes: CaseSensitive
+# SkipMacroDefinitionBody: false
+# SortIncludes: CaseSensitive
# SortJavaStaticImport: Before
-# SortUsingDeclarations: true
+# SortUsingDeclarations: LexicographicNumeric
# SpaceAfterCStyleCast: false
# SpaceAfterLogicalNot: false
# SpaceAfterTemplateKeyword: true
+# SpaceAroundPointerQualifiers: Default
# SpaceBeforeAssignmentOperators: true
# SpaceBeforeCaseColon: false
# SpaceBeforeCpp11BracedList: false
# SpaceBeforeCtorInitializerColon: true
# SpaceBeforeInheritanceColon: true
-# SpaceBeforeParens: ControlStatements
+# SpaceBeforeJsonColon: false
# SpaceBeforeParensOptions:
# AfterControlStatements: true
# AfterForeachMacros: true
-# AfterFunctionDefinitionName: false
# AfterFunctionDeclarationName: false
-# AfterIfMacros: true
+# AfterFunctionDefinitionName: false
+# AfterIfMacros: true
# AfterOverloadedOperator: false
+# AfterPlacementOperator: true
+# AfterRequiresInClause: false
+# AfterRequiresInExpression: false
# BeforeNonEmptyParentheses: false
-# SpaceAroundPointerQualifiers: Default
# SpaceBeforeRangeBasedForLoopColon: true
+# SpaceBeforeSquareBrackets: false
# SpaceInEmptyBlock: false
-# SpaceInEmptyParentheses: false
# SpacesBeforeTrailingComments: 1
-# SpacesInAngles: Never
-# SpacesInConditionalStatement: false
+# SpacesInAngles: Never
# SpacesInContainerLiterals: true
-# SpacesInCStyleCastParentheses: false
## Godot TODO: We'll want to use a min of 1, but we need to see how to fix
## our comment capitalization at the same time.
SpacesInLineCommentPrefix:
- Minimum: 0
- Maximum: -1
-# SpacesInParentheses: false
+ Minimum: 0
+ Maximum: -1
+# SpacesInParens: Never
+# SpacesInParensOptions:
+# InConditionalStatements: false
+# InCStyleCasts: false
+# InEmptyParentheses: false
+# Other: false
# SpacesInSquareBrackets: false
-# SpaceBeforeSquareBrackets: false
-# BitFieldColonSpacing: Both
+Standard: c++17
# StatementAttributeLikeMacros:
# - Q_EMIT
# StatementMacros:
# - Q_UNUSED
# - QT_REQUIRE_VERSION
-TabWidth: 4
-# UseCRLF: false
-UseTab: Always
+TabWidth: 4
+UseTab: Always
+# VerilogBreakBetweenInstancePorts: true
# WhitespaceSensitiveMacros:
-# - STRINGIZE
-# - PP_STRINGIZE
# - BOOST_PP_STRINGIZE
-# - NS_SWIFT_NAME
# - CF_SWIFT_NAME
----
-### C++ specific config ###
-Language: Cpp
-Standard: c++17
----
-### ObjC specific config ###
-Language: ObjC
-# ObjCBinPackProtocolList: Auto
-ObjCBlockIndentWidth: 4
-# ObjCBreakBeforeNestedBlockParam: true
-# ObjCSpaceAfterProperty: false
-# ObjCSpaceBeforeProtocolList: true
----
-### Java specific config ###
-Language: Java
-# BreakAfterJavaFieldAnnotations: false
-JavaImportGroups: ['org.godotengine', 'android', 'androidx', 'com.android', 'com.google', 'java', 'javax']
-...
+# - NS_SWIFT_NAME
+# - PP_STRINGIZE
+# - STRINGIZE
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index ebfd3b515af3..1c78dde7a4e9 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -54,3 +54,6 @@ df61dc4b2bd54a5a40c515493c76f5a458e5b541
# Enforce template syntax `typename` over `class`
9903e6779b70fc03aae70a37b9cf053f4f355b91
+
+# Style: Apply new `clang-format` fixes
+b37fc1014abf7adda70dc30b0822d775b3a4433f
diff --git a/.gitattributes b/.gitattributes
index 5af3e121a8cc..30d1acb49737 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,6 +1,6 @@
# Properly detect languages on Github
-*.h linguist-language=cpp
-*.inc linguist-language=cpp
+*.h linguist-language=C++
+*.inc linguist-language=C++
thirdparty/* linguist-vendored
# Normalize EOL for all files that Git considers text files
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index a1d406ff92de..748d787b86ea 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -4,180 +4,234 @@
# Buildsystem
-.* @godotengine/buildsystem
-.github/ @godotengine/buildsystem
-*.py @godotengine/buildsystem
-SConstruct @godotengine/buildsystem
-SCsub @godotengine/buildsystem
+* @godotengine/buildsystem
# Core
-/core/ @godotengine/core
-/core/crypto/ @godotengine/network
-/core/debugger/ @godotengine/debugger
-/core/extension/ @godotengine/gdextension
-/core/input/ @godotengine/input
+/core/ @godotengine/core
+/core/crypto/ @godotengine/network
+/core/debugger/ @godotengine/debugger
+/core/extension/ @godotengine/gdextension
+/core/input/ @godotengine/input
# Doc
-/doc/ @godotengine/documentation
-doc_classes/* @godotengine/documentation
+/doc/ @godotengine/documentation
+**/doc_classes/ @godotengine/documentation
# Drivers
## Audio
-/drivers/alsa/ @godotengine/audio
-/drivers/alsamidi/ @godotengine/audio
-/drivers/coreaudio/ @godotengine/audio
-/drivers/coremidi/ @godotengine/audio
-/drivers/pulseaudio/ @godotengine/audio
-/drivers/wasapi/ @godotengine/audio
-/drivers/winmidi/ @godotengine/audio
-/drivers/xaudio2/ @godotengine/audio
+/drivers/alsa/ @godotengine/audio
+/drivers/alsamidi/ @godotengine/audio
+/drivers/coreaudio/ @godotengine/audio
+/drivers/coremidi/ @godotengine/audio
+/drivers/pulseaudio/ @godotengine/audio
+/drivers/wasapi/ @godotengine/audio
+/drivers/winmidi/ @godotengine/audio
+/drivers/xaudio2/ @godotengine/audio
## Rendering
-/drivers/d3d12/ @godotengine/rendering
-/drivers/dummy/ @godotengine/rendering
-/drivers/gles3/ @godotengine/rendering
-/drivers/spirv-reflect/ @godotengine/rendering
-/drivers/vulkan/ @godotengine/rendering
+/drivers/d3d12/ @godotengine/rendering
+/drivers/dummy/ @godotengine/rendering
+/drivers/gles3/ @godotengine/rendering
+/drivers/spirv-reflect/ @godotengine/rendering
+/drivers/vulkan/ @godotengine/rendering
## OS
-/drivers/unix/ @godotengine/_platforms
-/drivers/windows/ @godotengine/windows
+/drivers/unix/ @godotengine/_platforms
+/drivers/windows/ @godotengine/windows
## Misc
-/drivers/png/ @godotengine/import
+/drivers/png/ @godotengine/import
# Editor
-/editor/*debugger* @godotengine/debugger
-/editor/gui/ @godotengine/usability @godotengine/gui-nodes
-/editor/icons/ @godotengine/usability
-/editor/import/ @godotengine/import
-/editor/plugins/*2d_*.* @godotengine/2d-editor
-/editor/plugins/*3d_*.* @godotengine/3d-editor
-/editor/plugins/script_*.* @godotengine/script-editor
-/editor/plugins/*shader*.* @godotengine/shaders
-/editor/themes/ @godotengine/usability @godotengine/gui-nodes
-/editor/code_editor.* @godotengine/script-editor
-/editor/*dock*.* @godotengine/docks
-/editor/*shader*.* @godotengine/shaders
+/editor/*debugger* @godotengine/debugger
+/editor/gui/ @godotengine/usability @godotengine/gui-nodes
+/editor/icons/ @godotengine/usability
+/editor/import/ @godotengine/import
+/editor/plugins/*2d_*.* @godotengine/2d-editor
+/editor/plugins/*3d_*.* @godotengine/3d-editor
+/editor/plugins/script_*.* @godotengine/script-editor
+/editor/plugins/*shader*.* @godotengine/shaders
+/editor/themes/ @godotengine/usability @godotengine/gui-nodes
+/editor/code_editor.* @godotengine/script-editor
+/editor/*dock*.* @godotengine/docks
+/editor/*shader*.* @godotengine/shaders
# Main
-/main/ @godotengine/core
+/main/ @godotengine/core
# Misc
-/misc/ @godotengine/buildsystem
-/misc/extension_api_validation/ @godotengine/gdextension @godotengine/dotnet
+/misc/ @godotengine/buildsystem
+/misc/extension_api_validation/ @godotengine/gdextension @godotengine/dotnet
# Modules
## Audio (+ video)
-/modules/minimp3/ @godotengine/audio
-/modules/ogg/ @godotengine/audio
-/modules/opus/ @godotengine/audio
-/modules/theora/ @godotengine/audio
-/modules/vorbis/ @godotengine/audio
-/modules/webm/ @godotengine/audio
+/modules/interactive_music/ @godotengine/audio
+/modules/interactive_music/doc_classes/ @godotengine/audio @godotengine/documentation
+/modules/minimp3/ @godotengine/audio
+/modules/minimp3/doc_classes/ @godotengine/audio @godotengine/documentation
+/modules/ogg/ @godotengine/audio
+/modules/ogg/doc_classes/ @godotengine/audio @godotengine/documentation
+/modules/theora/ @godotengine/audio
+/modules/theora/doc_classes/ @godotengine/audio @godotengine/documentation
+/modules/vorbis/ @godotengine/audio
+/modules/vorbis/doc_classes/ @godotengine/audio @godotengine/documentation
## Import
-/modules/basis_universal/ @godotengine/import
-/modules/bmp/ @godotengine/import
-/modules/cvtt/ @godotengine/import
-/modules/dds/ @godotengine/import
-/modules/etc/ @godotengine/import
-/modules/fbx/ @godotengine/import
-/modules/gltf/ @godotengine/import
-/modules/hdr/ @godotengine/import
-/modules/jpg/ @godotengine/import
-/modules/pvr/ @godotengine/import
-/modules/squish/ @godotengine/import
-/modules/svg/ @godotengine/import
-/modules/tga/ @godotengine/import
-/modules/tinyexr/ @godotengine/import
-/modules/webp/ @godotengine/import
+/modules/astcenc/ @godotengine/import
+/modules/basis_universal/ @godotengine/import
+/modules/betsy/ @godotengine/import
+/modules/bmp/ @godotengine/import
+/modules/cvtt/ @godotengine/import
+/modules/dds/ @godotengine/import
+/modules/etcpak/ @godotengine/import
+/modules/fbx/ @godotengine/import
+/modules/fbx/doc_classes/ @godotengine/import @godotengine/documentation
+/modules/gltf/ @godotengine/import
+/modules/gltf/doc_classes/ @godotengine/import @godotengine/documentation
+/modules/gltf/tests/ @godotengine/import @godotengine/tests
+/modules/hdr/ @godotengine/import
+/modules/jpg/ @godotengine/import
+/modules/ktx/ @godotengine/import
+/modules/squish/ @godotengine/import
+/modules/svg/ @godotengine/import
+/modules/tga/ @godotengine/import
+/modules/tinyexr/ @godotengine/import
+/modules/webp/ @godotengine/import
## Network
-/modules/enet/ @godotengine/network
-/modules/mbedtls/ @godotengine/network
-/modules/upnp/ @godotengine/network
-/modules/webrtc/ @godotengine/network
-/modules/websocket/ @godotengine/network
+/modules/enet/ @godotengine/network
+/modules/enet/doc_classes/ @godotengine/network @godotengine/documentation
+/modules/mbedtls/ @godotengine/network
+/modules/mbedtls/tests/ @godotengine/network @godotengine/tests
+/modules/multiplayer/ @godotengine/network
+/modules/multiplayer/doc_classes/ @godotengine/network @godotengine/documentation
+/modules/upnp/ @godotengine/network
+/modules/upnp/doc_classes/ @godotengine/network @godotengine/documentation
+/modules/webrtc/ @godotengine/network
+/modules/webrtc/doc_classes/ @godotengine/network @godotengine/documentation
+/modules/websocket/ @godotengine/network
+/modules/websocket/doc_classes/ @godotengine/network @godotengine/documentation
+
+## Physics
+/modules/godot_physics_2d/ @godotengine/physics
+/modules/godot_physics_3d/ @godotengine/physics
## Rendering
-/modules/denoise/ @godotengine/rendering
-/modules/glslang/ @godotengine/rendering
-/modules/lightmapper_rd/ @godotengine/rendering
-/modules/meshoptimizer/ @godotengine/rendering
-/modules/vhacd/ @godotengine/rendering
-/modules/xatlas_unwrap/ @godotengine/rendering
+/modules/glslang/ @godotengine/rendering
+/modules/lightmapper_rd/ @godotengine/rendering
+/modules/meshoptimizer/ @godotengine/rendering
+/modules/raycast/ @godotengine/rendering
+/modules/vhacd/ @godotengine/rendering
+/modules/xatlas_unwrap/ @godotengine/rendering
## Scripting
-/modules/gdscript/ @godotengine/gdscript
-/modules/jsonrpc/ @godotengine/gdscript
-/modules/mono/ @godotengine/dotnet
+/modules/gdscript/ @godotengine/gdscript
+/modules/gdscript/doc_classes/ @godotengine/gdscript @godotengine/documentation
+/modules/gdscript/icons/ @godotengine/gdscript @godotengine/usability
+/modules/gdscript/tests/ @godotengine/gdscript @godotengine/tests
+/modules/jsonrpc/ @godotengine/gdscript @godotengine/network
+/modules/jsonrpc/tests @godotengine/gdscript @godotengine/network @godotengine/tests
+/modules/mono/ @godotengine/dotnet
+/modules/mono/doc_classes/ @godotengine/dotnet @godotengine/documentation
+/modules/mono/icons/ @godotengine/dotnet @godotengine/usability
## Text
-/modules/freetype/ @godotengine/buildsystem
-/modules/text_server_adv/ @godotengine/gui-nodes
-/modules/text_server_fb/ @godotengine/gui-nodes
+/modules/freetype/ @godotengine/buildsystem
+/modules/msdfgen/ @godotengine/buildsystem
+/modules/text_server_adv/ @godotengine/gui-nodes
+/modules/text_server_adv/doc_classes/ @godotengine/gui-nodes @godotengine/documentation
+/modules/text_server_fb/ @godotengine/gui-nodes
+/modules/text_server_fb/doc_classes/ @godotengine/gui-nodes @godotengine/documentation
## XR
-/modules/camera/ @godotengine/xr
-/modules/gdextension/xr/ @godotengine/xr
-/modules/mobile_vr/ @godotengine/xr
-/modules/webxr/ @godotengine/xr
+/modules/camera/ @godotengine/xr
+/modules/mobile_vr/ @godotengine/xr
+/modules/mobile_vr/doc_classes/ @godotengine/xr @godotengine/documentation
+/modules/openxr/ @godotengine/xr
+/modules/openxr/doc_classes/ @godotengine/xr @godotengine/documentation
+/modules/webxr/ @godotengine/xr
+/modules/webxr/doc_classes/ @godotengine/xr @godotengine/documentation
## Misc
-/modules/bullet/ @godotengine/physics
-/modules/csg/ @godotengine/3d-nodes
-/modules/gdnavigation/ @godotengine/navigation
-/modules/gridmap/ @godotengine/3d-nodes
-/modules/opensimplex/ @godotengine/3d-nodes
-/modules/regex/ @godotengine/core
+/modules/csg/ @godotengine/3d-nodes
+/modules/csg/doc_classes/ @godotengine/3d-nodes @godotengine/documentation
+/modules/csg/icons/ @godotengine/3d-nodes @godotengine/usability
+/modules/navigation/ @godotengine/navigation
+/modules/gridmap/ @godotengine/3d-nodes
+/modules/gridmap/doc_classes/ @godotengine/3d-nodes @godotengine/documentation
+/modules/gridmap/icons/ @godotengine/3d-nodes @godotengine/usability
+/modules/noise/ @godotengine/core
+/modules/noise/doc_classes/ @godotengine/core @godotengine/documentation
+/modules/noise/icons/ @godotengine/core @godotengine/usability
+/modules/noise/tests/ @godotengine/core @godotengine/tests
+/modules/regex/ @godotengine/core
+/modules/regex/doc_classes/ @godotengine/core @godotengine/documentation
+/modules/regex/icons/ @godotengine/core @godotengine/usability
+/modules/regex/tests/ @godotengine/core @godotengine/tests
+/modules/zip/ @godotengine/core
+/modules/zip/doc_classes/ @godotengine/core @godotengine/documentation
# Platform
-/platform/android/ @godotengine/android
-/platform/ios/ @godotengine/ios
-/platform/linuxbsd/ @godotengine/linux-bsd
-/platform/macos/ @godotengine/macos
-/platform/web/ @godotengine/web
-/platform/windows/ @godotengine/windows
+/platform/android/ @godotengine/android
+/platform/android/doc_classes/ @godotengine/android @godotengine/documentation
+/platform/ios/ @godotengine/ios
+/platform/ios/doc_classes/ @godotengine/ios @godotengine/documentation
+/platform/linuxbsd/ @godotengine/linux-bsd
+/platform/linuxbsd/doc_classes/ @godotengine/linux-bsd @godotengine/documentation
+/platform/macos/ @godotengine/macos
+/platform/macos/doc_classes/ @godotengine/macos @godotengine/documentation
+/platform/web/ @godotengine/web
+/platform/web/doc_classes/ @godotengine/web @godotengine/documentation
+/platform/windows/ @godotengine/windows
+/platform/windows/doc_classes/ @godotengine/windows @godotengine/documentation
# Scene
-/scene/2d/ @godotengine/2d-nodes
-/scene/3d/ @godotengine/3d-nodes
-/scene/animation/ @godotengine/animation
-/scene/audio/ @godotengine/audio
-/scene/debugger/ @godotengine/debugger
-/scene/gui/ @godotengine/gui-nodes
-/scene/main/ @godotengine/core
-/scene/resources/font.* @godotengine/gui-nodes
-/scene/resources/text_line.* @godotengine/gui-nodes
-/scene/resources/text_paragraph.* @godotengine/gui-nodes
-/scene/resources/visual_shader*.* @godotengine/shaders
-/scene/theme/ @godotengine/gui-nodes
+/scene/2d/ @godotengine/2d-nodes
+/scene/2d/physics/ @godotengine/2d-nodes @godotengine/physics
+/scene/3d/ @godotengine/3d-nodes
+/scene/3d/physics/ @godotengine/3d-nodes @godotengine/physics
+/scene/animation/ @godotengine/animation
+/scene/audio/ @godotengine/audio
+/scene/debugger/ @godotengine/debugger
+/scene/gui/ @godotengine/gui-nodes
+/scene/main/ @godotengine/core
+/scene/resources/font.* @godotengine/gui-nodes
+/scene/resources/text_line.* @godotengine/gui-nodes
+/scene/resources/text_paragraph.* @godotengine/gui-nodes
+/scene/resources/visual_shader*.* @godotengine/shaders
+/scene/theme/ @godotengine/gui-nodes
+/scene/theme/icons/ @godotengine/gui-nodes @godotengine/usability
# Servers
-/servers/audio* @godotengine/audio
-/servers/camera* @godotengine/xr
-/servers/display_server.* @godotengine/_platforms
-/servers/navigation_server*.* @godotengine/navigation
-/servers/physics* @godotengine/physics
-/servers/rendering* @godotengine/rendering
-/servers/text_server.* @godotengine/gui-nodes
-/servers/xr* @godotengine/xr
+/servers/audio* @godotengine/audio
+/servers/camera* @godotengine/xr
+/servers/display_server.* @godotengine/_platforms
+/servers/navigation_server*.* @godotengine/navigation
+/servers/physics* @godotengine/physics
+/servers/rendering* @godotengine/rendering
+/servers/text_server.* @godotengine/gui-nodes
+/servers/xr* @godotengine/xr
# Tests
-/tests/ @godotengine/tests
+/tests/ @godotengine/tests
# Thirdparty
-/thirdparty/ @godotengine/buildsystem
+/thirdparty/ @godotengine/buildsystem
+
+# Buildsystem (After everything to catch all)
+
+*.py @godotengine/buildsystem
+SConstruct @godotengine/buildsystem
+SCsub @godotengine/buildsystem
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 7ffae10a2aab..6803d9afd52b 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -1,72 +1,72 @@
name: Bug report
description: Report a bug in Godot
-body:
-- type: markdown
- attributes:
- value: |
- When reporting bugs, please follow the guidelines in this template. This helps identify the problem precisely and thus enables contributors to fix it faster.
- - Write a descriptive issue title above.
- - The golden rule is to **always open *one* issue for *one* bug**. If you notice several bugs and want to report them, make sure to create one new issue for each of them.
- - Search [open](https://github.com/godotengine/godot/issues) and [closed](https://github.com/godotengine/godot/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported. If you don't find a relevant match or if you're unsure, don't hesitate to **open a new issue**. The bugsquad will handle it from there if it's a duplicate.
- - Verify that you are using a [supported Godot version](https://docs.godotengine.org/en/latest/about/release_policy.html). Please always check if your issue is reproducible in the latest version – it may already have been fixed!
- - If you use a custom build, please test if your issue is reproducible in official builds too. Likewise if you use any C++ modules, GDExtensions, or editor plugins, you should check if the bug is reproducible in a project without these.
+body:
+ - type: markdown
+ attributes:
+ value: |
+ When reporting bugs, please follow the guidelines in this template. This helps identify the problem precisely and thus enables contributors to fix it faster.
+ - Write a descriptive issue title above.
+ - The golden rule is to **always open *one* issue for *one* bug**. If you notice several bugs and want to report them, make sure to create one new issue for each of them.
+ - Search [open](https://github.com/godotengine/godot/issues) and [closed](https://github.com/godotengine/godot/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported. If you don't find a relevant match or if you're unsure, don't hesitate to **open a new issue**. The bugsquad will handle it from there if it's a duplicate.
+ - Verify that you are using a [supported Godot version](https://docs.godotengine.org/en/latest/about/release_policy.html). Please always check if your issue is reproducible in the latest version – it may already have been fixed!
+ - If you use a custom build, please test if your issue is reproducible in official builds too. Likewise if you use any C++ modules, GDExtensions, or editor plugins, you should check if the bug is reproducible in a project without these.
-- type: textarea
- attributes:
- label: Tested versions
- description: |
- To properly fix a bug, we need to identify if the bug was recently introduced in the engine, or if it was always present.
- - Please specify the Godot version you found the issue in, including the **Git commit hash** if using a development or non-official build. The exact Godot version (including the commit hash) can be copied by clicking the version shown in the editor (bottom bar) or in the project manager (top bar).
- - If you can, **please test earlier Godot versions** (previous stable branch, and development snapshots of the current feature release) and, if applicable, newer versions (development snapshots for the next feature release). Mention whether the bug is reproducible or not in the versions you tested. You can find all Godot releases in our [download archive](https://godotengine.org/download/archive/).
- - The aim is for us to identify whether a bug is a **regression**, i.e. an issue that didn't exist in a previous version, but was introduced later on, breaking existing functionality. For example, if a bug is reproducible in 4.2.stable but not in 4.1.stable, we would like you to test intermediate 4.2 dev and beta snapshots to find which snapshot is the first one where the issue can be reproduced.
- placeholder: |
+ - type: textarea
+ attributes:
+ label: Tested versions
+ description: |
+ To properly fix a bug, we need to identify if the bug was recently introduced in the engine, or if it was always present.
+ - Please specify the Godot version you found the issue in, including the **Git commit hash** if using a development or non-official build. The exact Godot version (including the commit hash) can be copied by clicking the version shown in the editor (bottom bar) or in the project manager (top bar).
+ - If you can, **please test earlier Godot versions** (previous stable branch, and development snapshots of the current feature release) and, if applicable, newer versions (development snapshots for the next feature release). Mention whether the bug is reproducible or not in the versions you tested. You can find all Godot releases in our [download archive](https://godotengine.org/download/archive/).
+ - The aim is for us to identify whether a bug is a **regression**, i.e. an issue that didn't exist in a previous version, but was introduced later on, breaking existing functionality. For example, if a bug is reproducible in 4.2.stable but not in 4.1.stable, we would like you to test intermediate 4.2 dev and beta snapshots to find which snapshot is the first one where the issue can be reproduced.
+ placeholder: |
- - Reproducible in: 4.3.dev [d76c1d0e5], 4.2.stable, 4.2.dev5 and later 4.2 snapshots.
- - Not reproducible in: 4.1.3.stable, 4.2.dev4 and earlier 4.2 snapshots.
- validations:
- required: true
+ - Reproducible in: 4.3.dev [d76c1d0e5], 4.2.stable, 4.2.dev5 and later 4.2 snapshots.
+ - Not reproducible in: 4.1.3.stable, 4.2.dev4 and earlier 4.2 snapshots.
+ validations:
+ required: true
-- type: input
- attributes:
- label: System information
- description: |
- - Specify the OS version, and when relevant hardware information.
- - For issues that are likely OS-specific and/or graphics-related, please specify the CPU model and architecture.
- - For graphics-related issues, specify the GPU model, driver version, and the rendering backend (GLES2, GLES3, Vulkan).
- - **Bug reports not including the required information may be closed at the maintainers' discretion.** If in doubt, always include all the requested information; it's better to include too much information than not enough information.
- - **Starting from Godot 4.1, you can copy this information to your clipboard by using *Help > Copy System Info* at the top of the editor window.**
- placeholder: Windows 10 - Godot v4.0.3.stable - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 970 (nvidia, 510.85.02) - Intel Core i7-10700KF CPU @ 3.80GHz (16 Threads)
- validations:
- required: true
+ - type: input
+ attributes:
+ label: System information
+ description: |
+ - Specify the OS version, and when relevant hardware information.
+ - For issues that are likely OS-specific and/or graphics-related, please specify the CPU model and architecture.
+ - For graphics-related issues, specify the GPU model, driver version, and the rendering backend (GLES2, GLES3, Vulkan).
+ - **Bug reports not including the required information may be closed at the maintainers' discretion.** If in doubt, always include all the requested information; it's better to include too much information than not enough information.
+ - **Starting from Godot 4.1, you can copy this information to your clipboard by using *Help > Copy System Info* at the top of the editor window.**
+ placeholder: Windows 10 - Godot v4.0.3.stable - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 970 (nvidia, 510.85.02) - Intel Core i7-10700KF CPU @ 3.80GHz (16 Threads)
+ validations:
+ required: true
-- type: textarea
- attributes:
- label: Issue description
- description: |
- Describe your issue briefly. What doesn't work, and how do you expect it to work instead?
- You can include images or videos with drag and drop, and format code blocks or logs with \`\`\`
tags, on separate lines before and after the text. (Use \`\`\`gdscript
to add GDScript syntax highlighting.)
- Please do not add code examples or error messages as screenshots, but as text, this helps searching for issues and testing the code. If you are reporting a bug in the editor interface, like the script editor, please provide both a screenshot *and* the text of the code to help with testing.
- validations:
- required: true
+ - type: textarea
+ attributes:
+ label: Issue description
+ description: |
+ Describe your issue briefly. What doesn't work, and how do you expect it to work instead?
+ You can include images or videos with drag and drop, and format code blocks or logs with \`\`\`
tags, on separate lines before and after the text. (Use \`\`\`gdscript
to add GDScript syntax highlighting.)
+ Please do not add code examples or error messages as screenshots, but as text, this helps searching for issues and testing the code. If you are reporting a bug in the editor interface, like the script editor, please provide both a screenshot *and* the text of the code to help with testing.
+ validations:
+ required: true
-- type: textarea
- attributes:
- label: Steps to reproduce
- description: |
- List of steps or sample code that reproduces the issue. Having reproducible issues is a prerequisite for contributors to be able to solve them.
- If you include a minimal reproduction project below, you can detail how to use it here.
- validations:
- required: true
+ - type: textarea
+ attributes:
+ label: Steps to reproduce
+ description: |
+ List of steps or sample code that reproduces the issue. Having reproducible issues is a prerequisite for contributors to be able to solve them.
+ If you include a minimal reproduction project below, you can detail how to use it here.
+ validations:
+ required: true
-- type: textarea
- attributes:
- label: Minimal reproduction project (MRP)
- description: |
- - A small Godot project which reproduces the issue, with no unnecessary files included. Be sure to not include the `.godot` folder in the archive (but keep `project.godot`).
- - Having an MRP is very important for contributors to be able to reproduce the bug in the same way that you are experiencing it. When testing a potential fix for the issue, contributors will use the MRP to validate that the fix is working as intended.
- - If the reproduction steps are not project dependent (e.g. the bug is visible in a brand new project), you can write "N/A" in the field.
- - Drag and drop a ZIP archive to upload it (max 10 MB). **Do not select another field until the project is done uploading.**
- - **Note for C# users:** If your issue is *not* C#-specific, please upload a minimal reproduction project written in GDScript. This will make it easier for contributors to reproduce the issue locally as not everyone has a .NET setup available.
- validations:
- required: true
+ - type: textarea
+ attributes:
+ label: Minimal reproduction project (MRP)
+ description: |
+ - A small Godot project which reproduces the issue, with no unnecessary files included. Be sure to not include the `.godot` folder in the archive (but keep `project.godot`).
+ - Having an MRP is very important for contributors to be able to reproduce the bug in the same way that you are experiencing it. When testing a potential fix for the issue, contributors will use the MRP to validate that the fix is working as intended.
+ - If the reproduction steps are not project dependent (e.g. the bug is visible in a brand new project), you can write "N/A" in the field.
+ - Drag and drop a ZIP archive to upload it (max 10 MB). **Do not select another field until the project is done uploading.**
+ - **Note for C# users:** If your issue is *not* C#-specific, please upload a minimal reproduction project written in GDScript. This will make it easier for contributors to reproduce the issue locally as not everyone has a .NET setup available.
+ validations:
+ required: true
diff --git a/.github/actions/download-artifact/action.yml b/.github/actions/download-artifact/action.yml
index 534b3251c5c0..c2a3d777c41b 100644
--- a/.github/actions/download-artifact/action.yml
+++ b/.github/actions/download-artifact/action.yml
@@ -1,15 +1,17 @@
name: Download Godot artifact
description: Download the Godot artifact.
+
inputs:
name:
description: The artifact name.
- default: "${{ github.job }}"
+ default: ${{ github.job }}
path:
description: The path to download and extract to.
required: true
- default: "./"
+ default: ./
+
runs:
- using: "composite"
+ using: composite
steps:
- name: Download Godot Artifact
uses: actions/download-artifact@v4
diff --git a/.github/actions/godot-api-dump/action.yml b/.github/actions/godot-api-dump/action.yml
index 773068866170..dee70298c4b5 100644
--- a/.github/actions/godot-api-dump/action.yml
+++ b/.github/actions/godot-api-dump/action.yml
@@ -1,11 +1,13 @@
name: Dump Godot API
description: Dump Godot API for GDExtension
+
inputs:
bin:
description: The path to the Godot executable
required: true
+
runs:
- using: "composite"
+ using: composite
steps:
# Dump GDExtension interface and API
- name: Dump GDExtension interface and API for godot-cpp build
@@ -19,5 +21,5 @@ runs:
- name: Upload API dump
uses: ./.github/actions/upload-artifact
with:
- name: 'godot-api-dump'
- path: './godot-api/*'
+ name: godot-api-dump
+ path: ./godot-api/*
diff --git a/.github/actions/godot-build/action.yml b/.github/actions/godot-build/action.yml
index bf29b7e430ad..93d6f076b728 100644
--- a/.github/actions/godot-build/action.yml
+++ b/.github/actions/godot-build/action.yml
@@ -1,34 +1,39 @@
name: Build Godot
description: Build Godot with the provided options.
+
inputs:
target:
description: Build target (editor, template_release, template_debug).
- default: "editor"
+ default: editor
tests:
description: Unit tests.
default: false
+ required: false
platform:
description: Target platform.
required: false
sconsflags:
- default: ""
+ description: Additional SCons flags.
+ default: ''
+ required: false
scons-cache:
description: The SCons cache path.
- default: "${{ github.workspace }}/.scons-cache/"
+ default: ${{ github.workspace }}/.scons-cache/
scons-cache-limit:
description: The SCons cache size limit.
# actions/cache has 10 GiB limit, and GitHub runners have a 14 GiB disk.
# Limit to 7 GiB to avoid having the extracted cache fill the disk.
default: 7168
+
runs:
- using: "composite"
+ using: composite
steps:
- - name: Scons Build
+ - name: SCons Build
shell: sh
env:
- SCONSFLAGS: ${{ inputs.sconsflags }}
- SCONS_CACHE: ${{ inputs.scons-cache }}
- SCONS_CACHE_LIMIT: ${{ inputs.scons-cache-limit }}
+ SCONSFLAGS: ${{ inputs.sconsflags }}
+ SCONS_CACHE: ${{ inputs.scons-cache }}
+ SCONS_CACHE_LIMIT: ${{ inputs.scons-cache-limit }}
run: |
echo "Building with flags:" platform=${{ inputs.platform }} target=${{ inputs.target }} tests=${{ inputs.tests }} ${{ env.SCONSFLAGS }}
diff --git a/.github/actions/godot-cache-restore/action.yml b/.github/actions/godot-cache-restore/action.yml
index eb955affef97..7abec20a2821 100644
--- a/.github/actions/godot-cache-restore/action.yml
+++ b/.github/actions/godot-cache-restore/action.yml
@@ -3,18 +3,19 @@ description: Restore Godot build cache.
inputs:
cache-name:
description: The cache base name (job name by default).
- default: "${{github.job}}"
+ default: ${{ github.job }}
scons-cache:
description: The SCons cache path.
- default: "${{github.workspace}}/.scons-cache/"
+ default: ${{ github.workspace }}/.scons-cache/
+
runs:
- using: "composite"
+ using: composite
steps:
- name: Restore SCons cache directory
uses: actions/cache/restore@v4
with:
- path: ${{inputs.scons-cache}}
- key: ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
+ path: ${{ inputs.scons-cache }}
+ key: ${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}-${{ github.ref }}-${{ github.sha }}
# We try to match an existing cache to restore from it. Each potential key is checked against
# all existing caches as a prefix. E.g. 'linux-template-minimal' would match any cache that
@@ -28,7 +29,7 @@ runs:
# 4. A partial match for the same base branch only (not ideal, matches any PR with the same base branch).
restore-keys: |
- ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
- ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
- ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-refs/heads/${{env.GODOT_BASE_BRANCH}}
- ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}
+ ${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}-${{ github.ref }}-${{ github.sha }}
+ ${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}-${{ github.ref }}
+ ${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}-refs/heads/${{ env.GODOT_BASE_BRANCH }}
+ ${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}
diff --git a/.github/actions/godot-cache-save/action.yml b/.github/actions/godot-cache-save/action.yml
index b7cbf91f94ff..df877cec67bd 100644
--- a/.github/actions/godot-cache-save/action.yml
+++ b/.github/actions/godot-cache-save/action.yml
@@ -3,15 +3,16 @@ description: Save Godot build cache.
inputs:
cache-name:
description: The cache base name (job name by default).
- default: "${{github.job}}"
+ default: ${{ github.job }}
scons-cache:
description: The SCons cache path.
- default: "${{github.workspace}}/.scons-cache/"
+ default: ${{ github.workspace }}/.scons-cache/
+
runs:
- using: "composite"
+ using: composite
steps:
- name: Save SCons cache directory
uses: actions/cache/save@v4
with:
- path: ${{inputs.scons-cache}}
- key: ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
+ path: ${{ inputs.scons-cache }}
+ key: ${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}-${{ github.ref }}-${{ github.sha }}
diff --git a/.github/actions/godot-converter-test/action.yml b/.github/actions/godot-converter-test/action.yml
index 919a76e69375..ffd558d98b1a 100644
--- a/.github/actions/godot-converter-test/action.yml
+++ b/.github/actions/godot-converter-test/action.yml
@@ -1,11 +1,13 @@
name: Test Godot project converter
description: Test the Godot project converter.
+
inputs:
bin:
description: The path to the Godot executable
required: true
+
runs:
- using: "composite"
+ using: composite
steps:
- name: Test 3-to-4 conversion
shell: sh
diff --git a/.github/actions/godot-deps/action.yml b/.github/actions/godot-deps/action.yml
index 99404657c63f..eb9bdef1e774 100644
--- a/.github/actions/godot-deps/action.yml
+++ b/.github/actions/godot-deps/action.yml
@@ -1,17 +1,19 @@
name: Setup Python and SCons
description: Setup Python, install the pip version of SCons.
+
inputs:
python-version:
description: The Python version to use.
- default: "3.x"
+ default: 3.x
python-arch:
description: The Python architecture.
- default: "x64"
+ default: x64
scons-version:
description: The SCons version to use.
- default: "4.8.0"
+ default: 4.8.0
+
runs:
- using: "composite"
+ using: composite
steps:
- name: Set up Python 3.x
uses: actions/setup-python@v5
diff --git a/.github/actions/godot-project-test/action.yml b/.github/actions/godot-project-test/action.yml
index fd8c024a37df..36448cd50aec 100644
--- a/.github/actions/godot-project-test/action.yml
+++ b/.github/actions/godot-project-test/action.yml
@@ -1,11 +1,13 @@
name: Test Godot project
description: Run the test Godot project.
+
inputs:
bin:
description: The path to the Godot executable
required: true
+
runs:
- using: "composite"
+ using: composite
steps:
# Download and extract zip archive with project, folder is renamed to be able to easy change used project
- name: Download test project
diff --git a/.github/actions/upload-artifact/action.yml b/.github/actions/upload-artifact/action.yml
index 80c680103d30..8524afdf593a 100644
--- a/.github/actions/upload-artifact/action.yml
+++ b/.github/actions/upload-artifact/action.yml
@@ -1,15 +1,17 @@
name: Upload Godot artifact
description: Upload the Godot artifact.
+
inputs:
name:
description: The artifact name.
- default: "${{ github.job }}"
+ default: ${{ github.job }}
path:
description: The path to upload.
required: true
- default: "bin/*"
+ default: bin/*
+
runs:
- using: "composite"
+ using: composite
steps:
- name: Upload Godot Artifact
uses: actions/upload-artifact@v4
diff --git a/.github/workflows/android_builds.yml b/.github/workflows/android_builds.yml
index ee75d532828d..7ff5502137c3 100644
--- a/.github/workflows/android_builds.yml
+++ b/.github/workflows/android_builds.yml
@@ -6,15 +6,15 @@ on:
env:
# Used for the cache key. Add version suffix to force clean build.
GODOT_BASE_BRANCH: master
- SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no module_text_server_fb_enabled=yes
+ SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no module_text_server_fb_enabled=yes strict_checks=yes
concurrency:
- group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-android
+ group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-android
cancel-in-progress: true
jobs:
build-android:
- runs-on: "ubuntu-20.04"
+ runs-on: ubuntu-20.04
name: ${{ matrix.name }}
strategy:
fail-fast: false
@@ -39,7 +39,8 @@ jobs:
sconsflags: arch=arm64
steps:
- - uses: actions/checkout@v4
+ - name: Checkout
+ uses: actions/checkout@v4
with:
submodules: recursive
@@ -85,6 +86,7 @@ jobs:
run: |
cd platform/android/java
./gradlew generateGodotEditor
+ ./gradlew generateGodotHorizonOSEditor
cd ../../..
ls -l bin/android_editor_builds/
diff --git a/.github/workflows/godot_cpp_test.yml b/.github/workflows/godot_cpp_test.yml
index 57114dacfc20..e26c109d75c4 100644
--- a/.github/workflows/godot_cpp_test.yml
+++ b/.github/workflows/godot_cpp_test.yml
@@ -7,52 +7,58 @@ env:
# Used for the cache key. Add version suffix to force clean build.
GODOT_BASE_BRANCH: master
# Used for the godot-cpp checkout.
- GODOT_CPP_BRANCH: '4.2'
+ GODOT_CPP_BRANCH: 4.3
concurrency:
- group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-cpp-tests
+ group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-cpp-tests
cancel-in-progress: true
jobs:
godot-cpp-tests:
- runs-on: "ubuntu-20.04"
- name: "Build and test Godot CPP"
+ runs-on: ubuntu-20.04
+ name: Build and test Godot CPP
steps:
- - uses: actions/checkout@v4
+ - name: Checkout
+ uses: actions/checkout@v4
with:
- submodules: recursive
-
- - name: Setup Python and SCons
- uses: ./.github/actions/godot-deps
+ sparse-checkout: .github
- # Checkout godot-cpp
- name: Checkout godot-cpp
uses: actions/checkout@v4
with:
+ submodules: recursive
repository: godotengine/godot-cpp
ref: ${{ env.GODOT_CPP_BRANCH }}
- submodules: 'recursive'
- path: 'godot-cpp'
+ path: godot-cpp
+
+ - name: Setup Python and SCons
+ uses: ./.github/actions/godot-deps
+
+ - name: Setup GCC problem matcher
+ uses: ammaraskar/gcc-problem-matcher@master
- # Download generated API dump
- name: Download GDExtension interface and API dump
uses: ./.github/actions/download-artifact
with:
- name: 'godot-api-dump'
- path: './godot-api'
+ name: godot-api-dump
+ path: ./godot-cpp/gdextension
- # Extract and override existing files with generated files
- - name: Extract GDExtension interface and API dump
- run: |
- cp -f godot-api/gdextension_interface.h godot-cpp/gdextension/
- cp -f godot-api/extension_api.json godot-cpp/gdextension/
+ # TODO: Enable caching when godot-cpp has proper cache limiting.
- # TODO: Add caching to the SCons build and store it for CI via the godot-cache
- # action.
+ # - name: Restore Godot build cache
+ # uses: ./.github/actions/godot-cache-restore
+ # with:
+ # cache-name: godot-cpp
+ # continue-on-error: true
- # Build godot-cpp test extension
- name: Build godot-cpp test extension
- run: |
- cd godot-cpp/test
- scons target=template_debug dev_build=yes
- cd ../..
+ env: # Keep synced with godot-build.
+ SCONS_CACHE: ${{ github.workspace }}/.scons-cache/
+ SCONS_CACHE_LIMIT: 7168
+ run: scons --directory=./godot-cpp/test target=template_debug dev_build=yes verbose=yes
+
+ # - name: Save Godot build cache
+ # uses: ./.github/actions/godot-cache-save
+ # with:
+ # cache-name: godot-cpp
+ # continue-on-error: true
diff --git a/.github/workflows/ios_builds.yml b/.github/workflows/ios_builds.yml
index 8da6d1311e6e..8513429f9a63 100644
--- a/.github/workflows/ios_builds.yml
+++ b/.github/workflows/ios_builds.yml
@@ -6,19 +6,20 @@ on:
env:
# Used for the cache key. Add version suffix to force clean build.
GODOT_BASE_BRANCH: master
- SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no module_text_server_fb_enabled=yes
+ SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no module_text_server_fb_enabled=yes strict_checks=yes
concurrency:
- group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-ios
+ group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-ios
cancel-in-progress: true
jobs:
ios-template:
- runs-on: "macos-latest"
+ runs-on: macos-latest
name: Template (target=template_release)
steps:
- - uses: actions/checkout@v4
+ - name: Checkout
+ uses: actions/checkout@v4
with:
submodules: recursive
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index 10389dc9da0d..dc3d9f378624 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.github/workflows/linux_builds.yml
@@ -6,18 +6,18 @@ on:
env:
# Used for the cache key. Add version suffix to force clean build.
GODOT_BASE_BRANCH: master
- SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes
+ SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes strict_checks=yes
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
TSAN_OPTIONS: suppressions=misc/error_suppressions/tsan.txt
concurrency:
- group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-linux
+ group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-linux
cancel-in-progress: true
jobs:
build-linux:
- runs-on: "ubuntu-20.04"
+ runs-on: ubuntu-20.04
name: ${{ matrix.name }}
strategy:
fail-fast: false
@@ -27,7 +27,7 @@ jobs:
cache-name: linux-editor-mono
target: editor
sconsflags: module_mono_enabled=yes
- bin: "./bin/godot.linuxbsd.editor.x86_64.mono"
+ bin: ./bin/godot.linuxbsd.editor.x86_64.mono
build-mono: true
tests: false # Disabled due freeze caused by mix Mono build and CI
doc-test: true
@@ -40,7 +40,7 @@ jobs:
target: editor
# Debug symbols disabled as they're huge on this build and we hit the 14 GB limit for runners.
sconsflags: dev_build=yes scu_build=yes debug_symbols=no precision=double use_asan=yes use_ubsan=yes linker=gold
- bin: "./bin/godot.linuxbsd.editor.dev.double.x86_64.san"
+ bin: ./bin/godot.linuxbsd.editor.dev.double.x86_64.san
build-mono: false
tests: true
proj-test: true
@@ -53,7 +53,7 @@ jobs:
cache-name: linux-editor-llvm-sanitizers
target: editor
sconsflags: dev_build=yes use_asan=yes use_ubsan=yes use_llvm=yes linker=lld
- bin: "./bin/godot.linuxbsd.editor.dev.x86_64.llvm.san"
+ bin: ./bin/godot.linuxbsd.editor.dev.x86_64.llvm.san
build-mono: false
tests: true
# Skip 2GiB artifact speeding up action.
@@ -66,36 +66,37 @@ jobs:
target: editor
tests: true
sconsflags: dev_build=yes use_tsan=yes use_llvm=yes linker=lld
- bin: "./bin/godot.linuxbsd.editor.dev.x86_64.llvm.san"
+ bin: ./bin/godot.linuxbsd.editor.dev.x86_64.llvm.san
build-mono: false
# Skip 2GiB artifact speeding up action.
artifact: false
- - name: Template w/ Mono (target=template_release)
+ - name: Template w/ Mono (target=template_release, tests=yes)
cache-name: linux-template-mono
target: template_release
- sconsflags: module_mono_enabled=yes tests=yes
- bin: "./bin/godot.linuxbsd.template_release.x86_64.mono"
+ sconsflags: module_mono_enabled=yes
+ bin: ./bin/godot.linuxbsd.template_release.x86_64.mono
build-mono: false
tests: true
artifact: true
- - name: Minimal template (target=template_release, everything disabled)
+ - name: Minimal template (target=template_release, tests=yes, everything disabled)
cache-name: linux-template-minimal
target: template_release
- sconsflags: modules_enabled_by_default=no disable_3d=yes disable_advanced_gui=yes deprecated=no minizip=no tests=yes
- bin: "./bin/godot.linuxbsd.template_release.x86_64"
+ sconsflags: modules_enabled_by_default=no disable_3d=yes disable_advanced_gui=yes deprecated=no minizip=no
+ bin: ./bin/godot.linuxbsd.template_release.x86_64
tests: true
artifact: true
steps:
- - uses: actions/checkout@v4
+ - name: Checkout
+ uses: actions/checkout@v4
with:
submodules: recursive
# Need newer mesa for lavapipe to work properly.
- name: Linux dependencies for tests
- if: ${{ matrix.proj-test }}
+ if: matrix.proj-test
run: |
sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EB8B81E14DA65431D7504EA8F63F0F2B90935439
@@ -120,11 +121,11 @@ jobs:
continue-on-error: true
- name: Setup Python and SCons
- if: ${{ ! matrix.legacy-scons }}
+ if: '!matrix.legacy-scons'
uses: ./.github/actions/godot-deps
- name: Setup Python and SCons (legacy versions)
- if: ${{ matrix.legacy-scons }}
+ if: matrix.legacy-scons
uses: ./.github/actions/godot-deps
with:
# Sync with Ensure*Version in SConstruct.
@@ -149,48 +150,48 @@ jobs:
continue-on-error: true
- name: Generate C# glue
- if: ${{ matrix.build-mono }}
+ if: matrix.build-mono
run: |
${{ matrix.bin }} --headless --generate-mono-glue ./modules/mono/glue
- name: Build .NET solutions
- if: ${{ matrix.build-mono }}
+ if: matrix.build-mono
run: |
./modules/mono/build_scripts/build_assemblies.py --godot-output-dir=./bin --godot-platform=linuxbsd
- name: Prepare artifact
- if: ${{ matrix.artifact }}
+ if: matrix.artifact
run: |
strip bin/godot.*
chmod +x bin/godot.*
- name: Upload artifact
uses: ./.github/actions/upload-artifact
- if: ${{ matrix.artifact }}
+ if: matrix.artifact
with:
name: ${{ matrix.cache-name }}
- name: Dump Godot API
uses: ./.github/actions/godot-api-dump
- if: ${{ matrix.api-dump }}
+ if: matrix.api-dump
with:
bin: ${{ matrix.bin }}
- name: Unit tests
- if: ${{ matrix.tests }}
+ if: matrix.tests
run: |
${{ matrix.bin }} --version
${{ matrix.bin }} --help
${{ matrix.bin }} --headless --test --force-colors
- name: .NET source generators tests
- if: ${{ matrix.build-mono }}
+ if: matrix.build-mono
run: |
dotnet test modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests
# Check class reference
- name: Check for class reference updates
- if: ${{ matrix.doc-test }}
+ if: matrix.doc-test
run: |
echo "Running --doctool to see if this changes the public API without updating the documentation."
echo -e "If a diff is shown, it means that your code/doc changes are incomplete and you should update the class reference with --doctool.\n\n"
@@ -199,20 +200,20 @@ jobs:
# Check API backwards compatibility
- name: Check for GDExtension compatibility
- if: ${{ matrix.api-compat }}
+ if: matrix.api-compat
run: |
./misc/scripts/validate_extension_api.sh "${{ matrix.bin }}"
# Download and run the test project
- name: Test Godot project
uses: ./.github/actions/godot-project-test
- if: ${{ matrix.proj-test }}
+ if: matrix.proj-test
with:
bin: ${{ matrix.bin }}
# Test the project converter
- name: Test project converter
uses: ./.github/actions/godot-converter-test
- if: ${{ matrix.proj-conv }}
+ if: matrix.proj-conv
with:
bin: ${{ matrix.bin }}
diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml
index 4db7462b3a85..fcf4b00afb18 100644
--- a/.github/workflows/macos_builds.yml
+++ b/.github/workflows/macos_builds.yml
@@ -6,15 +6,15 @@ on:
env:
# Used for the cache key. Add version suffix to force clean build.
GODOT_BASE_BRANCH: master
- SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes
+ SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes strict_checks=yes
concurrency:
- group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-macos
+ group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-macos
cancel-in-progress: true
jobs:
build-macos:
- runs-on: "macos-latest"
+ runs-on: macos-latest
name: ${{ matrix.name }}
strategy:
fail-fast: false
@@ -24,17 +24,18 @@ jobs:
cache-name: macos-editor
target: editor
tests: true
- bin: "./bin/godot.macos.editor.universal"
+ bin: ./bin/godot.macos.editor.universal
- - name: Template (target=template_release)
+ - name: Template (target=template_release, tests=yes)
cache-name: macos-template
target: template_release
tests: true
- sconsflags: debug_symbols=no tests=yes
- bin: "./bin/godot.macos.template_release.universal"
+ sconsflags: debug_symbols=no
+ bin: ./bin/godot.macos.template_release.universal
steps:
- - uses: actions/checkout@v4
+ - name: Checkout
+ uses: actions/checkout@v4
with:
submodules: recursive
@@ -86,7 +87,7 @@ jobs:
name: ${{ matrix.cache-name }}
- name: Unit tests
- if: ${{ matrix.tests }}
+ if: matrix.tests
run: |
${{ matrix.bin }} --version
${{ matrix.bin }} --help
diff --git a/.github/workflows/runner.yml b/.github/workflows/runner.yml
index 34b6af43074d..fd5e74b914ad 100644
--- a/.github/workflows/runner.yml
+++ b/.github/workflows/runner.yml
@@ -1,52 +1,46 @@
name: 🔗 GHA
-on: [push, pull_request]
+on: [push, pull_request, merge_group]
concurrency:
- group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-runner
+ group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-runner
cancel-in-progress: true
jobs:
# First stage: Only static checks, fast and prevent expensive builds from running.
static-checks:
- if: ${{ vars.DISABLE_GODOT_CI == '' }}
+ if: '!vars.DISABLE_GODOT_CI'
name: 📊 Static checks
uses: ./.github/workflows/static_checks.yml
# Second stage: Run all the builds and some of the tests.
android-build:
- if: ${{ vars.DISABLE_GODOT_CI == '' }}
name: 🤖 Android
needs: static-checks
uses: ./.github/workflows/android_builds.yml
ios-build:
- if: ${{ vars.DISABLE_GODOT_CI == '' }}
name: 🍏 iOS
needs: static-checks
uses: ./.github/workflows/ios_builds.yml
linux-build:
- if: ${{ vars.DISABLE_GODOT_CI == '' }}
name: 🐧 Linux
needs: static-checks
uses: ./.github/workflows/linux_builds.yml
macos-build:
- if: ${{ vars.DISABLE_GODOT_CI == '' }}
name: 🍎 macOS
needs: static-checks
uses: ./.github/workflows/macos_builds.yml
windows-build:
- if: ${{ vars.DISABLE_GODOT_CI == '' }}
name: 🏁 Windows
needs: static-checks
uses: ./.github/workflows/windows_builds.yml
web-build:
- if: ${{ vars.DISABLE_GODOT_CI == '' }}
name: 🌐 Web
needs: static-checks
uses: ./.github/workflows/web_builds.yml
@@ -56,7 +50,6 @@ jobs:
# Can be turned off for PRs that intentionally break compat with godot-cpp,
# until both the upstream PR and the matching godot-cpp changes are merged.
godot-cpp-test:
- if: ${{ vars.DISABLE_GODOT_CI == '' }}
name: 🪲 Godot CPP
# This can be changed to depend on another platform, if we decide to use it for
# godot-cpp instead. Make sure to move the .github/actions/godot-api-dump step
diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml
index 9a8a4a8f1912..ff102a06cc57 100644
--- a/.github/workflows/static_checks.yml
+++ b/.github/workflows/static_checks.yml
@@ -3,7 +3,7 @@ on:
workflow_call:
concurrency:
- group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-static
+ group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-static
cancel-in-progress: true
jobs:
@@ -48,7 +48,7 @@ jobs:
- name: Style checks via pre-commit
uses: pre-commit/action@v3.0.1
with:
- extra_args: --verbose --files ${{ env.CHANGED_FILES }}
+ extra_args: --files ${{ env.CHANGED_FILES }}
- name: Python builders checks via pytest
run: |
@@ -57,3 +57,7 @@ jobs:
- name: Class reference schema checks
run: |
xmllint --noout --schema doc/class.xsd doc/classes/*.xml modules/*/doc_classes/*.xml platform/*/doc_classes/*.xml
+
+ - name: Run C compiler on `gdextension_interface.h`
+ run: |
+ gcc -c core/extension/gdextension_interface.h
diff --git a/.github/workflows/web_builds.yml b/.github/workflows/web_builds.yml
index d3a6b5b8b828..8e30c99fbc65 100644
--- a/.github/workflows/web_builds.yml
+++ b/.github/workflows/web_builds.yml
@@ -6,17 +6,17 @@ on:
env:
# Used for the cache key. Add version suffix to force clean build.
GODOT_BASE_BRANCH: master
- SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no use_closure_compiler=yes
+ SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no use_closure_compiler=yes strict_checks=yes
EM_VERSION: 3.1.64
- EM_CACHE_FOLDER: "emsdk-cache"
+ EM_CACHE_FOLDER: emsdk-cache
concurrency:
- group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-web
+ group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-web
cancel-in-progress: true
jobs:
web-template:
- runs-on: "ubuntu-22.04"
+ runs-on: ubuntu-22.04
name: ${{ matrix.name }}
strategy:
fail-fast: false
@@ -37,16 +37,17 @@ jobs:
artifact: true
steps:
- - uses: actions/checkout@v4
+ - name: Checkout
+ uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Emscripten latest
uses: mymindstorm/setup-emsdk@v14
with:
- version: ${{env.EM_VERSION}}
- actions-cache-folder: ${{env.EM_CACHE_FOLDER}}
- cache-key: emsdk-${{ matrix.cache-name }}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
+ version: ${{ env.EM_VERSION }}
+ actions-cache-folder: ${{ env.EM_CACHE_FOLDER }}
+ cache-key: emsdk-${{ matrix.cache-name }}-${{ env.GODOT_BASE_BRANCH }}-${{ github.ref }}-${{ github.sha }}
- name: Verify Emscripten setup
run: |
@@ -77,6 +78,6 @@ jobs:
- name: Upload artifact
uses: ./.github/actions/upload-artifact
- if: ${{ matrix.artifact }}
+ if: matrix.artifact
with:
name: ${{ matrix.cache-name }}
diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml
index 90629204e6a9..95e3d4a553d1 100644
--- a/.github/workflows/windows_builds.yml
+++ b/.github/workflows/windows_builds.yml
@@ -7,17 +7,17 @@ on:
env:
# Used for the cache key. Add version suffix to force clean build.
GODOT_BASE_BRANCH: master
- SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes d3d12=yes "angle_libs=${{github.workspace}}/"
+ SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes d3d12=yes strict_checks=yes "angle_libs=${{ github.workspace }}/"
SCONS_CACHE_MSVC_CONFIG: true
concurrency:
- group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-windows
+ group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-windows
cancel-in-progress: true
jobs:
build-windows:
# Windows 10 with latest image
- runs-on: "windows-latest"
+ runs-on: windows-latest
name: ${{ matrix.name }}
strategy:
fail-fast: false
@@ -29,17 +29,27 @@ jobs:
tests: true
# Skip debug symbols, they're way too big with MSVC.
sconsflags: debug_symbols=no vsproj=yes vsproj_gen_only=no windows_subsystem=console
- bin: "./bin/godot.windows.editor.x86_64.exe"
+ bin: ./bin/godot.windows.editor.x86_64.exe
+ artifact: true
- - name: Template (target=template_release)
+ - name: Editor w/ clang-cl (target=editor, tests=yes, use_llvm=yes)
+ cache-name: windows-editor-clang
+ target: editor
+ tests: true
+ sconsflags: debug_symbols=no windows_subsystem=console use_llvm=yes
+ bin: ./bin/godot.windows.editor.x86_64.llvm.exe
+
+ - name: Template (target=template_release, tests=yes)
cache-name: windows-template
target: template_release
tests: true
- sconsflags: debug_symbols=no tests=yes
- bin: "./bin/godot.windows.template_release.x86_64.console.exe"
+ sconsflags: debug_symbols=no
+ bin: ./bin/godot.windows.template_release.x86_64.console.exe
+ artifact: true
steps:
- - uses: actions/checkout@v4
+ - name: Checkout
+ uses: actions/checkout@v4
with:
submodules: recursive
@@ -64,7 +74,7 @@ jobs:
target: angle/angle.zip
- name: Extract pre-built ANGLE static libraries
- run: Expand-Archive -Force angle/angle.zip ${{github.workspace}}/
+ run: Expand-Archive -Force angle/angle.zip ${{ github.workspace }}/
- name: Setup MSVC problem matcher
uses: ammaraskar/msvc-problem-matcher@master
@@ -84,16 +94,18 @@ jobs:
continue-on-error: true
- name: Prepare artifact
+ if: ${{ matrix.artifact }}
run: |
Remove-Item bin/* -Include *.exp,*.lib,*.pdb -Force
- name: Upload artifact
+ if: ${{ matrix.artifact }}
uses: ./.github/actions/upload-artifact
with:
name: ${{ matrix.cache-name }}
- name: Unit tests
- if: ${{ matrix.tests }}
+ if: matrix.tests
run: |
${{ matrix.bin }} --version
${{ matrix.bin }} --help
diff --git a/.mailmap b/.mailmap
index 078d6c606b11..825e72bfd08f 100644
--- a/.mailmap
+++ b/.mailmap
@@ -84,6 +84,7 @@ Jean-Michel Bernard
Jérôme Gully
JFonS
jitspoe
+Johan Aires Rastén
Juan Linietsky
Juan Linietsky
Juan Linietsky
@@ -97,6 +98,7 @@ karroffel
Kelly Thomas
Kongfa Waroros
K. S. Ernest (iFire) Lee
+K. S. Ernest (iFire) Lee
kleonc <9283098+kleonc@users.noreply.github.com>
Leon Krause
Leon Krause
@@ -142,6 +144,7 @@ Pieter-Jan Briers
Pieter-Jan Briers
Poommetee Ketson
Przemysław Gołąb (n-pigeon)
+Radiant <69520693+RadiantUwU@users.noreply.github.com>
Rafał Mikrut
Rafał Mikrut
Ralf Hölzemer
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 46f29d0d5fc9..572eaf67912f 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -9,7 +9,7 @@ exclude: |
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
- rev: v17.0.6
+ rev: v19.1.0
hooks:
- id: clang-format
files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java|glsl)$
@@ -17,6 +17,7 @@ repos:
exclude: |
(?x)^(
tests/python_build/.*|
+ platform/android/java/editor/src/main/java/com/android/.*|
platform/android/java/lib/src/com/.*
)
@@ -30,6 +31,7 @@ repos:
exclude: |
(?x)^(
tests/python_build/.*|
+ platform/android/java/editor/src/main/java/com/android/.*|
platform/android/java/lib/src/com/.*
)
additional_dependencies: [clang-tidy==18.1.1]
@@ -37,14 +39,14 @@ repos:
stages: [manual] # Not automatically triggered, invoked via `pre-commit run --hook-stage manual clang-tidy`
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.4.4
+ rev: v0.6.6
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: v0.971
+ rev: v1.11.2
hooks:
- id: mypy
files: \.py$
@@ -54,6 +56,11 @@ repos:
rev: v2.3.0
hooks:
- id: codespell
+ exclude: |
+ (?x)^(
+ platform/android/java/editor/src/main/java/com/android/.*|
+ platform/android/java/lib/src/com/.*
+ )
additional_dependencies: [tomli]
### Requires Docker; look into alternative implementation.
@@ -89,16 +96,21 @@ repos:
language: node
entry: eslint
files: ^(platform/web/js/|modules/|misc/dist/html/).*\.(js|html)$
- args: [--fix, --no-warn-ignored, --no-config-lookup, --config, platform/web/eslint.config.cjs]
+ args:
+ - --fix
+ - --no-warn-ignored
+ - --no-config-lookup
+ - --config
+ - platform/web/eslint.config.cjs
additional_dependencies:
- '@eslint/js@^9.3.0'
- '@html-eslint/eslint-plugin@^0.24.1'
- '@html-eslint/parser@^0.24.1'
- '@stylistic/eslint-plugin@^2.1.0'
- - 'eslint@^9.3.0'
- - 'eslint-plugin-html@^8.1.1'
- - 'globals@^15.3.0'
- - 'espree@^10.0.1'
+ - eslint@^9.3.0
+ - eslint-plugin-html@^8.1.1
+ - globals@^15.3.0
+ - espree@^10.0.1
- id: jsdoc
name: jsdoc
@@ -116,7 +128,7 @@ repos:
- -d
- dry-run
pass_filenames: false
- additional_dependencies: ['jsdoc@^4.0.3']
+ additional_dependencies: [jsdoc@^4.0.3]
- id: svgo
name: svgo
@@ -124,7 +136,7 @@ repos:
entry: svgo
files: \.svg$
args: [--quiet, --config, misc/utility/svgo.config.mjs]
- additional_dependencies: ["svgo@3.3.2"]
+ additional_dependencies: [svgo@3.3.2]
- id: copyright-headers
name: copyright-headers
@@ -135,6 +147,7 @@ repos:
(?x)^(
core/math/bvh_.*\.inc$|
platform/(?!android|ios|linuxbsd|macos|web|windows)\w+/.*|
+ platform/android/java/editor/src/main/java/com/android/.*|
platform/android/java/lib/src/com/.*|
platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView\.java$|
platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper\.java$|
@@ -162,6 +175,7 @@ repos:
modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines\.gd$|
modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment\.notest\.gd$|
modules/gdscript/tests/scripts/parser/warnings/empty_file_newline\.notest\.gd$|
+ platform/android/java/editor/src/main/java/com/android/.*|
platform/android/java/lib/src/com/google/.*
)
@@ -170,7 +184,7 @@ repos:
language: python
entry: python misc/scripts/dotnet_format.py
types_or: [c#]
-
+#
# End of upstream Godot pre-commit hooks.
#
# Keep this separation to let downstream forks add their own hooks to this file,
diff --git a/AUTHORS.md b/AUTHORS.md
index 76d78dcefa29..b21270b3f700 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -1,11 +1,17 @@
# Godot Engine authors
Godot Engine is developed by a community of voluntary contributors who
-contribute code, bug reports, documentation, artwork, support, etc.
+contribute code, bug reports, documentation, translations, support, etc.,
+across multiple repositories.
It is impossible to list them all; nevertheless, this file aims at listing
-the developers who contributed significant patches to this MIT licensed
-source code. "Significant" is arbitrarily decided, but should be fair :)
+the developers who contributed significant improvements to the engine code.
+
+To keep the list curated, we use a threshold of a minimum of 11 commits.
+
+This file does not strictly reflect copyright ownership for the engine
+source code. For this, refer to the Git history to know which contributor
+wrote which part of the codebase.
GitHub usernames are indicated in parentheses, or as sole entry when no other
name is available.
@@ -25,8 +31,6 @@ name is available.
## Developers
-(in alphabetical order, with over 10 commits excluding merges)
-
Aaron Franke (aaronfranke)
Aaron Pagano (aaronp64)
Aaron Record (LightningAA)
@@ -38,6 +42,7 @@ name is available.
Alfred Reinold Baudisch (alfredbaudisch)
Alistair Leslie-Hughes (alesliehughes)
Alket Rexhepi (alketii)
+ Alvin Wong (alvinhochun)
Andrea Catania (AndreaCatania)
Andreia Gaita (shana)
Andrii Doroshenko (Xrayez)
@@ -46,11 +51,13 @@ name is available.
Angad Kambli (angad-k)
Anilforextra (AnilBK)
Anish Bhobe (KidRigger)
+ Anni Ryynänen (anniryynanen)
Anton Yabchinskiy (a12n)
Anutrix
Aren Villanueva (kurikaesu)
Ariel Manzur (punto-)
Arman Elgudzhyan (puchik)
+ Arseny Kapoulkine (zeux)
AThousandShips
aXu-AP
Bartłomiej T. Listwon (Listwon)
@@ -72,6 +79,7 @@ name is available.
Carter Anderson (cart)
ChibiDenDen
Chris Bradfield (cbscribe)
+ Chris Cranford (Naros)
Christian Kaiser (ckaiser)
Clay John (clayjohn)
ConteZero
@@ -86,7 +94,9 @@ name is available.
David Cambré (Gallilus)
David Sichma (DavidSichma)
David Snopek (dsnopek)
+ derammo
Dharkael (lupoDharkael)
+ Dirk Steinmetz (rsjtdrjgfuzkfg)
Dmitry Koteroff (Krakean)
Dmitry Maganov (vonagam)
Dominik Jasiński (dreamsComeTrue)
@@ -117,6 +127,7 @@ name is available.
Geequlim
George Marques (vnen)
Gerrit Großkopf (Grosskopf)
+ Giganzo
Gilles Roudiere (groud)
Gordon MacPherson (RevoluPowered)
Guilherme Felipe de C. G. da Silva (guilhermefelipecgs)
@@ -126,7 +137,6 @@ name is available.
Hein-Pieter van Braam-Stewart (hpvb)
Hendrik Brucker (Geometror)
Hilderin
- hilfazer
Hiroshi Ogawa (hi-ogawa)
HolonProduction
homer666
@@ -151,6 +161,7 @@ name is available.
Jia Jun Chai (SkyLucilfer)
jitspoe
Joan Fons Sanchez (JFonS)
+ Johan Aires Rastén (JohanAR)
Johan Manuel (29jm)
Johannes Witt (HaSa1002)
Jonathan Nicholl (jtnicholl)
@@ -163,11 +174,13 @@ name is available.
Jummit
Justo Delgado (mrcdk)
karroffel
+ Kassandra Pucher (PucklaJ)
Kelly Thomas (KellyThomas)
kleonc
Kongfa Waroros (gongpha)
Kostadin Damyanov (Max-Might)
K. S. Ernest (iFire) Lee (fire)
+ Kyle Eichlin (likeich)
lawnjelly
Leon Krause (leonkrause)
Liz Haas (27thLiz)
@@ -185,6 +198,7 @@ name is available.
Marcus Brummer (mbrlabs)
Marcus Elg (MCrafterzz)
Mariano Javier Suligoy (MarianoGnu)
+ Mario Liebisch (MarioLiebisch)
Mario Schlack (hurikhan)
Marios Staikopoulos (marstaik)
Marius Hanl (Maran23)
@@ -198,6 +212,7 @@ name is available.
Masoud BH (masoudbh3)
Mateo Kuruk Miccino (kuruk-mm)
Matias N. Goldberg (darksylinc)
+ Matthew Murphy (mashumafi)
Matthew (skyace65)
Matthias Hölzl (hoelzl)
Max Hilbrunner (mhilbrunner)
@@ -209,9 +224,10 @@ name is available.
MichiRecRoom (LikeLakers2)
Micky (Mickeon)
Mikael Hermansson (mihe)
+ Mika Viskari (miv391)
MinusKube
MJacred
- Morris "Tabor" Arroad (mortarroad)
+ Mounir Tohami (WhalesState)
mrezai
Muhammad Huri (CakHuri)
muiroc
@@ -234,6 +250,7 @@ name is available.
Patrick Dawson (pkdawson)
Patrick Exner (FlameLizard)
Patrick (firefly2442)
+ patwork
Paul Batty (Paulb23)
Paul Joannon (paulloz)
Paul Trojahn (ptrojahn)
@@ -245,6 +262,7 @@ name is available.
Pieter-Jan Briers (PJB3005)
Poommetee Ketson (Noshyaar)
Przemysław Gołąb (n-pigeon)
+ Radiant (RadiantUwU)
Rafael M. G. (rafallus)
Rafał Mikrut (qarmin)
Raffaele Picca (RPicster)
@@ -282,6 +300,7 @@ name is available.
Stanislav Labzyuk (DarkMessiah)
Stijn Hinlopen (hinlopen)
stmSi
+ Stuart Carnie (stuartcarnie)
Swarnim Arun (minraws)
TC (floppyhammer)
TechnoPorg
@@ -289,6 +308,7 @@ name is available.
Thakee Nathees (ThakeeNathees)
thebestnom
Theo Hallenius (TheoXD)
+ Thomas ten Cate (ttencate)
Timo Schwarzer (timoschwarzer)
Timothé Bonhoure (ajreckof)
Timo (toger5)
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 1e32c87c0167..6d227d6615a7 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -63,6 +63,44 @@ Copyright: 2011, Ole Kniemeyer, MAXON, www.maxon.net
2007-2014, Juan Linietsky, Ariel Manzur
License: Expat and Zlib
+Files: ./modules/godot_physics_2d/godot_joints_2d.cpp
+Comment: Chipmunk2D Joint Constraints
+Copyright: 2007, Scott Lembcke
+License: Expat
+
+Files: ./modules/godot_physics_3d/gjk_epa.cpp
+ ./modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp
+ ./modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.h
+ ./modules/godot_physics_3d/joints/godot_hinge_joint_3d.cpp
+ ./modules/godot_physics_3d/joints/godot_hinge_joint_3d_sw.h
+ ./modules/godot_physics_3d/joints/godot_jacobian_entry_3d_sw.h
+ ./modules/godot_physics_3d/joints/godot_pin_joint_3d.cpp
+ ./modules/godot_physics_3d/joints/godot_pin_joint_3d.h
+ ./modules/godot_physics_3d/joints/godot_slider_joint_3d.cpp
+ ./modules/godot_physics_3d/joints/godot_slider_joint_3d.h
+ ./modules/godot_physics_3d/godot_soft_body_3d.cpp
+ ./modules/godot_physics_3d/godot_soft_body_3d.h
+ ./modules/godot_physics_3d/godot_shape_3d.cpp
+ ./modules/godot_physics_3d/godot_shape_3d.h
+Comment: Bullet Continuous Collision Detection and Physics Library
+Copyright: 2003-2008, Erwin Coumans
+ 2014-present, Godot Engine contributors
+ 2007-2014, Juan Linietsky, Ariel Manzur
+License: Expat and Zlib
+
+Files: ./modules/godot_physics_3d/godot_collision_solver_3d_sat.cpp
+Comment: Open Dynamics Engine
+Copyright: 2001-2003, Russell L. Smith, Alen Ladavac, Nguyen Binh
+License: BSD-3-clause
+
+Files: ./modules/godot_physics_3d/joints/godot_cone_twist_joint_3d.cpp
+ ./modules/godot_physics_3d/joints/godot_cone_twist_joint_3d.h
+Comment: Bullet Continuous Collision Detection and Physics Library
+Copyright: 2007, Starbreeze Studios
+ 2014-present, Godot Engine contributors
+ 2007-2014, Juan Linietsky, Ariel Manzur
+License: Expat and Zlib
+
Files: ./modules/lightmapper_rd/lm_compute.glsl
Comment: Joint Non-Local Means (JNLM) denoiser
Copyright: 2020, Manuel Prandini
@@ -70,7 +108,8 @@ Copyright: 2020, Manuel Prandini
2007-2014, Juan Linietsky, Ariel Manzur
License: Expat
-Files: ./platform/android/java/lib/aidl/com/android/*
+Files: ./platform/android/java/editor/src/main/java/com/android/*
+ ./platform/android/java/lib/aidl/com/android/*
./platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml
./platform/android/java/lib/src/com/google/android/*
./platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java
@@ -92,44 +131,6 @@ Copyright: 2001, Robert Penner
2007-2014, Juan Linietsky, Ariel Manzur
License: Expat
-Files: ./servers/physics_2d/godot_joints_2d.cpp
-Comment: Chipmunk2D Joint Constraints
-Copyright: 2007, Scott Lembcke
-License: Expat
-
-Files: ./servers/physics_3d/collision_solver_3d_sat.cpp
-Comment: Open Dynamics Engine
-Copyright: 2001-2003, Russell L. Smith, Alen Ladavac, Nguyen Binh
-License: BSD-3-clause
-
-Files: ./servers/physics_3d/gjk_epa.cpp
- ./servers/physics_3d/joints/generic_6dof_joint_3d_sw.cpp
- ./servers/physics_3d/joints/generic_6dof_joint_3d_sw.h
- ./servers/physics_3d/joints/hinge_joint_3d_sw.cpp
- ./servers/physics_3d/joints/hinge_joint_3d_sw.h
- ./servers/physics_3d/joints/jacobian_entry_3d_sw.h
- ./servers/physics_3d/joints/pin_joint_3d_sw.cpp
- ./servers/physics_3d/joints/pin_joint_3d_sw.h
- ./servers/physics_3d/joints/slider_joint_3d_sw.cpp
- ./servers/physics_3d/joints/slider_joint_3d_sw.h
- ./servers/physics_3d/soft_body_3d_sw.cpp
- ./servers/physics_3d/soft_body_3d_sw.h
- ./servers/physics_3d/shape_3d_sw.cpp
- ./servers/physics_3d/shape_3d_sw.h
-Comment: Bullet Continuous Collision Detection and Physics Library
-Copyright: 2003-2008, Erwin Coumans
- 2014-present, Godot Engine contributors
- 2007-2014, Juan Linietsky, Ariel Manzur
-License: Expat and Zlib
-
-Files: ./servers/physics_3d/joints/cone_twist_joint_3d_sw.cpp
- ./servers/physics_3d/joints/cone_twist_joint_3d_sw.h
-Comment: Bullet Continuous Collision Detection and Physics Library
-Copyright: 2007, Starbreeze Studios
- 2014-present, Godot Engine contributors
- 2007-2014, Juan Linietsky, Ariel Manzur
-License: Expat and Zlib
-
Files: ./servers/rendering/renderer_rd/shaders/ss_effects_downsample.glsl
./servers/rendering/renderer_rd/shaders/ssao_blur.glsl
./servers/rendering/renderer_rd/shaders/ssao_importance_map.glsl
@@ -475,6 +476,11 @@ Comment: RVO2
Copyright: 2016, University of North Carolina at Chapel Hill
License: Apache-2.0
+Files: ./thirdparty/spirv-cross/
+Comment: SPIRV-Cross
+Copyright: 2015-2021, Arm Limited
+License: Apache-2.0 or Expat
+
Files: ./thirdparty/spirv-reflect/
Comment: SPIRV-Reflect
Copyright: 2017-2022, Google Inc.
diff --git a/DONORS.md b/DONORS.md
index 899c44306cd4..314a0f649637 100644
--- a/DONORS.md
+++ b/DONORS.md
@@ -12,12 +12,12 @@ generous deed immortalized in the next stable release of Godot Engine.
## Patrons
+ Khronos® Group
OSS Capital
- Re-Logic
## Platinum sponsors
- Google Play
+ Google Play
Ramatak
V-Sekai
W4 Games
@@ -25,53 +25,56 @@ generous deed immortalized in the next stable release of Godot Engine.
## Gold sponsors
Mega Crit
- Pirate Software
- Prehensile Tales
+ Pirate Software
+ Prehensile Tales
Robot Gentleman
## Silver sponsors
- Broken Rules
- Chasing Carrots
+ Broken Rules
+ Chasing Carrots
Copia Wealth Studios
Indoor Astronaut
- Load Complete
+ LoadComplete
Null
Orbital Knight
Playful Studios
+ Re-Logic
## Diamond members
Bippinbits
- Sealow
- And 5 anonymous donors
+ Sylv
+ And 3 anonymous donors
## Titanium members
- Adriaan de Jongh
- Anitya Space
+ Adriaan de Jongh
+ Anitya Space
Basically Games
- FDG Entertainment
- Game Dev Artisan
+ FDG Entertainment
+ Game Dev Artisan
Garry Newman
- Isaiah Smith
- Libretrend
+ Kenney
+ Libretrend
Life Art Studios
Lucid Silence Games
Matthew Campbell
PolyMars
- RPG in a Box
Razenpok
- Smirk Software
+ RPG in a Box
+ Smirk Software
+ Studio Sunshower
+ TrampolineTales
粟二华 (Su Erhua)
- And 6 anonymous donors
+ And 4 anonymous donors
## Platinum members
Andy Touch
BlockImperiumGames (BIG)
- Christoph Woinke
Christopher Shifflett
+ Christoph Woinke
Cody Bentley
Darrin Massena
Edward Flick
@@ -79,8 +82,8 @@ generous deed immortalized in the next stable release of Godot Engine.
HP van Braam
iCommitGames
Jonah Stich
+ Justo Delgado Baudí
katnamag
- Marek Belski
Matthew Ekenstedt
Memories in 8Bit
Mike King
@@ -97,13 +100,14 @@ generous deed immortalized in the next stable release of Godot Engine.
TigerJ
Violin Iliev
Vladimír Chvátil
- And 16 anonymous donors
+ And 13 anonymous donors
## Gold members
80px
afreytes
alMoo Games
+ alMoo Games
Alva Majo
Antti Vesanen
Asher Glick
@@ -119,6 +123,8 @@ generous deed immortalized in the next stable release of Godot Engine.
Bryce Dixon
c64cosmin
Carlo del Mundo
+ Carl van der Geest
+ Chocolate Software
Cindy Trieu
ClarkThyLord
Codex404
@@ -135,17 +141,21 @@ generous deed immortalized in the next stable release of Godot Engine.
dgehrig
dhanielk
Distorted Realities
+ Donkung
Dono
Don't You Know Who I Am? Inc.
Dustuu
+ Dylan P.
Edelweiss
Ends
Eren Ogrul
Eric Brand
Eric Phy
Faisal Al-Kubaisi (QatariGameDev)
+ Felix Adam
FeralBytes
Festzeltgaming.de
+ Frozen Fractal
Gaudipern
GlassBrick
Grau
@@ -153,6 +163,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Hayden Oliver
hiulit
Illyan
+ Immaculate Lift Studio
Ivan Tabashki
Jacob (HACKhalo2 Studios)
Jam
@@ -160,13 +171,12 @@ generous deed immortalized in the next stable release of Godot Engine.
Javier Roman
Jeff Hungerford
Jeronimo Schreyer
- Joel Martinez
Johannes Wuensch
John Gabriel
Jonas Yamazaki
+ Jonathan
José Canepa
Joshua Stelly
- Justin Sasso
Kalydi Balázs
KAR Games
Kiri "ExpiredPopsicle" Artemis
@@ -181,7 +191,9 @@ generous deed immortalized in the next stable release of Godot Engine.
m1n1ster
Manuel Requena
Mara Huldra
+ Marek Belski
Martin Šenkeřík
+ MHDante
Michael Gooch
Modus Ponens
Moshe Harris
@@ -190,6 +202,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Nassor Paulino da Silva
nezticle
Niklas Wahrman
+ Nitzan Bueno
Niwl Games
NotNet
Oathbringer
@@ -207,6 +220,7 @@ generous deed immortalized in the next stable release of Godot Engine.
re:thinc
Richard Ivánek
Rudi P
+ Sam Leathers
Samuel Judd
ScoreSpace
Shiny Shinken
@@ -238,12 +252,12 @@ generous deed immortalized in the next stable release of Godot Engine.
Zhu Li
zikes
嗯大爷
+ 潘彦圣
Alex Khayrullin
Algebrute
Andriy
Antanas Paskauskas
- anti666
Ari
Arisaka Mayuki
Arthur S. Muszynski
@@ -269,13 +283,11 @@ generous deed immortalized in the next stable release of Godot Engine.
Liam Smyth
LoparPanda
Martin Gulliksson
- Martin Soucek
Michael Dürwald
Michael Policastro
n00sh
Nicolás Monner Sans
Nikita Rotskov
- Nikola Whallon
Oliver Dick
Patrick Wuttke
Pete Goodwin
@@ -298,7 +310,6 @@ generous deed immortalized in the next stable release of Godot Engine.
VoidPointer
Yifan Lai
- Aaron Mayfield
Adam Carr
Adam Smeltzer
Adisibio
@@ -315,7 +326,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Ano Nim
Arch Toasty
Arda Erol
- A Really Tall Horse
Arturo Rosales
Ash K
Aubrey Falconer
@@ -343,6 +353,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Dakota Watkins
Daniele Tolomelli
Daniel Ramos
+ Daren Scot Wilson
Dave Jansen
Davesnothere
David Baker
@@ -361,12 +372,11 @@ generous deed immortalized in the next stable release of Godot Engine.
Eric Stokes
Eric Williams
Erkki Seppälä
- Ewan Holmes
- Felix Adam
Frank
Frying☆Pan
Game Endeavor
gamerminstrel
+ Garrett S
Gary Thomas
gebba
Greyson Richey
@@ -385,7 +395,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Jamie Massey
JARKKO PARVIAINEN
Jason Evans
- Joakim Askenbäck
Jonas
Jonas Arndt
Jonas Yamazaki
@@ -421,7 +430,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Martin Holas
Martin Liška
Martin Trbola
- Matěj Drábek
Mathieu
Matt Edwards
Maverick
@@ -441,7 +449,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Neofytos Chimonas
Nerdforge
Nerdyninja
- Nick Eldrenkamp
Nik Rudenko
Noel Billig
ozrk
@@ -450,7 +457,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Patrick Nafarrete
Paul Black
Paul Gieske
- Paul Mozet
Pete
Phoenix Jauregui
Pierre Caye
@@ -462,7 +468,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Raghava Kovvali
Ragnar Pettersson
Rammeow
- Rebecca H
Richard Hayes
Riley
RobotCritter
@@ -508,7 +513,7 @@ generous deed immortalized in the next stable release of Godot Engine.
ケルベロス
貴宏 小松
- And 181 anonymous donors
+ And 176 anonymous donors
## Silver and bronze donors
diff --git a/SConstruct b/SConstruct
index 0ae8f1a38729..63cff4fe1672 100644
--- a/SConstruct
+++ b/SConstruct
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
EnsureSConsVersion(3, 1, 2)
EnsurePythonVersion(3, 6)
@@ -218,10 +219,11 @@ opts.Add(BoolVariable("deprecated", "Enable compatibility code for deprecated an
opts.Add(EnumVariable("precision", "Set the floating-point precision level", "single", ("single", "double")))
opts.Add(BoolVariable("minizip", "Enable ZIP archive support using minizip", True))
opts.Add(BoolVariable("brotli", "Enable Brotli for decompresson and WOFF2 fonts support", True))
-opts.Add(BoolVariable("xaudio2", "Enable the XAudio2 audio driver", False))
+opts.Add(BoolVariable("xaudio2", "Enable the XAudio2 audio driver on supported platforms", False))
opts.Add(BoolVariable("vulkan", "Enable the vulkan rendering driver", True))
opts.Add(BoolVariable("opengl3", "Enable the OpenGL/GLES3 rendering driver", True))
-opts.Add(BoolVariable("d3d12", "Enable the Direct3D 12 rendering driver", False))
+opts.Add(BoolVariable("d3d12", "Enable the Direct3D 12 rendering driver on supported platforms", False))
+opts.Add(BoolVariable("metal", "Enable the Metal rendering driver on supported platforms (Apple arm64 only)", False))
opts.Add(BoolVariable("openxr", "Enable the OpenXR driver", True))
opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True))
opts.Add(BoolVariable("disable_exceptions", "Force disabling exception handling code", True))
@@ -229,11 +231,22 @@ opts.Add("custom_modules", "A list of comma-separated directory paths containing
opts.Add(BoolVariable("custom_modules_recursive", "Detect custom modules recursively for each specified path.", True))
# Advanced options
-opts.Add(BoolVariable("dev_mode", "Alias for dev options: verbose=yes warnings=extra werror=yes tests=yes", False))
+opts.Add(
+ BoolVariable(
+ "dev_mode", "Alias for dev options: verbose=yes warnings=extra werror=yes tests=yes strict_checks=yes", False
+ )
+)
opts.Add(BoolVariable("tests", "Build the unit tests", False))
opts.Add(BoolVariable("fast_unsafe", "Enable unsafe options for faster rebuilds", False))
opts.Add(BoolVariable("ninja", "Use the ninja backend for faster rebuilds", False))
+opts.Add(BoolVariable("ninja_auto_run", "Run ninja automatically after generating the ninja file", True))
+opts.Add("ninja_file", "Path to the generated ninja file", "build.ninja")
opts.Add(BoolVariable("compiledb", "Generate compilation DB (`compile_commands.json`) for external tools", False))
+opts.Add(
+ "num_jobs",
+ "Use up to N jobs when compiling (equivalent to `-j N`). Defaults to max jobs - 1. Ignored if -j is used.",
+ "",
+)
opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False))
opts.Add(BoolVariable("progress", "Show a progress indicator during compilation", True))
opts.Add(EnumVariable("warnings", "Level of compilation warnings", "all", ("extra", "all", "moderate", "no")))
@@ -254,6 +267,7 @@ opts.Add(
"",
)
opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False))
+opts.Add(BoolVariable("strict_checks", "Enforce stricter checks (debug option)", False))
opts.Add(BoolVariable("scu_build", "Use single compilation unit build", False))
opts.Add("scu_limit", "Max includes per SCU file when using scu_build (determines RAM use)", "0")
opts.Add(BoolVariable("engine_update_check", "Enable engine update checks in the Project Manager", True))
@@ -285,7 +299,6 @@ opts.Add(BoolVariable("builtin_pcre2_with_jit", "Use JIT compiler for the built-
opts.Add(BoolVariable("builtin_recastnavigation", "Use the built-in Recast navigation library", True))
opts.Add(BoolVariable("builtin_rvo2_2d", "Use the built-in RVO2 2D library", True))
opts.Add(BoolVariable("builtin_rvo2_3d", "Use the built-in RVO2 3D library", True))
-opts.Add(BoolVariable("builtin_squish", "Use the built-in squish library", True))
opts.Add(BoolVariable("builtin_xatlas", "Use the built-in xatlas library", True))
opts.Add(BoolVariable("builtin_zlib", "Use the built-in zlib library", True))
opts.Add(BoolVariable("builtin_zstd", "Use the built-in Zstd library", True))
@@ -537,16 +550,22 @@ initial_num_jobs = env.GetOption("num_jobs")
altered_num_jobs = initial_num_jobs + 1
env.SetOption("num_jobs", altered_num_jobs)
if env.GetOption("num_jobs") == altered_num_jobs:
- cpu_count = os.cpu_count()
- if cpu_count is None:
- print_warning("Couldn't auto-detect CPU count to configure build parallelism. Specify it with the -j argument.")
+ num_jobs = env.get("num_jobs", "")
+ if str(num_jobs).isdigit() and int(num_jobs) > 0:
+ env.SetOption("num_jobs", num_jobs)
else:
- safer_cpu_count = cpu_count if cpu_count <= 4 else cpu_count - 1
- print(
- "Auto-detected %d CPU cores available for build parallelism. Using %d cores by default. You can override it with the -j argument."
- % (cpu_count, safer_cpu_count)
- )
- env.SetOption("num_jobs", safer_cpu_count)
+ cpu_count = os.cpu_count()
+ if cpu_count is None:
+ print_warning(
+ "Couldn't auto-detect CPU count to configure build parallelism. Specify it with the `-j` or `num_jobs` arguments."
+ )
+ else:
+ safer_cpu_count = cpu_count if cpu_count <= 4 else cpu_count - 1
+ print(
+ "Auto-detected %d CPU cores available for build parallelism. Using %d cores by default. You can override it with the `-j` or `num_jobs` arguments."
+ % (cpu_count, safer_cpu_count)
+ )
+ env.SetOption("num_jobs", safer_cpu_count)
env.extra_suffix = ""
@@ -588,12 +607,16 @@ if env["dev_mode"]:
env["warnings"] = ARGUMENTS.get("warnings", "extra")
env["werror"] = methods.get_cmdline_bool("werror", True)
env["tests"] = methods.get_cmdline_bool("tests", True)
+ env["strict_checks"] = methods.get_cmdline_bool("strict_checks", True)
if env["production"]:
env["use_static_cpp"] = methods.get_cmdline_bool("use_static_cpp", True)
env["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", False)
# LTO "auto" means we handle the preferred option in each platform detect.py.
env["lto"] = ARGUMENTS.get("lto", "auto")
+if env["strict_checks"]:
+ env.Append(CPPDEFINES=["STRICT_CHECKS"])
+
# Run SCU file generation script if in a SCU build.
if env["scu_build"]:
max_includes_per_scu = 8
@@ -616,25 +639,18 @@ if env.dev_build:
print("NOTE: Developer build, with debug optimization level and debug symbols (unless overridden).")
# Enforce our minimal compiler version requirements
-cc_version = methods.get_compiler_version(env) or {
- "major": None,
- "minor": None,
- "patch": None,
- "metadata1": None,
- "metadata2": None,
- "date": None,
-}
-cc_version_major = int(cc_version["major"] or -1)
-cc_version_minor = int(cc_version["minor"] or -1)
-cc_version_metadata1 = cc_version["metadata1"] or ""
-
-if methods.using_gcc(env):
- if cc_version_major == -1:
- print_warning(
- "Couldn't detect compiler version, skipping version checks. "
- "Build may fail if the compiler doesn't support C++17 fully."
- )
- elif cc_version_major < 9:
+cc_version = methods.get_compiler_version(env)
+cc_version_major = cc_version["major"]
+cc_version_minor = cc_version["minor"]
+cc_version_metadata1 = cc_version["metadata1"]
+
+if cc_version_major == -1:
+ print_warning(
+ "Couldn't detect compiler version, skipping version checks. "
+ "Build may fail if the compiler doesn't support C++17 fully."
+ )
+elif methods.using_gcc(env):
+ if cc_version_major < 9:
print_error(
"Detected GCC version older than 9, which does not fully support "
"C++17, or has bugs when compiling Godot. Supported versions are 9 "
@@ -654,17 +670,12 @@ if methods.using_gcc(env):
print_warning("GCC < 8 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
env["debug_paths_relative"] = False
elif methods.using_clang(env):
- if cc_version_major == -1:
- print_warning(
- "Couldn't detect compiler version, skipping version checks. "
- "Build may fail if the compiler doesn't support C++17 fully."
- )
# Apple LLVM versions differ from upstream LLVM version \o/, compare
# in https://en.wikipedia.org/wiki/Xcode#Toolchain_versions
- elif env["platform"] == "macos" or env["platform"] == "ios":
+ if env["platform"] == "macos" or env["platform"] == "ios":
vanilla = methods.is_vanilla_clang(env)
if vanilla and cc_version_major < 6:
- print_warning(
+ print_error(
"Detected Clang version older than 6, which does not fully support "
"C++17. Supported versions are Clang 6 and later."
)
@@ -689,6 +700,28 @@ elif methods.using_clang(env):
if env["debug_paths_relative"] and cc_version_major < 10:
print_warning("Clang < 10 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
env["debug_paths_relative"] = False
+elif env.msvc:
+ # Ensure latest minor builds of Visual Studio 2017/2019.
+ # https://github.com/godotengine/godot/pull/94995#issuecomment-2336464574
+ if cc_version_major == 16 and cc_version_minor < 11:
+ print_error(
+ "Detected Visual Studio 2019 version older than 16.11, which has bugs "
+ "when compiling Godot. Use a newer VS2019 version, or VS2022."
+ )
+ Exit(255)
+ if cc_version_major == 15 and cc_version_minor < 9:
+ print_error(
+ "Detected Visual Studio 2017 version older than 15.9, which has bugs "
+ "when compiling Godot. Use a newer VS2017 version, or VS2019/VS2022."
+ )
+ Exit(255)
+ if cc_version_major < 15:
+ print_error(
+ "Detected Visual Studio 2015 or earlier, which is unsupported in Godot. "
+ "Supported versions are Visual Studio 2017 and later."
+ )
+ Exit(255)
+
# Set optimize and debug_symbols flags.
# "custom" means do nothing and let users set their own optimization flags.
@@ -766,13 +799,23 @@ if env["lto"] != "none":
# This needs to come after `configure`, otherwise we don't have env.msvc.
if not env.msvc:
# Specifying GNU extensions support explicitly, which are supported by
- # both GCC and Clang. Both currently default to gnu11 and gnu++14.
+ # both GCC and Clang. Both currently default to gnu11 and gnu++17.
env.Prepend(CFLAGS=["-std=gnu11"])
env.Prepend(CXXFLAGS=["-std=gnu++17"])
else:
- # MSVC doesn't have clear C standard support, /std only covers C++.
- # We apply it to CCFLAGS (both C and C++ code) in case it impacts C features.
- env.Prepend(CCFLAGS=["/std:c++17"])
+ # MSVC started offering C standard support with Visual Studio 2019 16.8, which covers all
+ # of our supported VS2019 & VS2022 versions; VS2017 will only pass the C++ standard.
+ env.Prepend(CXXFLAGS=["/std:c++17"])
+ if cc_version_major < 16:
+ print_warning("Visual Studio 2017 cannot specify a C-Standard.")
+ else:
+ env.Prepend(CFLAGS=["/std:c11"])
+ # MSVC is non-conforming with the C++ standard by default, so we enable more conformance.
+ # Note that this is still not complete conformance, as certain Windows-related headers
+ # don't compile under complete conformance.
+ env.Prepend(CCFLAGS=["/permissive-"])
+ # Allow use of `__cplusplus` macro to determine C++ standard universally.
+ env.Prepend(CXXFLAGS=["/Zc:__cplusplus"])
# Disable exception handling. Godot doesn't use exceptions anywhere, and this
# saves around 20% of binary size and very significant build time (GH-80513).
@@ -785,7 +828,7 @@ elif env.msvc:
env.Append(CXXFLAGS=["/EHsc"])
# Configure compiler warnings
-if env.msvc: # MSVC
+if env.msvc and not methods.using_clang(env): # MSVC
if env["warnings"] == "no":
env.Append(CCFLAGS=["/w"])
else:
@@ -835,8 +878,11 @@ else: # GCC, Clang
# for putting them in `Set` or `Map`. We don't mind about unreliable ordering.
common_warnings += ["-Wno-ordered-compare-function-pointers"]
+ # clang-cl will interpret `-Wall` as `-Weverything`, workaround with compatibility cast
+ W_ALL = "-Wall" if not env.msvc else "-W3"
+
if env["warnings"] == "extra":
- env.Append(CCFLAGS=["-Wall", "-Wextra", "-Wwrite-strings", "-Wno-unused-parameter"] + common_warnings)
+ env.Append(CCFLAGS=[W_ALL, "-Wextra", "-Wwrite-strings", "-Wno-unused-parameter"] + common_warnings)
env.Append(CXXFLAGS=["-Wctor-dtor-privacy", "-Wnon-virtual-dtor"])
if methods.using_gcc(env):
env.Append(
@@ -858,9 +904,9 @@ else: # GCC, Clang
elif methods.using_clang(env) or methods.using_emcc(env):
env.Append(CCFLAGS=["-Wimplicit-fallthrough"])
elif env["warnings"] == "all":
- env.Append(CCFLAGS=["-Wall"] + common_warnings)
+ env.Append(CCFLAGS=[W_ALL] + common_warnings)
elif env["warnings"] == "moderate":
- env.Append(CCFLAGS=["-Wall", "-Wno-unused"] + common_warnings)
+ env.Append(CCFLAGS=[W_ALL, "-Wno-unused"] + common_warnings)
else: # 'no'
env.Append(CCFLAGS=["-w"])
@@ -1014,7 +1060,9 @@ if env["vsproj"]:
if env["compiledb"]:
if env.scons_version < (4, 0, 0):
# Generating the compilation DB (`compile_commands.json`) requires SCons 4.0.0 or later.
- print_error("The `compiledb=yes` option requires SCons 4.0 or later, but your version is %s." % scons_raw_version)
+ print_error(
+ "The `compiledb=yes` option requires SCons 4.0 or later, but your version is %s." % scons_raw_version
+ )
Exit(255)
env.Tool("compilation_db")
@@ -1026,12 +1074,9 @@ if env["ninja"]:
Exit(255)
SetOption("experimental", "ninja")
- env.Tool("ninja")
-
- # By setting this we allow the user to run ninja by themselves with all
- # the flags they need, as apparently automatically running from scons
- # is way slower.
- SetOption("disable_execute_ninja", True)
+ env["NINJA_FILE_NAME"] = env["ninja_file"]
+ env["NINJA_DISABLE_AUTO_RUN"] = not env["ninja_auto_run"]
+ env.Tool("ninja", "build.ninja")
# Threads
if env["threads"]:
@@ -1070,7 +1115,6 @@ if "check_c_headers" in env:
env.AppendUnique(CPPDEFINES=[headers[header]])
-# FIXME: This method mixes both cosmetic progress stuff and cache handling...
methods.show_progress(env)
# TODO: replace this with `env.Dump(format="json")`
# once we start requiring SCons 4.0 as min version.
@@ -1094,7 +1138,7 @@ atexit.register(print_elapsed_time)
def purge_flaky_files():
- paths_to_keep = ["ninja.build"]
+ paths_to_keep = ["build.ninja"]
for build_failure in GetBuildFailures():
path = build_failure.node.path
if os.path.isfile(path) and path not in paths_to_keep:
@@ -1102,3 +1146,5 @@ def purge_flaky_files():
atexit.register(purge_flaky_files)
+
+methods.clean_cache(env)
diff --git a/core/SCsub b/core/SCsub
index 1bd4eae16c4c..8bda230b870c 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
@@ -140,7 +141,7 @@ if env["builtin_zstd"]:
"decompress/zstd_decompress_block.c",
"decompress/zstd_decompress.c",
]
- if env["platform"] in ["android", "ios", "linuxbsd", "macos"]:
+ if env["platform"] in ["android", "ios", "linuxbsd", "macos"] and env["arch"] == "x86_64":
# Match platforms with ZSTD_ASM_SUPPORTED in common/portability_macros.h
thirdparty_zstd_sources.append("decompress/huf_decompress_amd64.S")
thirdparty_zstd_sources = [thirdparty_zstd_dir + file for file in thirdparty_zstd_sources]
diff --git a/core/config/SCsub b/core/config/SCsub
index bf702854905f..1417a258c1a7 100644
--- a/core/config/SCsub
+++ b/core/config/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 3574430cf752..d77c91331426 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -263,6 +263,18 @@ bool Engine::is_generate_spirv_debug_info_enabled() const {
return generate_spirv_debug_info;
}
+bool Engine::is_extra_gpu_memory_tracking_enabled() const {
+ return extra_gpu_memory_tracking;
+}
+
+void Engine::set_print_to_stdout(bool p_enabled) {
+ CoreGlobals::print_line_enabled = p_enabled;
+}
+
+bool Engine::is_printing_to_stdout() const {
+ return CoreGlobals::print_line_enabled;
+}
+
void Engine::set_print_error_messages(bool p_enabled) {
CoreGlobals::print_error_enabled = p_enabled;
}
diff --git a/core/config/engine.h b/core/config/engine.h
index 7e617d8773a0..a0b1ffa98123 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -72,6 +72,7 @@ class Engine {
bool abort_on_gpu_errors = false;
bool use_validation_layers = false;
bool generate_spirv_debug_info = false;
+ bool extra_gpu_memory_tracking = false;
int32_t gpu_idx = -1;
uint64_t _process_frames = 0;
@@ -127,6 +128,9 @@ class Engine {
void set_time_scale(double p_scale);
double get_time_scale() const;
+ void set_print_to_stdout(bool p_enabled);
+ bool is_printing_to_stdout() const;
+
void set_print_error_messages(bool p_enabled);
bool is_printing_error_messages() const;
void print_header(const String &p_string) const;
@@ -181,6 +185,7 @@ class Engine {
bool is_abort_on_gpu_errors_enabled() const;
bool is_validation_layers_enabled() const;
bool is_generate_spirv_debug_info_enabled() const;
+ bool is_extra_gpu_memory_tracking_enabled() const;
int32_t get_gpu_index() const;
void increment_frames_drawn();
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 37a2608c102c..9fe54e57a7a7 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -348,7 +348,6 @@ bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const {
_THREAD_SAFE_METHOD_
if (!props.has(p_name)) {
- WARN_PRINT("Property not found: " + String(p_name));
return false;
}
r_ret = props[p_name].variant;
@@ -1016,7 +1015,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
}
}
// Check for the existence of a csproj file.
- if (_csproj_exists(p_path.get_base_dir())) {
+ if (_csproj_exists(get_resource_path())) {
// If there is a csproj file, add the C# feature if it doesn't already exist.
if (!project_features.has("C#")) {
project_features.append("C#");
@@ -1398,7 +1397,7 @@ void ProjectSettings::_add_builtin_input_map() {
}
Dictionary action;
- action["deadzone"] = Variant(0.5f);
+ action["deadzone"] = Variant(0.2f);
action["events"] = events;
String action_name = "input/" + E.key;
@@ -1472,10 +1471,6 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_height_override", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"), 0); // 8K resolution
GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true);
-#ifdef TOOLS_ENABLED
- GLOBAL_DEF("display/window/energy_saving/keep_screen_on.editor_hint", false);
-#endif
-
GLOBAL_DEF("animation/warnings/check_invalid_track_paths", true);
GLOBAL_DEF("animation/warnings/check_angle_interpolation_type_conflicting", true);
@@ -1489,15 +1484,6 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF(PropertyInfo(Variant::INT, "audio/general/ios/session_category", PROPERTY_HINT_ENUM, "Ambient,Multi Route,Play and Record,Playback,Record,Solo Ambient"), 0);
GLOBAL_DEF("audio/general/ios/mix_with_others", false);
- PackedStringArray extensions;
- extensions.push_back("gd");
- if (ClassDB::class_exists("CSharpScript")) {
- extensions.push_back("cs");
- }
- extensions.push_back("gdshader");
-
- GLOBAL_DEF(PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions"), extensions);
-
_add_builtin_input_map();
// Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum.
@@ -1570,6 +1556,11 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("collada/use_ambient", false);
+ // Input settings
+ GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false);
+ GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false);
+ GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "input_devices/pointing/android/rotary_input_scroll_axis", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1);
+
// These properties will not show up in the dialog. If you want to exclude whole groups, use add_hidden_prefix().
GLOBAL_DEF_INTERNAL("application/config/features", PackedStringArray());
GLOBAL_DEF_INTERNAL("internationalization/locale/translation_remaps", PackedStringArray());
@@ -1582,6 +1573,7 @@ ProjectSettings::ProjectSettings() {
ProjectSettings::ProjectSettings(const String &p_path) {
if (load_custom(p_path) == OK) {
+ resource_path = p_path.get_base_dir();
project_loaded = true;
}
}
diff --git a/core/core_bind.compat.inc b/core/core_bind.compat.inc
new file mode 100644
index 000000000000..3e8ac3c5de6a
--- /dev/null
+++ b/core/core_bind.compat.inc
@@ -0,0 +1,57 @@
+/**************************************************************************/
+/* core_bind.compat.inc */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef DISABLE_DEPRECATED
+
+namespace core_bind {
+
+// Semaphore
+
+void Semaphore::_post_bind_compat_93605() {
+ post(1);
+}
+
+void Semaphore::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("post"), &Semaphore::_post_bind_compat_93605);
+}
+
+// OS
+
+Dictionary OS::_execute_with_pipe_bind_compat_94434(const String &p_path, const Vector &p_arguments) {
+ return execute_with_pipe(p_path, p_arguments, true);
+}
+
+void OS::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("execute_with_pipe", "path", "arguments"), &OS::_execute_with_pipe_bind_compat_94434);
+}
+
+} // namespace core_bind
+
+#endif // DISABLE_DEPRECATED
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index a1b7b81111df..891e3a28c9ed 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "core_bind.h"
+#include "core_bind.compat.inc"
#include "core/config/project_settings.h"
#include "core/crypto/crypto_core.h"
@@ -56,8 +57,11 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String &
ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const String &p_path, Array r_progress) {
float progress = 0;
::ResourceLoader::ThreadLoadStatus tls = ::ResourceLoader::load_threaded_get_status(p_path, &progress);
- r_progress.resize(1);
- r_progress[0] = progress;
+ // Default array should never be modified, it causes the hash of the method to change.
+ if (!ClassDB::is_default_array_arg(r_progress)) {
+ r_progress.resize(1);
+ r_progress[0] = progress;
+ }
return (ThreadLoadStatus)tls;
}
@@ -115,6 +119,11 @@ bool ResourceLoader::has_cached(const String &p_path) {
return ResourceCache::has(local_path);
}
+Ref ResourceLoader::get_cached_ref(const String &p_path) {
+ String local_path = ProjectSettings::get_singleton()->localize_path(p_path);
+ return ResourceCache::get_ref(local_path);
+}
+
bool ResourceLoader::exists(const String &p_path, const String &p_type_hint) {
return ::ResourceLoader::exists(p_path, p_type_hint);
}
@@ -125,7 +134,7 @@ ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) {
void ResourceLoader::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_threaded_request", "path", "type_hint", "use_sub_threads", "cache_mode"), &ResourceLoader::load_threaded_request, DEFVAL(""), DEFVAL(false), DEFVAL(CACHE_MODE_REUSE));
- ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &ResourceLoader::load_threaded_get_status, DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &ResourceLoader::load_threaded_get_status, DEFVAL_ARRAY);
ClassDB::bind_method(D_METHOD("load_threaded_get", "path"), &ResourceLoader::load_threaded_get);
ClassDB::bind_method(D_METHOD("load", "path", "type_hint", "cache_mode"), &ResourceLoader::load, DEFVAL(""), DEFVAL(CACHE_MODE_REUSE));
@@ -135,6 +144,7 @@ void ResourceLoader::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_abort_on_missing_resources", "abort"), &ResourceLoader::set_abort_on_missing_resources);
ClassDB::bind_method(D_METHOD("get_dependencies", "path"), &ResourceLoader::get_dependencies);
ClassDB::bind_method(D_METHOD("has_cached", "path"), &ResourceLoader::has_cached);
+ ClassDB::bind_method(D_METHOD("get_cached_ref", "path"), &ResourceLoader::get_cached_ref);
ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &ResourceLoader::exists, DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_resource_uid", "path"), &ResourceLoader::get_resource_uid);
@@ -174,6 +184,10 @@ void ResourceSaver::remove_resource_format_saver(Ref p_form
::ResourceSaver::remove_resource_format_saver(p_format_saver);
}
+ResourceUID::ID ResourceSaver::get_resource_id_for_path(const String &p_path, bool p_generate) {
+ return ::ResourceSaver::get_resource_id_for_path(p_path, p_generate);
+}
+
ResourceSaver *ResourceSaver::singleton = nullptr;
void ResourceSaver::_bind_methods() {
@@ -181,6 +195,7 @@ void ResourceSaver::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_recognized_extensions", "type"), &ResourceSaver::get_recognized_extensions);
ClassDB::bind_method(D_METHOD("add_resource_format_saver", "format_saver", "at_front"), &ResourceSaver::add_resource_format_saver, DEFVAL(false));
ClassDB::bind_method(D_METHOD("remove_resource_format_saver", "format_saver"), &ResourceSaver::remove_resource_format_saver);
+ ClassDB::bind_method(D_METHOD("get_resource_id_for_path", "path", "generate"), &ResourceSaver::get_resource_id_for_path, DEFVAL(false));
BIND_BITFIELD_FLAG(FLAG_NONE);
BIND_BITFIELD_FLAG(FLAG_RELATIVE_PATHS);
@@ -300,19 +315,22 @@ int OS::execute(const String &p_path, const Vector &p_arguments, Array r
String pipe;
int exitcode = 0;
Error err = ::OS::get_singleton()->execute(p_path, args, &pipe, &exitcode, p_read_stderr, nullptr, p_open_console);
- r_output.push_back(pipe);
+ // Default array should never be modified, it causes the hash of the method to change.
+ if (!ClassDB::is_default_array_arg(r_output)) {
+ r_output.push_back(pipe);
+ }
if (err != OK) {
return -1;
}
return exitcode;
}
-Dictionary OS::execute_with_pipe(const String &p_path, const Vector &p_arguments) {
+Dictionary OS::execute_with_pipe(const String &p_path, const Vector &p_arguments, bool p_blocking) {
List args;
for (const String &arg : p_arguments) {
args.push_back(arg);
}
- return ::OS::get_singleton()->execute_with_pipe(p_path, args);
+ return ::OS::get_singleton()->execute_with_pipe(p_path, args, p_blocking);
}
int OS::create_instance(const Vector &p_arguments) {
@@ -611,8 +629,8 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_system_font_path_for_text", "font_name", "text", "locale", "script", "weight", "stretch", "italic"), &OS::get_system_font_path_for_text, DEFVAL(String()), DEFVAL(String()), DEFVAL(400), DEFVAL(100), DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path);
ClassDB::bind_method(D_METHOD("read_string_from_stdin"), &OS::read_string_from_stdin);
- ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL(Array()), DEFVAL(false), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("execute_with_pipe", "path", "arguments"), &OS::execute_with_pipe);
+ ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL_ARRAY, DEFVAL(false), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("execute_with_pipe", "path", "arguments", "blocking"), &OS::execute_with_pipe, DEFVAL(true));
ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false));
ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance);
ClassDB::bind_method(D_METHOD("kill", "pid"), &OS::kill);
@@ -692,6 +710,7 @@ void OS::_bind_methods() {
BIND_ENUM_CONSTANT(RENDERING_DRIVER_VULKAN);
BIND_ENUM_CONSTANT(RENDERING_DRIVER_OPENGL3);
BIND_ENUM_CONSTANT(RENDERING_DRIVER_D3D12);
+ BIND_ENUM_CONSTANT(RENDERING_DRIVER_METAL);
BIND_ENUM_CONSTANT(SYSTEM_DIR_DESKTOP);
BIND_ENUM_CONSTANT(SYSTEM_DIR_DCIM);
@@ -1209,14 +1228,15 @@ bool Semaphore::try_wait() {
return semaphore.try_wait();
}
-void Semaphore::post() {
- semaphore.post();
+void Semaphore::post(int p_count) {
+ ERR_FAIL_COND(p_count <= 0);
+ semaphore.post(p_count);
}
void Semaphore::_bind_methods() {
ClassDB::bind_method(D_METHOD("wait"), &Semaphore::wait);
ClassDB::bind_method(D_METHOD("try_wait"), &Semaphore::try_wait);
- ClassDB::bind_method(D_METHOD("post"), &Semaphore::post);
+ ClassDB::bind_method(D_METHOD("post", "count"), &Semaphore::post, DEFVAL(1));
}
////// Mutex //////
@@ -1404,6 +1424,11 @@ Variant ClassDB::instantiate(const StringName &p_class) const {
}
}
+ClassDB::APIType ClassDB::class_get_api_type(const StringName &p_class) const {
+ ::ClassDB::APIType api_type = ::ClassDB::get_api_type(p_class);
+ return (APIType)api_type;
+}
+
bool ClassDB::class_has_signal(const StringName &p_class, const StringName &p_signal) const {
return ::ClassDB::has_signal(p_class, p_signal);
}
@@ -1440,6 +1465,14 @@ TypedArray ClassDB::class_get_property_list(const StringName &p_clas
return ret;
}
+StringName ClassDB::class_get_property_getter(const StringName &p_class, const StringName &p_property) {
+ return ::ClassDB::get_property_getter(p_class, p_property);
+}
+
+StringName ClassDB::class_get_property_setter(const StringName &p_class, const StringName &p_property) {
+ return ::ClassDB::get_property_setter(p_class, p_property);
+}
+
Variant ClassDB::class_get_property(Object *p_object, const StringName &p_property) const {
Variant ret;
::ClassDB::get_property(p_object, p_property, ret);
@@ -1492,6 +1525,23 @@ TypedArray ClassDB::class_get_method_list(const StringName &p_class,
return ret;
}
+Variant ClassDB::class_call_static_method(const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) {
+ if (p_argcount < 2) {
+ r_call_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ return Variant::NIL;
+ }
+ if (!p_arguments[0]->is_string() || !p_arguments[1]->is_string()) {
+ r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ return Variant::NIL;
+ }
+ StringName class_ = *p_arguments[0];
+ StringName method = *p_arguments[1];
+ const MethodBind *bind = ::ClassDB::get_method(class_, method);
+ ERR_FAIL_NULL_V_MSG(bind, Variant::NIL, "Cannot find static method.");
+ ERR_FAIL_COND_V_MSG(!bind->is_static(), Variant::NIL, "Method is not static.");
+ return bind->call(nullptr, p_arguments + 2, p_argcount - 2, r_call_error);
+}
+
PackedStringArray ClassDB::class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance) const {
List constants;
::ClassDB::get_integer_constant_list(p_class, &constants, p_no_inheritance);
@@ -1575,7 +1625,7 @@ void ClassDB::get_argument_options(const StringName &p_function, int p_idx, List
pf == "class_has_method" || pf == "class_get_method_list" ||
pf == "class_get_integer_constant_list" || pf == "class_has_integer_constant" || pf == "class_get_integer_constant" ||
pf == "class_has_enum" || pf == "class_get_enum_list" || pf == "class_get_enum_constants" || pf == "class_get_integer_constant_enum" ||
- pf == "is_class_enabled" || pf == "is_class_enum_bitfield");
+ pf == "is_class_enabled" || pf == "is_class_enum_bitfield" || pf == "class_get_api_type");
}
if (first_argument_is_class || pf == "is_parent_class") {
for (const String &E : get_class_list()) {
@@ -1596,11 +1646,15 @@ void ClassDB::_bind_methods() {
::ClassDB::bind_method(D_METHOD("can_instantiate", "class"), &ClassDB::can_instantiate);
::ClassDB::bind_method(D_METHOD("instantiate", "class"), &ClassDB::instantiate);
+ ::ClassDB::bind_method(D_METHOD("class_get_api_type", "class"), &ClassDB::class_get_api_type);
+
::ClassDB::bind_method(D_METHOD("class_has_signal", "class", "signal"), &ClassDB::class_has_signal);
::ClassDB::bind_method(D_METHOD("class_get_signal", "class", "signal"), &ClassDB::class_get_signal);
::ClassDB::bind_method(D_METHOD("class_get_signal_list", "class", "no_inheritance"), &ClassDB::class_get_signal_list, DEFVAL(false));
::ClassDB::bind_method(D_METHOD("class_get_property_list", "class", "no_inheritance"), &ClassDB::class_get_property_list, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_get_property_getter", "class", "property"), &ClassDB::class_get_property_getter);
+ ::ClassDB::bind_method(D_METHOD("class_get_property_setter", "class", "property"), &ClassDB::class_get_property_setter);
::ClassDB::bind_method(D_METHOD("class_get_property", "object", "property"), &ClassDB::class_get_property);
::ClassDB::bind_method(D_METHOD("class_set_property", "object", "property", "value"), &ClassDB::class_set_property);
@@ -1612,6 +1666,8 @@ void ClassDB::_bind_methods() {
::ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &ClassDB::class_get_method_list, DEFVAL(false));
+ ::ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "class_call_static_method", &ClassDB::class_call_static_method, MethodInfo("class_call_static_method", PropertyInfo(Variant::STRING_NAME, "class"), PropertyInfo(Variant::STRING_NAME, "method")));
+
::ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &ClassDB::class_get_integer_constant_list, DEFVAL(false));
::ClassDB::bind_method(D_METHOD("class_has_integer_constant", "class", "name"), &ClassDB::class_has_integer_constant);
@@ -1625,6 +1681,12 @@ void ClassDB::_bind_methods() {
::ClassDB::bind_method(D_METHOD("is_class_enum_bitfield", "class", "enum", "no_inheritance"), &ClassDB::is_class_enum_bitfield, DEFVAL(false));
::ClassDB::bind_method(D_METHOD("is_class_enabled", "class"), &ClassDB::is_class_enabled);
+
+ BIND_ENUM_CONSTANT(API_CORE);
+ BIND_ENUM_CONSTANT(API_EDITOR);
+ BIND_ENUM_CONSTANT(API_EXTENSION);
+ BIND_ENUM_CONSTANT(API_EDITOR_EXTENSION);
+ BIND_ENUM_CONSTANT(API_NONE);
}
} // namespace special
@@ -1738,7 +1800,7 @@ Object *Engine::get_singleton_object(const StringName &p_name) const {
void Engine::register_singleton(const StringName &p_name, Object *p_object) {
ERR_FAIL_COND_MSG(has_singleton(p_name), "Singleton already registered: " + String(p_name));
- ERR_FAIL_COND_MSG(!String(p_name).is_valid_identifier(), "Singleton name is not a valid identifier: " + p_name);
+ ERR_FAIL_COND_MSG(!String(p_name).is_valid_ascii_identifier(), "Singleton name is not a valid identifier: " + p_name);
::Engine::Singleton s;
s.class_name = p_name;
s.name = p_name;
@@ -1791,6 +1853,14 @@ String Engine::get_write_movie_path() const {
return ::Engine::get_singleton()->get_write_movie_path();
}
+void Engine::set_print_to_stdout(bool p_enabled) {
+ ::Engine::get_singleton()->set_print_to_stdout(p_enabled);
+}
+
+bool Engine::is_printing_to_stdout() const {
+ return ::Engine::get_singleton()->is_printing_to_stdout();
+}
+
void Engine::set_print_error_messages(bool p_enabled) {
::Engine::get_singleton()->set_print_error_messages(p_enabled);
}
@@ -1859,10 +1929,14 @@ void Engine::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_write_movie_path"), &Engine::get_write_movie_path);
+ ClassDB::bind_method(D_METHOD("set_print_to_stdout", "enabled"), &Engine::set_print_to_stdout);
+ ClassDB::bind_method(D_METHOD("is_printing_to_stdout"), &Engine::is_printing_to_stdout);
+
ClassDB::bind_method(D_METHOD("set_print_error_messages", "enabled"), &Engine::set_print_error_messages);
ClassDB::bind_method(D_METHOD("is_printing_error_messages"), &Engine::is_printing_error_messages);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "print_error_messages"), "set_print_error_messages", "is_printing_error_messages");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "print_to_stdout"), "set_print_to_stdout", "is_printing_to_stdout");
ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_ticks_per_second"), "set_physics_ticks_per_second", "get_physics_ticks_per_second");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_physics_steps_per_frame"), "set_max_physics_steps_per_frame", "get_max_physics_steps_per_frame");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_fps"), "set_max_fps", "get_max_fps");
diff --git a/core/core_bind.h b/core/core_bind.h
index b142a2fbbd9c..ce0bde3c05b8 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -73,7 +73,7 @@ class ResourceLoader : public Object {
static ResourceLoader *get_singleton() { return singleton; }
Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, CacheMode p_cache_mode = CACHE_MODE_REUSE);
- ThreadLoadStatus load_threaded_get_status(const String &p_path, Array r_progress = Array());
+ ThreadLoadStatus load_threaded_get_status(const String &p_path, Array r_progress = ClassDB::default_array_arg);
Ref load_threaded_get(const String &p_path);
Ref load(const String &p_path, const String &p_type_hint = "", CacheMode p_cache_mode = CACHE_MODE_REUSE);
@@ -83,6 +83,7 @@ class ResourceLoader : public Object {
void set_abort_on_missing_resources(bool p_abort);
PackedStringArray get_dependencies(const String &p_path);
bool has_cached(const String &p_path);
+ Ref get_cached_ref(const String &p_path);
bool exists(const String &p_path, const String &p_type_hint = "");
ResourceUID::ID get_resource_uid(const String &p_path);
@@ -115,6 +116,8 @@ class ResourceSaver : public Object {
void add_resource_format_saver(Ref p_format_saver, bool p_at_front);
void remove_resource_format_saver(Ref p_format_saver);
+ ResourceUID::ID get_resource_id_for_path(const String &p_path, bool p_generate = false);
+
ResourceSaver() { singleton = this; }
};
@@ -127,11 +130,18 @@ class OS : public Object {
static void _bind_methods();
static OS *singleton;
+#ifndef DISABLE_DEPRECATED
+ Dictionary _execute_with_pipe_bind_compat_94434(const String &p_path, const Vector &p_arguments);
+
+ static void _bind_compatibility_methods();
+#endif
+
public:
enum RenderingDriver {
RENDERING_DRIVER_VULKAN,
RENDERING_DRIVER_OPENGL3,
RENDERING_DRIVER_D3D12,
+ RENDERING_DRIVER_METAL,
};
PackedByteArray get_entropy(int p_bytes);
@@ -158,8 +168,8 @@ class OS : public Object {
Vector get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const;
String get_executable_path() const;
String read_string_from_stdin();
- int execute(const String &p_path, const Vector &p_arguments, Array r_output = Array(), bool p_read_stderr = false, bool p_open_console = false);
- Dictionary execute_with_pipe(const String &p_path, const Vector &p_arguments);
+ int execute(const String &p_path, const Vector &p_arguments, Array r_output = ClassDB::default_array_arg, bool p_read_stderr = false, bool p_open_console = false);
+ Dictionary execute_with_pipe(const String &p_path, const Vector &p_arguments, bool p_blocking = true);
int create_process(const String &p_path, const Vector &p_arguments, bool p_open_console = false);
int create_instance(const Vector &p_arguments);
Error kill(int p_pid);
@@ -389,12 +399,17 @@ class Semaphore : public RefCounted {
GDCLASS(Semaphore, RefCounted);
::Semaphore semaphore;
+protected:
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ void _post_bind_compat_93605();
+ static void _bind_compatibility_methods();
+#endif // DISABLE_DEPRECATED
public:
void wait();
bool try_wait();
- void post();
+ void post(int p_count = 1);
};
class Thread : public RefCounted {
@@ -434,6 +449,14 @@ class ClassDB : public Object {
static void _bind_methods();
public:
+ enum APIType {
+ API_CORE,
+ API_EDITOR,
+ API_EXTENSION,
+ API_EDITOR_EXTENSION,
+ API_NONE,
+ };
+
PackedStringArray get_class_list() const;
PackedStringArray get_inheriters_from_class(const StringName &p_class) const;
StringName get_parent_class(const StringName &p_class) const;
@@ -442,11 +465,14 @@ class ClassDB : public Object {
bool can_instantiate(const StringName &p_class) const;
Variant instantiate(const StringName &p_class) const;
+ APIType class_get_api_type(const StringName &p_class) const;
bool class_has_signal(const StringName &p_class, const StringName &p_signal) const;
Dictionary class_get_signal(const StringName &p_class, const StringName &p_signal) const;
TypedArray class_get_signal_list(const StringName &p_class, bool p_no_inheritance = false) const;
TypedArray class_get_property_list(const StringName &p_class, bool p_no_inheritance = false) const;
+ StringName class_get_property_getter(const StringName &p_class, const StringName &p_property);
+ StringName class_get_property_setter(const StringName &p_class, const StringName &p_property);
Variant class_get_property(Object *p_object, const StringName &p_property) const;
Error class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const;
@@ -457,6 +483,7 @@ class ClassDB : public Object {
int class_get_method_argument_count(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false) const;
TypedArray class_get_method_list(const StringName &p_class, bool p_no_inheritance = false) const;
+ Variant class_call_static_method(const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error);
PackedStringArray class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const;
bool class_has_integer_constant(const StringName &p_class, const StringName &p_name) const;
@@ -542,6 +569,9 @@ class Engine : public Object {
// `set_write_movie_path()` is not exposed to the scripting API as changing it at run-time has no effect.
String get_write_movie_path() const;
+ void set_print_to_stdout(bool p_enabled);
+ bool is_printing_to_stdout() const;
+
void set_print_error_messages(bool p_enabled);
bool is_printing_error_messages() const;
@@ -618,4 +648,6 @@ VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyEndType);
VARIANT_ENUM_CAST(core_bind::Thread::Priority);
+VARIANT_ENUM_CAST(core_bind::special::ClassDB::APIType);
+
#endif // CORE_BIND_H
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index 5322e39ec0de..25da49fa5c3e 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -671,11 +671,13 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_OBJECTID);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_POINTER);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ARRAY_TYPE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_DICTIONARY_TYPE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LOCALE_ID);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LOCALIZABLE_STRING);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_TYPE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_HIDE_QUATERNION_EDIT);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PASSWORD);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_TOOL_BUTTON);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NONE);
diff --git a/core/crypto/SCsub b/core/crypto/SCsub
index 8cff3cf679cb..3cea6bfb4711 100644
--- a/core/crypto/SCsub
+++ b/core/crypto/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/crypto/crypto.cpp b/core/crypto/crypto.cpp
index d3d007941057..62bacadf91aa 100644
--- a/core/crypto/crypto.cpp
+++ b/core/crypto/crypto.cpp
@@ -36,10 +36,10 @@
/// Resources
-CryptoKey *(*CryptoKey::_create)() = nullptr;
-CryptoKey *CryptoKey::create() {
+CryptoKey *(*CryptoKey::_create)(bool p_notify_postinitialize) = nullptr;
+CryptoKey *CryptoKey::create(bool p_notify_postinitialize) {
if (_create) {
- return _create();
+ return _create(p_notify_postinitialize);
}
return nullptr;
}
@@ -52,10 +52,10 @@ void CryptoKey::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_from_string", "string_key", "public_only"), &CryptoKey::load_from_string, DEFVAL(false));
}
-X509Certificate *(*X509Certificate::_create)() = nullptr;
-X509Certificate *X509Certificate::create() {
+X509Certificate *(*X509Certificate::_create)(bool p_notify_postinitialize) = nullptr;
+X509Certificate *X509Certificate::create(bool p_notify_postinitialize) {
if (_create) {
- return _create();
+ return _create(p_notify_postinitialize);
}
return nullptr;
}
@@ -116,10 +116,10 @@ void HMACContext::_bind_methods() {
ClassDB::bind_method(D_METHOD("finish"), &HMACContext::finish);
}
-HMACContext *(*HMACContext::_create)() = nullptr;
-HMACContext *HMACContext::create() {
+HMACContext *(*HMACContext::_create)(bool p_notify_postinitialize) = nullptr;
+HMACContext *HMACContext::create(bool p_notify_postinitialize) {
if (_create) {
- return _create();
+ return _create(p_notify_postinitialize);
}
ERR_FAIL_V_MSG(nullptr, "HMACContext is not available when the mbedtls module is disabled.");
}
@@ -127,10 +127,10 @@ HMACContext *HMACContext::create() {
/// Crypto
void (*Crypto::_load_default_certificates)(const String &p_path) = nullptr;
-Crypto *(*Crypto::_create)() = nullptr;
-Crypto *Crypto::create() {
+Crypto *(*Crypto::_create)(bool p_notify_postinitialize) = nullptr;
+Crypto *Crypto::create(bool p_notify_postinitialize) {
if (_create) {
- return _create();
+ return _create(p_notify_postinitialize);
}
ERR_FAIL_V_MSG(nullptr, "Crypto is not available when the mbedtls module is disabled.");
}
diff --git a/core/crypto/crypto.h b/core/crypto/crypto.h
index 16649422cffb..c19e6b6773bb 100644
--- a/core/crypto/crypto.h
+++ b/core/crypto/crypto.h
@@ -42,10 +42,10 @@ class CryptoKey : public Resource {
protected:
static void _bind_methods();
- static CryptoKey *(*_create)();
+ static CryptoKey *(*_create)(bool p_notify_postinitialize);
public:
- static CryptoKey *create();
+ static CryptoKey *create(bool p_notify_postinitialize = true);
virtual Error load(const String &p_path, bool p_public_only = false) = 0;
virtual Error save(const String &p_path, bool p_public_only = false) = 0;
virtual String save_to_string(bool p_public_only = false) = 0;
@@ -58,10 +58,10 @@ class X509Certificate : public Resource {
protected:
static void _bind_methods();
- static X509Certificate *(*_create)();
+ static X509Certificate *(*_create)(bool p_notify_postinitialize);
public:
- static X509Certificate *create();
+ static X509Certificate *create(bool p_notify_postinitialize = true);
virtual Error load(const String &p_path) = 0;
virtual Error load_from_memory(const uint8_t *p_buffer, int p_len) = 0;
virtual Error save(const String &p_path) = 0;
@@ -106,10 +106,10 @@ class HMACContext : public RefCounted {
protected:
static void _bind_methods();
- static HMACContext *(*_create)();
+ static HMACContext *(*_create)(bool p_notify_postinitialize);
public:
- static HMACContext *create();
+ static HMACContext *create(bool p_notify_postinitialize = true);
virtual Error start(HashingContext::HashType p_hash_type, const PackedByteArray &p_key) = 0;
virtual Error update(const PackedByteArray &p_data) = 0;
@@ -124,11 +124,11 @@ class Crypto : public RefCounted {
protected:
static void _bind_methods();
- static Crypto *(*_create)();
+ static Crypto *(*_create)(bool p_notify_postinitialize);
static void (*_load_default_certificates)(const String &p_path);
public:
- static Crypto *create();
+ static Crypto *create(bool p_notify_postinitialize = true);
static void load_default_certificates(const String &p_path);
virtual PackedByteArray generate_random_bytes(int p_bytes) = 0;
diff --git a/core/debugger/SCsub b/core/debugger/SCsub
index 19a65492252d..ab811758940f 100644
--- a/core/debugger/SCsub
+++ b/core/debugger/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
index e2ed7245a245..fc1b7b74f992 100644
--- a/core/debugger/remote_debugger.cpp
+++ b/core/debugger/remote_debugger.cpp
@@ -37,6 +37,7 @@
#include "core/debugger/script_debugger.h"
#include "core/input/input.h"
#include "core/io/resource_loader.h"
+#include "core/math/expression.h"
#include "core/object/script_language.h"
#include "core/os/os.h"
#include "servers/display_server.h"
@@ -529,6 +530,41 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
} else if (command == "set_skip_breakpoints") {
ERR_FAIL_COND(data.is_empty());
script_debugger->set_skip_breakpoints(data[0]);
+ } else if (command == "evaluate") {
+ String expression_str = data[0];
+ int frame = data[1];
+
+ ScriptInstance *breaked_instance = script_debugger->get_break_language()->debug_get_stack_level_instance(frame);
+ if (!breaked_instance) {
+ break;
+ }
+
+ List locals;
+ List local_vals;
+
+ script_debugger->get_break_language()->debug_get_stack_level_locals(frame, &locals, &local_vals);
+ ERR_FAIL_COND(locals.size() != local_vals.size());
+
+ PackedStringArray locals_vector;
+ for (const String &S : locals) {
+ locals_vector.append(S);
+ }
+
+ Array local_vals_array;
+ for (const Variant &V : local_vals) {
+ local_vals_array.append(V);
+ }
+
+ Expression expression;
+ expression.parse(expression_str, locals_vector);
+ const Variant return_val = expression.execute(local_vals_array, breaked_instance->get_owner());
+
+ DebuggerMarshalls::ScriptStackVariable stvar;
+ stvar.name = expression_str;
+ stvar.value = return_val;
+ stvar.type = 3;
+
+ send_message("evaluation_return", stvar.serialize());
} else {
bool captured = false;
ERR_CONTINUE(_try_capture(command, data, captured) != OK);
diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp
index 21a9014626db..9dca47a0b4f2 100644
--- a/core/debugger/remote_debugger_peer.cpp
+++ b/core/debugger/remote_debugger_peer.cpp
@@ -144,9 +144,8 @@ void RemoteDebuggerPeerTCP::_read_in() {
Error err = decode_variant(var, buf, in_pos, &read);
ERR_CONTINUE(read != in_pos || err != OK);
ERR_CONTINUE_MSG(var.get_type() != Variant::ARRAY, "Malformed packet received, not an Array.");
- mutex.lock();
+ MutexLock lock(mutex);
in_queue.push_back(var);
- mutex.unlock();
}
}
}
diff --git a/core/doc_data.cpp b/core/doc_data.cpp
index 672a36c35c19..f40e878d52b9 100644
--- a/core/doc_data.cpp
+++ b/core/doc_data.cpp
@@ -33,6 +33,8 @@
String DocData::get_default_value_string(const Variant &p_value) {
if (p_value.get_type() == Variant::ARRAY) {
return Variant(Array(p_value, 0, StringName(), Variant())).get_construct_string().replace("\n", " ");
+ } else if (p_value.get_type() == Variant::DICTIONARY) {
+ return Variant(Dictionary(p_value, 0, StringName(), Variant(), 0, StringName(), Variant())).get_construct_string().replace("\n", " ");
} else {
return p_value.get_construct_string().replace("\n", " ");
}
@@ -57,6 +59,8 @@ void DocData::return_doc_from_retinfo(DocData::MethodDoc &p_method, const Proper
p_method.return_type = p_retinfo.class_name;
} else if (p_retinfo.type == Variant::ARRAY && p_retinfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
p_method.return_type = p_retinfo.hint_string + "[]";
+ } else if (p_retinfo.type == Variant::DICTIONARY && p_retinfo.hint == PROPERTY_HINT_DICTIONARY_TYPE) {
+ p_method.return_type = "Dictionary[" + p_retinfo.hint_string.replace(";", ", ") + "]";
} else if (p_retinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
p_method.return_type = p_retinfo.hint_string;
} else if (p_retinfo.type == Variant::NIL && p_retinfo.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
@@ -89,6 +93,8 @@ void DocData::argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const
p_argument.type = p_arginfo.class_name;
} else if (p_arginfo.type == Variant::ARRAY && p_arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
p_argument.type = p_arginfo.hint_string + "[]";
+ } else if (p_arginfo.type == Variant::DICTIONARY && p_arginfo.hint == PROPERTY_HINT_DICTIONARY_TYPE) {
+ p_argument.type = "Dictionary[" + p_arginfo.hint_string.replace(";", ", ") + "]";
} else if (p_arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
p_argument.type = p_arginfo.hint_string;
} else if (p_arginfo.type == Variant::NIL) {
diff --git a/core/doc_data.h b/core/doc_data.h
index 04bd55eabae9..6a7f4355db54 100644
--- a/core/doc_data.h
+++ b/core/doc_data.h
@@ -522,6 +522,10 @@ class DocData {
String type;
String data_type;
String description;
+ bool is_deprecated = false;
+ String deprecated_message;
+ bool is_experimental = false;
+ String experimental_message;
String default_value;
String keywords;
bool operator<(const ThemeItemDoc &p_theme_item) const {
@@ -550,6 +554,16 @@ class DocData {
doc.description = p_dict["description"];
}
+ if (p_dict.has("deprecated")) {
+ doc.is_deprecated = true;
+ doc.deprecated_message = p_dict["deprecated"];
+ }
+
+ if (p_dict.has("experimental")) {
+ doc.is_experimental = true;
+ doc.experimental_message = p_dict["experimental"];
+ }
+
if (p_dict.has("default_value")) {
doc.default_value = p_dict["default_value"];
}
@@ -579,6 +593,14 @@ class DocData {
dict["description"] = p_doc.description;
}
+ if (p_doc.is_deprecated) {
+ dict["deprecated"] = p_doc.deprecated_message;
+ }
+
+ if (p_doc.is_experimental) {
+ dict["experimental"] = p_doc.experimental_message;
+ }
+
if (!p_doc.default_value.is_empty()) {
dict["default_value"] = p_doc.default_value;
}
diff --git a/core/error/SCsub b/core/error/SCsub
index dfd6248a941c..08089d31b075 100644
--- a/core/error/SCsub
+++ b/core/error/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/error/error_list.h b/core/error/error_list.h
index abc637106a57..cdf06eb06d7f 100644
--- a/core/error/error_list.h
+++ b/core/error/error_list.h
@@ -41,6 +41,7 @@
* - Are added to the Error enum in core/error/error_list.h
* - Have a description added to error_names in core/error/error_list.cpp
* - Are bound with BIND_CORE_ENUM_CONSTANT() in core/core_constants.cpp
+ * - Have a matching Android version in platform/android/java/lib/src/org/godotengine/godot/error/Error.kt
*/
enum Error {
diff --git a/core/error/error_macros.cpp b/core/error/error_macros.cpp
index 8376c0aaf84c..813ee7684ff4 100644
--- a/core/error/error_macros.cpp
+++ b/core/error/error_macros.cpp
@@ -34,6 +34,12 @@
#include "core/os/os.h"
#include "core/string/ustring.h"
+// Optional physics interpolation warnings try to include the path to the relevant node.
+#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
+#include "core/config/project_settings.h"
+#include "scene/main/node.h"
+#endif
+
static ErrorHandlerList *error_handler_list = nullptr;
void add_error_handler(ErrorHandlerList *p_handler) {
@@ -128,3 +134,48 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
void _err_flush_stdout() {
fflush(stdout);
}
+
+// Prevent error spam by limiting the warnings to a certain frequency.
+void _physics_interpolation_warning(const char *p_function, const char *p_file, int p_line, ObjectID p_id, const char *p_warn_string) {
+#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
+ const uint32_t warn_max = 2048;
+ const uint32_t warn_timeout_seconds = 15;
+
+ static uint32_t warn_count = warn_max;
+ static uint32_t warn_timeout = warn_timeout_seconds;
+
+ uint32_t time_now = UINT32_MAX;
+
+ if (warn_count) {
+ warn_count--;
+ }
+
+ if (!warn_count) {
+ time_now = OS::get_singleton()->get_ticks_msec() / 1000;
+ }
+
+ if ((warn_count == 0) && (time_now >= warn_timeout)) {
+ warn_count = warn_max;
+ warn_timeout = time_now + warn_timeout_seconds;
+
+ if (GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
+ // UINT64_MAX means unused.
+ if (p_id.operator uint64_t() == UINT64_MAX) {
+ _err_print_error(p_function, p_file, p_line, "[Physics interpolation] " + String(p_warn_string) + " (possibly benign).", false, ERR_HANDLER_WARNING);
+ } else {
+ String node_name;
+ if (p_id.is_valid()) {
+ Node *node = Object::cast_to(ObjectDB::get_instance(p_id));
+ if (node && node->is_inside_tree()) {
+ node_name = "\"" + String(node->get_path()) + "\"";
+ } else {
+ node_name = "\"unknown\"";
+ }
+ }
+
+ _err_print_error(p_function, p_file, p_line, "[Physics interpolation] " + String(p_warn_string) + ": " + node_name + " (possibly benign).", false, ERR_HANDLER_WARNING);
+ }
+ }
+ }
+#endif
+}
diff --git a/core/error/error_macros.h b/core/error/error_macros.h
index ab7dbcbd44b7..19c16667d01b 100644
--- a/core/error/error_macros.h
+++ b/core/error/error_macros.h
@@ -31,6 +31,7 @@
#ifndef ERROR_MACROS_H
#define ERROR_MACROS_H
+#include "core/object/object_id.h"
#include "core/typedefs.h"
#include // We'd normally use safe_refcount.h, but that would cause circular includes.
@@ -71,6 +72,8 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify = false, bool fatal = false);
void _err_flush_stdout();
+void _physics_interpolation_warning(const char *p_function, const char *p_file, int p_line, ObjectID p_id, const char *p_warn_string);
+
#ifdef __GNUC__
//#define FUNCTION_STR __PRETTY_FUNCTION__ - too annoying
#define FUNCTION_STR __FUNCTION__
@@ -832,4 +835,14 @@ void _err_flush_stdout();
#define DEV_CHECK_ONCE(m_cond)
#endif
+/**
+ * Physics Interpolation warnings.
+ * These are spam protection warnings.
+ */
+#define PHYSICS_INTERPOLATION_NODE_WARNING(m_object_id, m_string) \
+ _physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, m_object_id, m_string)
+
+#define PHYSICS_INTERPOLATION_WARNING(m_string) \
+ _physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, ObjectID(UINT64_MAX), m_string)
+
#endif // ERROR_MACROS_H
diff --git a/core/extension/SCsub b/core/extension/SCsub
index 6ab2d2b0a672..8688ca5b6e7c 100644
--- a/core/extension/SCsub
+++ b/core/extension/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp
index 848b6f3886e5..c5f7502c12f7 100644
--- a/core/extension/extension_api_dump.cpp
+++ b/core/extension/extension_api_dump.cpp
@@ -60,6 +60,9 @@ static String get_property_info_type_name(const PropertyInfo &p_info) {
if (p_info.type == Variant::ARRAY && (p_info.hint == PROPERTY_HINT_ARRAY_TYPE)) {
return String("typedarray::") + p_info.hint_string;
}
+ if (p_info.type == Variant::DICTIONARY && (p_info.hint == PROPERTY_HINT_DICTIONARY_TYPE)) {
+ return String("typeddictionary::") + p_info.hint_string;
+ }
if (p_info.type == Variant::INT && (p_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM))) {
return String("enum::") + String(p_info.class_name);
}
@@ -85,7 +88,7 @@ static String get_property_info_type_name(const PropertyInfo &p_info) {
}
static String get_type_meta_name(const GodotTypeInfo::Metadata metadata) {
- static const char *argmeta[11] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double" };
+ static const char *argmeta[13] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double", "char16", "char32" };
return argmeta[metadata];
}
@@ -1014,6 +1017,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
d2["name"] = String(method_name);
d2["is_const"] = (F.flags & METHOD_FLAG_CONST) ? true : false;
d2["is_static"] = (F.flags & METHOD_FLAG_STATIC) ? true : false;
+ d2["is_required"] = (F.flags & METHOD_FLAG_VIRTUAL_REQUIRED) ? true : false;
d2["is_vararg"] = false;
d2["is_virtual"] = true;
// virtual functions have no hash since no MethodBind is involved
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 8e2366fc95e1..7cba5cb161ef 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -32,11 +32,9 @@
#include "gdextension.compat.inc"
#include "core/config/project_settings.h"
-#include "core/io/dir_access.h"
#include "core/object/class_db.h"
#include "core/object/method_bind.h"
-#include "core/os/os.h"
-#include "core/version.h"
+#include "gdextension_library_loader.h"
#include "gdextension_manager.h"
extern void gdextension_setup_interface();
@@ -48,146 +46,6 @@ String GDExtension::get_extension_list_config_file() {
return ProjectSettings::get_singleton()->get_project_data_path().path_join("extension_list.cfg");
}
-Vector GDExtension::find_extension_dependencies(const String &p_path, Ref p_config, std::function p_has_feature) {
- Vector dependencies_shared_objects;
- if (p_config->has_section("dependencies")) {
- List config_dependencies;
- p_config->get_section_keys("dependencies", &config_dependencies);
-
- for (const String &dependency : config_dependencies) {
- Vector dependency_tags = dependency.split(".");
- bool all_tags_met = true;
- for (int i = 0; i < dependency_tags.size(); i++) {
- String tag = dependency_tags[i].strip_edges();
- if (!p_has_feature(tag)) {
- all_tags_met = false;
- break;
- }
- }
-
- if (all_tags_met) {
- Dictionary dependency_value = p_config->get_value("dependencies", dependency);
- for (const Variant *key = dependency_value.next(nullptr); key; key = dependency_value.next(key)) {
- String dependency_path = *key;
- String target_path = dependency_value[*key];
- if (dependency_path.is_relative_path()) {
- dependency_path = p_path.get_base_dir().path_join(dependency_path);
- }
- dependencies_shared_objects.push_back(SharedObject(dependency_path, dependency_tags, target_path));
- }
- break;
- }
- }
- }
-
- return dependencies_shared_objects;
-}
-
-String GDExtension::find_extension_library(const String &p_path, Ref p_config, std::function p_has_feature, PackedStringArray *r_tags) {
- // First, check the explicit libraries.
- if (p_config->has_section("libraries")) {
- List libraries;
- p_config->get_section_keys("libraries", &libraries);
-
- // Iterate the libraries, finding the best matching tags.
- String best_library_path;
- Vector best_library_tags;
- for (const String &E : libraries) {
- Vector tags = E.split(".");
- bool all_tags_met = true;
- for (int i = 0; i < tags.size(); i++) {
- String tag = tags[i].strip_edges();
- if (!p_has_feature(tag)) {
- all_tags_met = false;
- break;
- }
- }
-
- if (all_tags_met && tags.size() > best_library_tags.size()) {
- best_library_path = p_config->get_value("libraries", E);
- best_library_tags = tags;
- }
- }
-
- if (!best_library_path.is_empty()) {
- if (best_library_path.is_relative_path()) {
- best_library_path = p_path.get_base_dir().path_join(best_library_path);
- }
- if (r_tags != nullptr) {
- r_tags->append_array(best_library_tags);
- }
- return best_library_path;
- }
- }
-
- // Second, try to autodetect
- String autodetect_library_prefix;
- if (p_config->has_section_key("configuration", "autodetect_library_prefix")) {
- autodetect_library_prefix = p_config->get_value("configuration", "autodetect_library_prefix");
- }
- if (!autodetect_library_prefix.is_empty()) {
- String autodetect_path = autodetect_library_prefix;
- if (autodetect_path.is_relative_path()) {
- autodetect_path = p_path.get_base_dir().path_join(autodetect_path);
- }
-
- // Find the folder and file parts of the prefix.
- String folder;
- String file_prefix;
- if (DirAccess::dir_exists_absolute(autodetect_path)) {
- folder = autodetect_path;
- } else if (DirAccess::dir_exists_absolute(autodetect_path.get_base_dir())) {
- folder = autodetect_path.get_base_dir();
- file_prefix = autodetect_path.get_file();
- } else {
- ERR_FAIL_V_MSG(String(), vformat("Error in extension: %s. Could not find folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
- }
-
- // Open the folder.
- Ref dir = DirAccess::open(folder);
- ERR_FAIL_COND_V_MSG(!dir.is_valid(), String(), vformat("Error in extension: %s. Could not open folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
-
- // Iterate the files and check the prefixes, finding the best matching file.
- String best_file;
- Vector best_file_tags;
- dir->list_dir_begin();
- String file_name = dir->_get_next();
- while (file_name != "") {
- if (!dir->current_is_dir() && file_name.begins_with(file_prefix)) {
- // Check if the files matches all requested feature tags.
- String tags_str = file_name.trim_prefix(file_prefix);
- tags_str = tags_str.trim_suffix(tags_str.get_extension());
-
- Vector tags = tags_str.split(".", false);
- bool all_tags_met = true;
- for (int i = 0; i < tags.size(); i++) {
- String tag = tags[i].strip_edges();
- if (!p_has_feature(tag)) {
- all_tags_met = false;
- break;
- }
- }
-
- // If all tags are found in the feature list, and we found more tags than before, use this file.
- if (all_tags_met && tags.size() > best_file_tags.size()) {
- best_file_tags = tags;
- best_file = file_name;
- }
- }
- file_name = dir->_get_next();
- }
-
- if (!best_file.is_empty()) {
- String library_path = folder.path_join(best_file);
- if (r_tags != nullptr) {
- r_tags->append_array(best_file_tags);
- }
- return library_path;
- }
- }
- return String();
-}
-
class GDExtensionMethodBind : public MethodBind {
GDExtensionClassMethodCall call_func;
GDExtensionClassMethodValidatedCall validated_call_func;
@@ -382,7 +240,7 @@ class GDExtensionMethodBind : public MethodBind {
#ifndef DISABLE_DEPRECATED
void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs) {
- const GDExtensionClassCreationInfo3 class_info3 = {
+ const GDExtensionClassCreationInfo4 class_info4 = {
p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
true, // GDExtensionBool is_exposed;
@@ -398,25 +256,26 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library
p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
- p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */
+ nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */
p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
nullptr, // GDExtensionClassRecreateInstance recreate_instance_func;
p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
nullptr, // GDExtensionClassCallVirtualWithData call_virtual_func;
- p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
p_extension_funcs->class_userdata, // void *class_userdata;
};
const ClassCreationDeprecatedInfo legacy = {
p_extension_funcs->notification_func, // GDExtensionClassNotification notification_func;
p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
+ p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func;
+ p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
};
- _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3, &legacy);
+ _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy);
}
void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs) {
- const GDExtensionClassCreationInfo3 class_info3 = {
+ const GDExtensionClassCreationInfo4 class_info4 = {
p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
p_extension_funcs->is_exposed, // GDExtensionBool is_exposed;
@@ -432,34 +291,71 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar
p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
- p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */
+ nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */
p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func;
p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func;
- p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
p_extension_funcs->class_userdata, // void *class_userdata;
};
const ClassCreationDeprecatedInfo legacy = {
nullptr, // GDExtensionClassNotification notification_func;
p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
+ p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func;
+ p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
};
- _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3, &legacy);
+ _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy);
}
-#endif // DISABLE_DEPRECATED
void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs) {
+ const GDExtensionClassCreationInfo4 class_info4 = {
+ p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
+ p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
+ p_extension_funcs->is_exposed, // GDExtensionBool is_exposed;
+ p_extension_funcs->is_runtime, // GDExtensionBool is_runtime;
+ p_extension_funcs->set_func, // GDExtensionClassSet set_func;
+ p_extension_funcs->get_func, // GDExtensionClassGet get_func;
+ p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
+ p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
+ p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func;
+ p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func;
+ p_extension_funcs->validate_property_func, // GDExtensionClassValidateProperty validate_property_func;
+ p_extension_funcs->notification_func, // GDExtensionClassNotification2 notification_func;
+ p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
+ p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
+ p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
+ nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */
+ p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
+ p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func;
+ p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
+ p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
+ p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func;
+ p_extension_funcs->class_userdata, // void *class_userdata;
+ };
+
+ const ClassCreationDeprecatedInfo legacy = {
+ nullptr, // GDExtensionClassNotification notification_func;
+ nullptr, // GDExtensionClassFreePropertyList free_property_list_func;
+ p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance2 create_instance_func;
+ p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
+ };
+ _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy);
+}
+
+#endif // DISABLE_DEPRECATED
+
+void GDExtension::_register_extension_class4(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs) {
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, p_extension_funcs);
}
-void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) {
+void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) {
GDExtension *self = reinterpret_cast(p_library);
StringName class_name = *reinterpret_cast(p_class_name);
StringName parent_class_name = *reinterpret_cast(p_parent_class_name);
- ERR_FAIL_COND_MSG(!String(class_name).is_valid_identifier(), "Attempt to register extension class '" + class_name + "', which is not a valid class identifier.");
+ ERR_FAIL_COND_MSG(!String(class_name).is_valid_unicode_identifier(), "Attempt to register extension class '" + class_name + "', which is not a valid class identifier.");
ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), "Attempt to register extension class '" + class_name + "', which appears to be already registered.");
Extension *parent_extension = nullptr;
@@ -530,6 +426,8 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
if (p_deprecated_funcs) {
extension->gdextension.notification = p_deprecated_funcs->notification_func;
extension->gdextension.free_property_list = p_deprecated_funcs->free_property_list_func;
+ extension->gdextension.create_instance = p_deprecated_funcs->create_instance_func;
+ extension->gdextension.get_rid = p_deprecated_funcs->get_rid_func;
}
#endif // DISABLE_DEPRECATED
extension->gdextension.notification2 = p_extension_funcs->notification_func;
@@ -537,13 +435,12 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
extension->gdextension.reference = p_extension_funcs->reference_func;
extension->gdextension.unreference = p_extension_funcs->unreference_func;
extension->gdextension.class_userdata = p_extension_funcs->class_userdata;
- extension->gdextension.create_instance = p_extension_funcs->create_instance_func;
+ extension->gdextension.create_instance2 = p_extension_funcs->create_instance_func;
extension->gdextension.free_instance = p_extension_funcs->free_instance_func;
extension->gdextension.recreate_instance = p_extension_funcs->recreate_instance_func;
extension->gdextension.get_virtual = p_extension_funcs->get_virtual_func;
extension->gdextension.get_virtual_call_data = p_extension_funcs->get_virtual_call_data_func;
extension->gdextension.call_virtual_with_data = p_extension_funcs->call_virtual_with_data_func;
- extension->gdextension.get_rid = p_extension_funcs->get_rid_func;
extension->gdextension.reloadable = self->reloadable;
#ifdef TOOLS_ENABLED
@@ -755,7 +652,13 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra
void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringPtr r_path) {
GDExtension *self = reinterpret_cast(p_library);
- memnew_placement(r_path, String(self->library_path));
+ Ref library_loader = self->loader;
+ String library_path;
+ if (library_loader.is_valid()) {
+ library_path = library_loader->library_path;
+ }
+
+ memnew_placement(r_path, String(library_path));
}
HashMap GDExtension::gdextension_interface_functions;
@@ -771,64 +674,32 @@ GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(const String
return *function;
}
-Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol, Vector *p_dependencies) {
- String abs_path = ProjectSettings::get_singleton()->globalize_path(p_path);
-
- Vector abs_dependencies_paths;
- if (p_dependencies != nullptr && !p_dependencies->is_empty()) {
- for (const SharedObject &dependency : *p_dependencies) {
- abs_dependencies_paths.push_back(ProjectSettings::get_singleton()->globalize_path(dependency.path));
- }
- }
-
- String actual_lib_path;
- OS::GDExtensionData data = {
- true, // also_set_library_path
- &actual_lib_path, // r_resolved_path
- Engine::get_singleton()->is_editor_hint(), // generate_temp_files
- &abs_dependencies_paths, // library_dependencies
- };
- Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, &data);
-
- if (actual_lib_path.get_file() != abs_path.get_file()) {
- // If temporary files are generated, let's change the library path to point at the original,
- // because that's what we want to check to see if it's changed.
- library_path = actual_lib_path.get_base_dir().path_join(p_path.get_file());
- } else {
- library_path = actual_lib_path;
- }
+Error GDExtension::open_library(const String &p_path, const Ref &p_loader) {
+ ERR_FAIL_COND_V_MSG(p_loader.is_null(), FAILED, "Can't open GDExtension without a loader.");
+ loader = p_loader;
- ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + abs_path);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + abs_path);
+ Error err = loader->open_library(p_path);
- void *entry_funcptr = nullptr;
+ ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + p_path);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + p_path);
- err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, p_entry_symbol, entry_funcptr, false);
+ err = loader->initialize(&gdextension_get_proc_address, this, &initialization);
if (err != OK) {
- ERR_PRINT("GDExtension entry point '" + p_entry_symbol + "' not found in library " + abs_path);
- OS::get_singleton()->close_dynamic_library(library);
+ // Errors already logged in initialize().
+ loader->close_library();
return err;
}
- GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr;
- GDExtensionBool ret = initialization_function(&gdextension_get_proc_address, this, &initialization);
+ level_initialized = -1;
- if (ret) {
- level_initialized = -1;
- return OK;
- } else {
- ERR_PRINT("GDExtension initialization function '" + p_entry_symbol + "' returned an error.");
- OS::get_singleton()->close_dynamic_library(library);
- return FAILED;
- }
+ return OK;
}
void GDExtension::close_library() {
- ERR_FAIL_NULL(library);
- OS::get_singleton()->close_dynamic_library(library);
+ ERR_FAIL_COND(!is_library_open());
+ loader->close_library();
- library = nullptr;
class_icon_paths.clear();
#ifdef TOOLS_ENABLED
@@ -837,16 +708,16 @@ void GDExtension::close_library() {
}
bool GDExtension::is_library_open() const {
- return library != nullptr;
+ return loader.is_valid() && loader->is_library_open();
}
GDExtension::InitializationLevel GDExtension::get_minimum_library_initialization_level() const {
- ERR_FAIL_NULL_V(library, INITIALIZATION_LEVEL_CORE);
+ ERR_FAIL_COND_V(!is_library_open(), INITIALIZATION_LEVEL_CORE);
return InitializationLevel(initialization.minimum_initialization_level);
}
void GDExtension::initialize_library(InitializationLevel p_level) {
- ERR_FAIL_NULL(library);
+ ERR_FAIL_COND(!is_library_open());
ERR_FAIL_COND_MSG(p_level <= int32_t(level_initialized), vformat("Level '%d' must be higher than the current level '%d'", p_level, level_initialized));
level_initialized = int32_t(p_level);
@@ -856,7 +727,7 @@ void GDExtension::initialize_library(InitializationLevel p_level) {
initialization.initialize(initialization.userdata, GDExtensionInitializationLevel(p_level));
}
void GDExtension::deinitialize_library(InitializationLevel p_level) {
- ERR_FAIL_NULL(library);
+ ERR_FAIL_COND(!is_library_open());
ERR_FAIL_COND(p_level > int32_t(level_initialized));
level_initialized = int32_t(p_level) - 1;
@@ -880,7 +751,7 @@ GDExtension::GDExtension() {
}
GDExtension::~GDExtension() {
- if (library != nullptr) {
+ if (is_library_open()) {
close_library();
}
#ifdef TOOLS_ENABLED
@@ -897,8 +768,9 @@ void GDExtension::initialize_gdextensions() {
#ifndef DISABLE_DEPRECATED
register_interface_function("classdb_register_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class);
register_interface_function("classdb_register_extension_class2", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class2);
-#endif // DISABLE_DEPRECATED
register_interface_function("classdb_register_extension_class3", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class3);
+#endif // DISABLE_DEPRECATED
+ register_interface_function("classdb_register_extension_class4", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class4);
register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method);
register_interface_function("classdb_register_extension_class_virtual_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_virtual_method);
register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant);
@@ -918,142 +790,15 @@ void GDExtension::finalize_gdextensions() {
Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, Ref &p_extension) {
ERR_FAIL_COND_V_MSG(p_extension.is_valid() && p_extension->is_library_open(), ERR_ALREADY_IN_USE, "Cannot load GDExtension resource into already opened library.");
- Ref config;
- config.instantiate();
-
- Error err = config->load(p_path);
-
- if (err != OK) {
- ERR_PRINT("Error loading GDExtension configuration file: " + p_path);
- return err;
- }
-
- if (!config->has_section_key("configuration", "entry_symbol")) {
- ERR_PRINT("GDExtension configuration file must contain a \"configuration/entry_symbol\" key: " + p_path);
- return ERR_INVALID_DATA;
- }
-
- String entry_symbol = config->get_value("configuration", "entry_symbol");
-
- uint32_t compatibility_minimum[3] = { 0, 0, 0 };
- if (config->has_section_key("configuration", "compatibility_minimum")) {
- String compat_string = config->get_value("configuration", "compatibility_minimum");
- Vector parts = compat_string.split_ints(".");
- for (int i = 0; i < parts.size(); i++) {
- if (i >= 3) {
- break;
- }
- if (parts[i] >= 0) {
- compatibility_minimum[i] = parts[i];
- }
- }
- } else {
- ERR_PRINT("GDExtension configuration file must contain a \"configuration/compatibility_minimum\" key: " + p_path);
- return ERR_INVALID_DATA;
- }
-
- if (compatibility_minimum[0] < 4 || (compatibility_minimum[0] == 4 && compatibility_minimum[1] == 0)) {
- ERR_PRINT(vformat("GDExtension's compatibility_minimum (%d.%d.%d) must be at least 4.1.0: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
- return ERR_INVALID_DATA;
- }
-
- bool compatible = true;
- // Check version lexicographically.
- if (VERSION_MAJOR != compatibility_minimum[0]) {
- compatible = VERSION_MAJOR > compatibility_minimum[0];
- } else if (VERSION_MINOR != compatibility_minimum[1]) {
- compatible = VERSION_MINOR > compatibility_minimum[1];
- } else {
- compatible = VERSION_PATCH >= compatibility_minimum[2];
- }
- if (!compatible) {
- ERR_PRINT(vformat("GDExtension only compatible with Godot version %d.%d.%d or later: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
- return ERR_INVALID_DATA;
- }
-
- // Optionally check maximum compatibility.
- if (config->has_section_key("configuration", "compatibility_maximum")) {
- uint32_t compatibility_maximum[3] = { 0, 0, 0 };
- String compat_string = config->get_value("configuration", "compatibility_maximum");
- Vector parts = compat_string.split_ints(".");
- for (int i = 0; i < 3; i++) {
- if (i < parts.size() && parts[i] >= 0) {
- compatibility_maximum[i] = parts[i];
- } else {
- // If a version part is missing, set the maximum to an arbitrary high value.
- compatibility_maximum[i] = 9999;
- }
- }
-
- compatible = true;
- if (VERSION_MAJOR != compatibility_maximum[0]) {
- compatible = VERSION_MAJOR < compatibility_maximum[0];
- } else if (VERSION_MINOR != compatibility_maximum[1]) {
- compatible = VERSION_MINOR < compatibility_maximum[1];
- }
-#if VERSION_PATCH
- // #if check to avoid -Wtype-limits warning when 0.
- else {
- compatible = VERSION_PATCH <= compatibility_maximum[2];
- }
-#endif
-
- if (!compatible) {
- ERR_PRINT(vformat("GDExtension only compatible with Godot version %s or earlier: %s", compat_string, p_path));
- return ERR_INVALID_DATA;
- }
- }
-
- String library_path = GDExtension::find_extension_library(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
-
- if (library_path.is_empty()) {
- const String os_arch = OS::get_singleton()->get_name().to_lower() + "." + Engine::get_singleton()->get_architecture_name();
- ERR_PRINT(vformat("No GDExtension library found for current OS and architecture (%s) in configuration file: %s", os_arch, p_path));
- return ERR_FILE_NOT_FOUND;
- }
-
- bool is_static_library = library_path.ends_with(".a") || library_path.ends_with(".xcframework");
-
- if (!library_path.is_resource_file() && !library_path.is_absolute_path()) {
- library_path = p_path.get_base_dir().path_join(library_path);
- }
-
- if (p_extension.is_null()) {
- p_extension.instantiate();
- }
-
-#ifdef TOOLS_ENABLED
- p_extension->set_reloadable(config->get_value("configuration", "reloadable", false) && Engine::get_singleton()->is_extension_reloading_enabled());
-
- p_extension->update_last_modified_time(
- FileAccess::get_modified_time(p_path),
- FileAccess::get_modified_time(library_path));
-#endif
-
- Vector library_dependencies = GDExtension::find_extension_dependencies(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
- err = p_extension->open_library(is_static_library ? String() : library_path, entry_symbol, &library_dependencies);
- if (err != OK) {
- // Unreference the extension so that this loading can be considered a failure.
- p_extension.unref();
-
- // Errors already logged in open_library()
- return err;
- }
-
- // Handle icons if any are specified.
- if (config->has_section("icons")) {
- List keys;
- config->get_section_keys("icons", &keys);
- for (const String &key : keys) {
- String icon_path = config->get_value("icons", key);
- if (icon_path.is_relative_path()) {
- icon_path = p_path.get_base_dir().path_join(icon_path);
- }
+ GDExtensionManager *extension_manager = GDExtensionManager::get_singleton();
- p_extension->class_icon_paths[key] = icon_path;
- }
+ GDExtensionManager::LoadStatus status = extension_manager->load_extension(p_path);
+ if (status != GDExtensionManager::LOAD_STATUS_OK && status != GDExtensionManager::LOAD_STATUS_ALREADY_LOADED) {
+ // Errors already logged in load_extension().
+ return FAILED;
}
+ p_extension = extension_manager->get_extension(p_path);
return OK;
}
@@ -1094,16 +839,7 @@ String GDExtensionResourceLoader::get_resource_type(const String &p_path) const
#ifdef TOOLS_ENABLED
bool GDExtension::has_library_changed() const {
- // Check only that the last modified time is different (rather than checking
- // that it's newer) since some OS's (namely Windows) will preserve the modified
- // time by default when copying files.
- if (FileAccess::get_modified_time(get_path()) != resource_last_modified_time) {
- return true;
- }
- if (FileAccess::get_modified_time(library_path) != library_last_modified_time) {
- return true;
- }
- return false;
+ return loader->has_library_changed();
}
void GDExtension::prepare_reload() {
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index 9393e7399ba2..706bc7e18961 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -31,13 +31,11 @@
#ifndef GDEXTENSION_H
#define GDEXTENSION_H
-#include
-
#include "core/extension/gdextension_interface.h"
+#include "core/extension/gdextension_loader.h"
#include "core/io/config_file.h"
#include "core/io/resource_loader.h"
#include "core/object/ref_counted.h"
-#include "core/os/shared_object.h"
class GDExtensionMethodBind;
@@ -46,8 +44,8 @@ class GDExtension : public Resource {
friend class GDExtensionManager;
- void *library = nullptr; // pointer if valid,
- String library_path;
+ Ref loader;
+
bool reloadable = false;
struct Extension {
@@ -72,15 +70,18 @@ class GDExtension : public Resource {
#ifndef DISABLE_DEPRECATED
GDExtensionClassNotification notification_func = nullptr;
GDExtensionClassFreePropertyList free_property_list_func = nullptr;
+ GDExtensionClassCreateInstance create_instance_func = nullptr;
+ GDExtensionClassGetRID get_rid_func = nullptr;
#endif // DISABLE_DEPRECATED
};
#ifndef DISABLE_DEPRECATED
static void _register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs);
static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs);
-#endif // DISABLE_DEPRECATED
static void _register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs);
- static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr);
+#endif // DISABLE_DEPRECATED
+ static void _register_extension_class4(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs);
+ static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr);
static void _register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info);
static void _register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info);
static void _register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield);
@@ -96,8 +97,6 @@ class GDExtension : public Resource {
int32_t level_initialized = -1;
#ifdef TOOLS_ENABLED
- uint64_t resource_last_modified_time = 0;
- uint64_t library_last_modified_time = 0;
bool is_reloading = false;
Vector invalid_methods;
Vector instance_bindings;
@@ -124,11 +123,12 @@ class GDExtension : public Resource {
virtual bool editor_can_reload_from_file() override { return false; } // Reloading is handled in a special way.
static String get_extension_list_config_file();
- static String find_extension_library(const String &p_path, Ref p_config, std::function p_has_feature, PackedStringArray *r_tags = nullptr);
- static Vector find_extension_dependencies(const String &p_path, Ref p_config, std::function p_has_feature);
- Error open_library(const String &p_path, const String &p_entry_symbol, Vector *p_dependencies = nullptr);
+ const Ref get_loader() const { return loader; }
+
+ Error open_library(const String &p_path, const Ref &p_loader);
void close_library();
+ bool is_library_open() const;
enum InitializationLevel {
INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
@@ -146,17 +146,11 @@ class GDExtension : public Resource {
#endif
public:
- bool is_library_open() const;
-
#ifdef TOOLS_ENABLED
bool is_reloadable() const { return reloadable; }
void set_reloadable(bool p_reloadable) { reloadable = p_reloadable; }
bool has_library_changed() const;
- void update_last_modified_time(uint64_t p_resource_last_modified_time, uint64_t p_library_last_modified_time) {
- resource_last_modified_time = p_resource_last_modified_time;
- library_last_modified_time = p_library_last_modified_time;
- }
void track_instance_binding(Object *p_object);
void untrack_instance_binding(Object *p_object);
diff --git a/core/extension/gdextension_compat_hashes.cpp b/core/extension/gdextension_compat_hashes.cpp
index ebbf795070f7..b07f5b185893 100644
--- a/core/extension/gdextension_compat_hashes.cpp
+++ b/core/extension/gdextension_compat_hashes.cpp
@@ -103,6 +103,14 @@ void GDExtensionCompatHashes::initialize() {
mappings.insert("AcceptDialog", {
{ "add_button", 4158837846, 3328440682 },
});
+ mappings.insert("AnimatedSprite2D", {
+ { "play", 2372066587, 3269405555 },
+ { "play_backwards", 1421762485, 3323268493 },
+ });
+ mappings.insert("AnimatedSprite3D", {
+ { "play", 2372066587, 3269405555 },
+ { "play_backwards", 1421762485, 3323268493 },
+ });
mappings.insert("Animation", {
{ "add_track", 2393815928, 3843682357 },
{ "track_insert_key", 1985425300, 808952278 },
@@ -146,6 +154,12 @@ void GDExtensionCompatHashes::initialize() {
{ "travel", 3683006648, 3823612587 },
{ "start", 3683006648, 3823612587 },
});
+ mappings.insert("AnimationPlayer", {
+ { "play", 3697947785, 3118260607 },
+ { "play", 2221377757, 3118260607 },
+ { "play_backwards", 3890664824, 2787282401 },
+ { "play_with_capture", 3180464118, 1572969103 },
+ });
mappings.insert("ArrayMesh", {
{ "add_surface_from_arrays", 172284304, 1796411378 },
});
@@ -247,13 +261,20 @@ void GDExtensionCompatHashes::initialize() {
});
mappings.insert("DisplayServer", {
{ "global_menu_add_submenu_item", 3806306913, 2828985934 },
- { "global_menu_add_item", 3415468211, 3401266716 },
- { "global_menu_add_check_item", 3415468211, 3401266716 },
- { "global_menu_add_icon_item", 1700867534, 4245856523 },
- { "global_menu_add_icon_check_item", 1700867534, 4245856523 },
- { "global_menu_add_radio_check_item", 3415468211, 3401266716 },
- { "global_menu_add_icon_radio_check_item", 1700867534, 4245856523 },
- { "global_menu_add_multistate_item", 635750054, 3431222859 },
+ { "global_menu_add_item", 3415468211, 3616842746 },
+ { "global_menu_add_item", 3401266716, 3616842746 },
+ { "global_menu_add_check_item", 3415468211, 3616842746 },
+ { "global_menu_add_check_item", 3401266716, 3616842746 },
+ { "global_menu_add_icon_item", 1700867534, 3867083847 },
+ { "global_menu_add_icon_item", 4245856523, 3867083847 },
+ { "global_menu_add_icon_check_item", 1700867534, 3867083847 },
+ { "global_menu_add_icon_check_item", 4245856523, 3867083847 },
+ { "global_menu_add_radio_check_item", 3415468211, 3616842746 },
+ { "global_menu_add_radio_check_item", 3401266716, 3616842746 },
+ { "global_menu_add_icon_radio_check_item", 1700867534, 3867083847 },
+ { "global_menu_add_icon_radio_check_item", 4245856523, 3867083847 },
+ { "global_menu_add_multistate_item", 635750054, 3297554655 },
+ { "global_menu_add_multistate_item", 3431222859, 3297554655 },
{ "global_menu_add_separator", 1041533178, 3214812433 },
{ "tts_speak", 3741216677, 903992738 },
{ "is_touchscreen_available", 4162880507, 3323674545 },
@@ -286,6 +307,12 @@ void GDExtensionCompatHashes::initialize() {
{ "virtual_keyboard_show", 860410478, 3042891259 },
#endif
});
+ mappings.insert("EditorExportPlatform", {
+ { "export_project_files", 425454869, 1063735070 },
+ });
+ mappings.insert("EditorProperty", {
+ { "emit_changed", 3069422438, 1822500399 },
+ });
mappings.insert("ENetConnection", {
{ "create_host_bound", 866250949, 1515002313 },
{ "connect_to_host", 385984708, 2171300490 },
@@ -453,18 +480,35 @@ void GDExtensionCompatHashes::initialize() {
mappings.insert("MultiplayerAPI", {
{ "rpc", 1833408346, 2077486355 },
});
+ mappings.insert("NativeMenu", {
+ { "add_item", 2553375659, 980552939 },
+ { "add_check_item", 2553375659, 980552939 },
+ { "add_icon_item", 2987595282, 1372188274 },
+ { "add_icon_check_item", 2987595282, 1372188274 },
+ { "add_radio_check_item", 2553375659, 980552939 },
+ { "add_icon_radio_check_item", 2987595282, 1372188274 },
+ { "add_multistate_item", 1558592568, 2674635658 },
+ });
mappings.insert("NavigationMeshGenerator", {
- { "parse_source_geometry_data", 3703028813, 685862123 },
- { "bake_from_source_geometry_data", 3669016597, 2469318639 },
+ { "parse_source_geometry_data", 3703028813, 3172802542 },
+ { "parse_source_geometry_data", 685862123, 3172802542 },
+ { "bake_from_source_geometry_data", 3669016597, 1286748856 },
+ { "bake_from_source_geometry_data", 2469318639, 1286748856 },
});
mappings.insert("NavigationServer2D", {
{ "map_get_path", 56240621, 3146466012 },
+ { "parse_source_geometry_data", 1176164995, 1766905497 },
+ { "bake_from_source_geometry_data", 2909414286, 2179660022 },
+ { "bake_from_source_geometry_data_async", 2909414286, 2179660022 },
});
mappings.insert("NavigationServer3D", {
{ "map_get_path", 2121045993, 1187418690 },
- { "parse_source_geometry_data", 3703028813, 685862123 },
- { "bake_from_source_geometry_data", 3669016597, 2469318639 },
- { "bake_from_source_geometry_data_async", 3669016597, 2469318639 },
+ { "parse_source_geometry_data", 3703028813, 3172802542 },
+ { "parse_source_geometry_data", 685862123, 3172802542 },
+ { "bake_from_source_geometry_data", 3669016597, 1286748856 },
+ { "bake_from_source_geometry_data", 2469318639, 1286748856 },
+ { "bake_from_source_geometry_data_async", 3669016597, 1286748856 },
+ { "bake_from_source_geometry_data_async", 2469318639, 1286748856 },
});
mappings.insert("Node", {
{ "add_child", 3070154285, 3863233950 },
@@ -631,6 +675,11 @@ void GDExtensionCompatHashes::initialize() {
mappings.insert("ProjectSettings", {
{ "load_resource_pack", 3001721055, 708980503 },
});
+ mappings.insert("RDShaderFile", {
+ { "bake_from_source_geometry_data_async", 2469318639, 1286748856 },
+ { "set_bytecode", 1558064255, 1526857008 },
+ { "get_spirv", 3340165340, 2689310080 },
+ });
mappings.insert("RegEx", {
{ "search", 4087180739, 3365977994 },
{ "search_all", 3354100289, 849021363 },
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index 85f83eecfddf..66b016116020 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -507,6 +507,14 @@ static GDExtensionBool gdextension_variant_has_key(GDExtensionConstVariantPtr p_
return ret;
}
+static GDObjectInstanceID gdextension_variant_get_object_instance_id(GDExtensionConstVariantPtr p_self) {
+ const Variant *self = (const Variant *)p_self;
+ if (likely(self->get_type() == Variant::OBJECT)) {
+ return self->operator ObjectID();
+ }
+ return 0;
+}
+
static void gdextension_variant_get_type_name(GDExtensionVariantType p_type, GDExtensionUninitializedVariantPtr r_ret) {
String name = Variant::get_type_name((Variant::Type)p_type);
memnew_placement(r_ret, String(name));
@@ -1199,6 +1207,15 @@ static GDExtensionVariantPtr gdextension_dictionary_operator_index_const(GDExten
return (GDExtensionVariantPtr)&self->operator[](*(const Variant *)p_key);
}
+void gdextension_dictionary_set_typed(GDExtensionTypePtr p_self, GDExtensionVariantType p_key_type, GDExtensionConstStringNamePtr p_key_class_name, GDExtensionConstVariantPtr p_key_script, GDExtensionVariantType p_value_type, GDExtensionConstStringNamePtr p_value_class_name, GDExtensionConstVariantPtr p_value_script) {
+ Dictionary *self = reinterpret_cast(p_self);
+ const StringName *key_class_name = reinterpret_cast(p_key_class_name);
+ const Variant *key_script = reinterpret_cast(p_key_script);
+ const StringName *value_class_name = reinterpret_cast(p_value_class_name);
+ const Variant *value_script = reinterpret_cast(p_value_script);
+ self->set_typed((uint32_t)p_key_type, *key_class_name, *key_script, (uint32_t)p_value_type, *value_class_name, *value_script);
+}
+
/* OBJECT API */
static void gdextension_object_method_bind_call(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error) {
@@ -1299,7 +1316,7 @@ static void gdextension_object_call_script_method(GDExtensionObjectPtr p_object,
const StringName method = *reinterpret_cast(p_method);
const Variant **args = (const Variant **)p_args;
- Callable::CallError error;
+ Callable::CallError error; // TODO: Check `error`?
memnew_placement(r_return, Variant);
*(Variant *)r_return = o->callp(method, args, p_argument_count, error);
@@ -1515,10 +1532,17 @@ static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionC
return (GDExtensionMethodBindPtr)mb;
}
+#ifndef DISABLE_DEPRECATED
static GDExtensionObjectPtr gdextension_classdb_construct_object(GDExtensionConstStringNamePtr p_classname) {
const StringName classname = *reinterpret_cast(p_classname);
return (GDExtensionObjectPtr)ClassDB::instantiate_no_placeholders(classname);
}
+#endif
+
+static GDExtensionObjectPtr gdextension_classdb_construct_object2(GDExtensionConstStringNamePtr p_classname) {
+ const StringName classname = *reinterpret_cast(p_classname);
+ return (GDExtensionObjectPtr)ClassDB::instantiate_without_postinitialization(classname);
+}
static void *gdextension_classdb_get_class_tag(GDExtensionConstStringNamePtr p_classname) {
const StringName classname = *reinterpret_cast(p_classname);
@@ -1594,6 +1618,7 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(variant_has_method);
REGISTER_INTERFACE_FUNC(variant_has_member);
REGISTER_INTERFACE_FUNC(variant_has_key);
+ REGISTER_INTERFACE_FUNC(variant_get_object_instance_id);
REGISTER_INTERFACE_FUNC(variant_get_type_name);
REGISTER_INTERFACE_FUNC(variant_can_convert);
REGISTER_INTERFACE_FUNC(variant_can_convert_strict);
@@ -1672,6 +1697,7 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(array_set_typed);
REGISTER_INTERFACE_FUNC(dictionary_operator_index);
REGISTER_INTERFACE_FUNC(dictionary_operator_index_const);
+ REGISTER_INTERFACE_FUNC(dictionary_set_typed);
REGISTER_INTERFACE_FUNC(object_method_bind_call);
REGISTER_INTERFACE_FUNC(object_method_bind_ptrcall);
REGISTER_INTERFACE_FUNC(object_destroy);
@@ -1701,7 +1727,10 @@ void gdextension_setup_interface() {
#endif // DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(callable_custom_create2);
REGISTER_INTERFACE_FUNC(callable_custom_get_userdata);
+#ifndef DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(classdb_construct_object);
+#endif // DISABLE_DEPRECATED
+ REGISTER_INTERFACE_FUNC(classdb_construct_object2);
REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
REGISTER_INTERFACE_FUNC(classdb_get_class_tag);
REGISTER_INTERFACE_FUNC(editor_add_plugin);
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index fce377f96738..374dbfd07115 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -268,6 +268,7 @@ typedef void (*GDExtensionClassReference)(GDExtensionClassInstancePtr p_instance
typedef void (*GDExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance);
typedef void (*GDExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance)(void *p_class_userdata);
+typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance2)(void *p_class_userdata, GDExtensionBool p_notify_postinitialize);
typedef void (*GDExtensionClassFreeInstance)(void *p_class_userdata, GDExtensionClassInstancePtr p_instance);
typedef GDExtensionClassInstancePtr (*GDExtensionClassRecreateInstance)(void *p_class_userdata, GDExtensionObjectPtr p_object);
typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
@@ -292,7 +293,7 @@ typedef struct {
GDExtensionClassGetVirtual get_virtual_func; // Queries a virtual function by name and returns a callback to invoke the requested virtual function.
GDExtensionClassGetRID get_rid_func;
void *class_userdata; // Per-class user data, later accessible in instance bindings.
-} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo3 instead.
+} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo4 instead.
typedef struct {
GDExtensionBool is_virtual;
@@ -325,7 +326,7 @@ typedef struct {
GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
GDExtensionClassGetRID get_rid_func;
void *class_userdata; // Per-class user data, later accessible in instance bindings.
-} GDExtensionClassCreationInfo2; // Deprecated. Use GDExtensionClassCreationInfo3 instead.
+} GDExtensionClassCreationInfo2; // Deprecated. Use GDExtensionClassCreationInfo4 instead.
typedef struct {
GDExtensionBool is_virtual;
@@ -359,7 +360,40 @@ typedef struct {
GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
GDExtensionClassGetRID get_rid_func;
void *class_userdata; // Per-class user data, later accessible in instance bindings.
-} GDExtensionClassCreationInfo3;
+} GDExtensionClassCreationInfo3; // Deprecated. Use GDExtensionClassCreationInfo4 instead.
+
+typedef struct {
+ GDExtensionBool is_virtual;
+ GDExtensionBool is_abstract;
+ GDExtensionBool is_exposed;
+ GDExtensionBool is_runtime;
+ GDExtensionClassSet set_func;
+ GDExtensionClassGet get_func;
+ GDExtensionClassGetPropertyList get_property_list_func;
+ GDExtensionClassFreePropertyList2 free_property_list_func;
+ GDExtensionClassPropertyCanRevert property_can_revert_func;
+ GDExtensionClassPropertyGetRevert property_get_revert_func;
+ GDExtensionClassValidateProperty validate_property_func;
+ GDExtensionClassNotification2 notification_func;
+ GDExtensionClassToString to_string_func;
+ GDExtensionClassReference reference_func;
+ GDExtensionClassUnreference unreference_func;
+ GDExtensionClassCreateInstance2 create_instance_func; // (Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract.
+ GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory.
+ GDExtensionClassRecreateInstance recreate_instance_func;
+ // Queries a virtual function by name and returns a callback to invoke the requested virtual function.
+ GDExtensionClassGetVirtual get_virtual_func;
+ // Paired with `call_virtual_with_data_func`, this is an alternative to `get_virtual_func` for extensions that
+ // need or benefit from extra data when calling virtual functions.
+ // Returns user data that will be passed to `call_virtual_with_data_func`.
+ // Returning `NULL` from this function signals to Godot that the virtual function is not overridden.
+ // Data returned from this function should be managed by the extension and must be valid until the extension is deinitialized.
+ // You should supply either `get_virtual_func`, or `get_virtual_call_data_func` with `call_virtual_with_data_func`.
+ GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
+ // Used to call virtual functions when `get_virtual_call_data_func` is not null.
+ GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
+ void *class_userdata; // Per-class user data, later accessible in instance bindings.
+} GDExtensionClassCreationInfo4;
typedef void *GDExtensionClassLibraryPtr;
@@ -386,7 +420,9 @@ typedef enum {
GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32,
GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64,
GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT,
- GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE
+ GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE,
+ GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_CHAR16,
+ GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_CHAR32,
} GDExtensionClassMethodArgumentMetadata;
typedef void (*GDExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
@@ -1271,6 +1307,21 @@ typedef GDExtensionBool (*GDExtensionInterfaceVariantHasMember)(GDExtensionVaria
*/
typedef GDExtensionBool (*GDExtensionInterfaceVariantHasKey)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionBool *r_valid);
+/**
+ * @name variant_get_object_instance_id
+ * @since 4.4
+ *
+ * Gets the object instance ID from a variant of type GDEXTENSION_VARIANT_TYPE_OBJECT.
+ *
+ * If the variant isn't of type GDEXTENSION_VARIANT_TYPE_OBJECT, then zero will be returned.
+ * The instance ID will be returned even if the object is no longer valid - use `object_get_instance_by_id()` to check if the object is still valid.
+ *
+ * @param p_self A pointer to the Variant.
+ *
+ * @return The instance ID for the contained object.
+ */
+typedef GDObjectInstanceID (*GDExtensionInterfaceVariantGetObjectInstanceId)(GDExtensionConstVariantPtr p_self);
+
/**
* @name variant_get_type_name
* @since 4.1
@@ -2337,6 +2388,22 @@ typedef GDExtensionVariantPtr (*GDExtensionInterfaceDictionaryOperatorIndex)(GDE
*/
typedef GDExtensionVariantPtr (*GDExtensionInterfaceDictionaryOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionConstVariantPtr p_key);
+/**
+ * @name dictionary_set_typed
+ * @since 4.4
+ *
+ * Makes a Dictionary into a typed Dictionary.
+ *
+ * @param p_self A pointer to the Dictionary.
+ * @param p_key_type The type of Variant the Dictionary key will store.
+ * @param p_key_class_name A pointer to a StringName with the name of the object (if p_key_type is GDEXTENSION_VARIANT_TYPE_OBJECT).
+ * @param p_key_script A pointer to a Script object (if p_key_type is GDEXTENSION_VARIANT_TYPE_OBJECT and the base class is extended by a script).
+ * @param p_value_type The type of Variant the Dictionary value will store.
+ * @param p_value_class_name A pointer to a StringName with the name of the object (if p_value_type is GDEXTENSION_VARIANT_TYPE_OBJECT).
+ * @param p_value_script A pointer to a Script object (if p_value_type is GDEXTENSION_VARIANT_TYPE_OBJECT and the base class is extended by a script).
+ */
+typedef void (*GDExtensionInterfaceDictionarySetTyped)(GDExtensionTypePtr p_self, GDExtensionVariantType p_key_type, GDExtensionConstStringNamePtr p_key_class_name, GDExtensionConstVariantPtr p_key_script, GDExtensionVariantType p_value_type, GDExtensionConstStringNamePtr p_value_class_name, GDExtensionConstVariantPtr p_value_script);
+
/* INTERFACE: Object */
/**
@@ -2680,6 +2747,7 @@ typedef void *(*GDExtensionInterfaceCallableCustomGetUserData)(GDExtensionConstT
/**
* @name classdb_construct_object
* @since 4.1
+ * @deprecated in Godot 4.4. Use `classdb_construct_object2` instead.
*
* Constructs an Object of the requested class.
*
@@ -2691,6 +2759,22 @@ typedef void *(*GDExtensionInterfaceCallableCustomGetUserData)(GDExtensionConstT
*/
typedef GDExtensionObjectPtr (*GDExtensionInterfaceClassdbConstructObject)(GDExtensionConstStringNamePtr p_classname);
+/**
+ * @name classdb_construct_object2
+ * @since 4.4
+ *
+ * Constructs an Object of the requested class.
+ *
+ * The passed class must be a built-in godot class, or an already-registered extension class. In both cases, object_set_instance() should be called to fully initialize the object.
+ *
+ * "NOTIFICATION_POSTINITIALIZE" must be sent after construction.
+ *
+ * @param p_classname A pointer to a StringName with the class name.
+ *
+ * @return A pointer to the newly created Object.
+ */
+typedef GDExtensionObjectPtr (*GDExtensionInterfaceClassdbConstructObject2)(GDExtensionConstStringNamePtr p_classname);
+
/**
* @name classdb_get_method_bind
* @since 4.1
@@ -2722,7 +2806,7 @@ typedef void *(*GDExtensionInterfaceClassdbGetClassTag)(GDExtensionConstStringNa
/**
* @name classdb_register_extension_class
* @since 4.1
- * @deprecated in Godot 4.2. Use `classdb_register_extension_class3` instead.
+ * @deprecated in Godot 4.2. Use `classdb_register_extension_class4` instead.
*
* Registers an extension class in the ClassDB.
*
@@ -2738,7 +2822,7 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass)(GDExtensionCla
/**
* @name classdb_register_extension_class2
* @since 4.2
- * @deprecated in Godot 4.3. Use `classdb_register_extension_class3` instead.
+ * @deprecated in Godot 4.3. Use `classdb_register_extension_class4` instead.
*
* Registers an extension class in the ClassDB.
*
@@ -2754,6 +2838,7 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionCl
/**
* @name classdb_register_extension_class3
* @since 4.3
+ * @deprecated in Godot 4.4. Use `classdb_register_extension_class4` instead.
*
* Registers an extension class in the ClassDB.
*
@@ -2766,6 +2851,21 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionCl
*/
typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass3)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs);
+/**
+ * @name classdb_register_extension_class4
+ * @since 4.4
+ *
+ * Registers an extension class in the ClassDB.
+ *
+ * Provided struct can be safely freed once the function returns.
+ *
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param p_class_name A pointer to a StringName with the class name.
+ * @param p_parent_class_name A pointer to a StringName with the parent class name.
+ * @param p_extension_funcs A pointer to a GDExtensionClassCreationInfo2 struct.
+ */
+typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass4)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs);
+
/**
* @name classdb_register_extension_class_method
* @since 4.1
diff --git a/core/extension/gdextension_library_loader.cpp b/core/extension/gdextension_library_loader.cpp
new file mode 100644
index 000000000000..d5f2eb668f55
--- /dev/null
+++ b/core/extension/gdextension_library_loader.cpp
@@ -0,0 +1,394 @@
+/**************************************************************************/
+/* gdextension_library_loader.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "gdextension_library_loader.h"
+
+#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
+#include "core/version.h"
+#include "gdextension.h"
+
+Vector GDExtensionLibraryLoader::find_extension_dependencies(const String &p_path, Ref p_config, std::function p_has_feature) {
+ Vector dependencies_shared_objects;
+ if (p_config->has_section("dependencies")) {
+ List config_dependencies;
+ p_config->get_section_keys("dependencies", &config_dependencies);
+
+ for (const String &dependency : config_dependencies) {
+ Vector dependency_tags = dependency.split(".");
+ bool all_tags_met = true;
+ for (int i = 0; i < dependency_tags.size(); i++) {
+ String tag = dependency_tags[i].strip_edges();
+ if (!p_has_feature(tag)) {
+ all_tags_met = false;
+ break;
+ }
+ }
+
+ if (all_tags_met) {
+ Dictionary dependency_value = p_config->get_value("dependencies", dependency);
+ for (const Variant *key = dependency_value.next(nullptr); key; key = dependency_value.next(key)) {
+ String dependency_path = *key;
+ String target_path = dependency_value[*key];
+ if (dependency_path.is_relative_path()) {
+ dependency_path = p_path.get_base_dir().path_join(dependency_path);
+ }
+ dependencies_shared_objects.push_back(SharedObject(dependency_path, dependency_tags, target_path));
+ }
+ break;
+ }
+ }
+ }
+
+ return dependencies_shared_objects;
+}
+
+String GDExtensionLibraryLoader::find_extension_library(const String &p_path, Ref p_config, std::function p_has_feature, PackedStringArray *r_tags) {
+ // First, check the explicit libraries.
+ if (p_config->has_section("libraries")) {
+ List libraries;
+ p_config->get_section_keys("libraries", &libraries);
+
+ // Iterate the libraries, finding the best matching tags.
+ String best_library_path;
+ Vector best_library_tags;
+ for (const String &E : libraries) {
+ Vector tags = E.split(".");
+ bool all_tags_met = true;
+ for (int i = 0; i < tags.size(); i++) {
+ String tag = tags[i].strip_edges();
+ if (!p_has_feature(tag)) {
+ all_tags_met = false;
+ break;
+ }
+ }
+
+ if (all_tags_met && tags.size() > best_library_tags.size()) {
+ best_library_path = p_config->get_value("libraries", E);
+ best_library_tags = tags;
+ }
+ }
+
+ if (!best_library_path.is_empty()) {
+ if (best_library_path.is_relative_path()) {
+ best_library_path = p_path.get_base_dir().path_join(best_library_path);
+ }
+ if (r_tags != nullptr) {
+ r_tags->append_array(best_library_tags);
+ }
+ return best_library_path;
+ }
+ }
+
+ // Second, try to autodetect.
+ String autodetect_library_prefix;
+ if (p_config->has_section_key("configuration", "autodetect_library_prefix")) {
+ autodetect_library_prefix = p_config->get_value("configuration", "autodetect_library_prefix");
+ }
+ if (!autodetect_library_prefix.is_empty()) {
+ String autodetect_path = autodetect_library_prefix;
+ if (autodetect_path.is_relative_path()) {
+ autodetect_path = p_path.get_base_dir().path_join(autodetect_path);
+ }
+
+ // Find the folder and file parts of the prefix.
+ String folder;
+ String file_prefix;
+ if (DirAccess::dir_exists_absolute(autodetect_path)) {
+ folder = autodetect_path;
+ } else if (DirAccess::dir_exists_absolute(autodetect_path.get_base_dir())) {
+ folder = autodetect_path.get_base_dir();
+ file_prefix = autodetect_path.get_file();
+ } else {
+ ERR_FAIL_V_MSG(String(), vformat("Error in extension: %s. Could not find folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
+ }
+
+ // Open the folder.
+ Ref dir = DirAccess::open(folder);
+ ERR_FAIL_COND_V_MSG(dir.is_null(), String(), vformat("Error in extension: %s. Could not open folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
+
+ // Iterate the files and check the prefixes, finding the best matching file.
+ String best_file;
+ Vector best_file_tags;
+ dir->list_dir_begin();
+ String file_name = dir->_get_next();
+ while (file_name != "") {
+ if (!dir->current_is_dir() && file_name.begins_with(file_prefix)) {
+ // Check if the files matches all requested feature tags.
+ String tags_str = file_name.trim_prefix(file_prefix);
+ tags_str = tags_str.trim_suffix(tags_str.get_extension());
+
+ Vector tags = tags_str.split(".", false);
+ bool all_tags_met = true;
+ for (int i = 0; i < tags.size(); i++) {
+ String tag = tags[i].strip_edges();
+ if (!p_has_feature(tag)) {
+ all_tags_met = false;
+ break;
+ }
+ }
+
+ // If all tags are found in the feature list, and we found more tags than before, use this file.
+ if (all_tags_met && tags.size() > best_file_tags.size()) {
+ best_file_tags = tags;
+ best_file = file_name;
+ }
+ }
+ file_name = dir->_get_next();
+ }
+
+ if (!best_file.is_empty()) {
+ String library_path = folder.path_join(best_file);
+ if (r_tags != nullptr) {
+ r_tags->append_array(best_file_tags);
+ }
+ return library_path;
+ }
+ }
+ return String();
+}
+
+Error GDExtensionLibraryLoader::open_library(const String &p_path) {
+ Error err = parse_gdextension_file(p_path);
+ if (err != OK) {
+ return err;
+ }
+
+ String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path);
+
+ Vector abs_dependencies_paths;
+ if (!library_dependencies.is_empty()) {
+ for (const SharedObject &dependency : library_dependencies) {
+ abs_dependencies_paths.push_back(ProjectSettings::get_singleton()->globalize_path(dependency.path));
+ }
+ }
+
+ OS::GDExtensionData data = {
+ true, // also_set_library_path
+ &library_path, // r_resolved_path
+ Engine::get_singleton()->is_editor_hint(), // generate_temp_files
+ &abs_dependencies_paths, // library_dependencies
+ };
+
+ err = OS::get_singleton()->open_dynamic_library(is_static_library ? String() : abs_path, library, &data);
+ if (err != OK) {
+ return err;
+ }
+
+ return OK;
+}
+
+Error GDExtensionLibraryLoader::initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref &p_extension, GDExtensionInitialization *r_initialization) {
+#ifdef TOOLS_ENABLED
+ p_extension->set_reloadable(is_reloadable && Engine::get_singleton()->is_extension_reloading_enabled());
+#endif
+
+ for (const KeyValue &icon : class_icon_paths) {
+ p_extension->class_icon_paths[icon.key] = icon.value;
+ }
+
+ void *entry_funcptr = nullptr;
+
+ Error err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, entry_symbol, entry_funcptr, false);
+
+ if (err != OK) {
+ ERR_PRINT("GDExtension entry point '" + entry_symbol + "' not found in library " + library_path);
+ return err;
+ }
+
+ GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr;
+
+ GDExtensionBool ret = initialization_function(p_get_proc_address, p_extension.ptr(), r_initialization);
+
+ if (ret) {
+ return OK;
+ } else {
+ ERR_PRINT("GDExtension initialization function '" + entry_symbol + "' returned an error.");
+ return FAILED;
+ }
+}
+
+void GDExtensionLibraryLoader::close_library() {
+ OS::get_singleton()->close_dynamic_library(library);
+ library = nullptr;
+}
+
+bool GDExtensionLibraryLoader::is_library_open() const {
+ return library != nullptr;
+}
+
+bool GDExtensionLibraryLoader::has_library_changed() const {
+#ifdef TOOLS_ENABLED
+ // Check only that the last modified time is different (rather than checking
+ // that it's newer) since some OS's (namely Windows) will preserve the modified
+ // time by default when copying files.
+ if (FileAccess::get_modified_time(resource_path) != resource_last_modified_time) {
+ return true;
+ }
+ if (FileAccess::get_modified_time(library_path) != library_last_modified_time) {
+ return true;
+ }
+#endif
+ return false;
+}
+
+bool GDExtensionLibraryLoader::library_exists() const {
+ return FileAccess::exists(resource_path);
+}
+
+Error GDExtensionLibraryLoader::parse_gdextension_file(const String &p_path) {
+ resource_path = p_path;
+
+ Ref config;
+ config.instantiate();
+
+ Error err = config->load(p_path);
+
+ if (err != OK) {
+ ERR_PRINT("Error loading GDExtension configuration file: " + p_path);
+ return err;
+ }
+
+ if (!config->has_section_key("configuration", "entry_symbol")) {
+ ERR_PRINT("GDExtension configuration file must contain a \"configuration/entry_symbol\" key: " + p_path);
+ return ERR_INVALID_DATA;
+ }
+
+ entry_symbol = config->get_value("configuration", "entry_symbol");
+
+ uint32_t compatibility_minimum[3] = { 0, 0, 0 };
+ if (config->has_section_key("configuration", "compatibility_minimum")) {
+ String compat_string = config->get_value("configuration", "compatibility_minimum");
+ Vector parts = compat_string.split_ints(".");
+ for (int i = 0; i < parts.size(); i++) {
+ if (i >= 3) {
+ break;
+ }
+ if (parts[i] >= 0) {
+ compatibility_minimum[i] = parts[i];
+ }
+ }
+ } else {
+ ERR_PRINT("GDExtension configuration file must contain a \"configuration/compatibility_minimum\" key: " + p_path);
+ return ERR_INVALID_DATA;
+ }
+
+ if (compatibility_minimum[0] < 4 || (compatibility_minimum[0] == 4 && compatibility_minimum[1] == 0)) {
+ ERR_PRINT(vformat("GDExtension's compatibility_minimum (%d.%d.%d) must be at least 4.1.0: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
+ return ERR_INVALID_DATA;
+ }
+
+ bool compatible = true;
+ // Check version lexicographically.
+ if (VERSION_MAJOR != compatibility_minimum[0]) {
+ compatible = VERSION_MAJOR > compatibility_minimum[0];
+ } else if (VERSION_MINOR != compatibility_minimum[1]) {
+ compatible = VERSION_MINOR > compatibility_minimum[1];
+ } else {
+ compatible = VERSION_PATCH >= compatibility_minimum[2];
+ }
+ if (!compatible) {
+ ERR_PRINT(vformat("GDExtension only compatible with Godot version %d.%d.%d or later: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
+ return ERR_INVALID_DATA;
+ }
+
+ // Optionally check maximum compatibility.
+ if (config->has_section_key("configuration", "compatibility_maximum")) {
+ uint32_t compatibility_maximum[3] = { 0, 0, 0 };
+ String compat_string = config->get_value("configuration", "compatibility_maximum");
+ Vector parts = compat_string.split_ints(".");
+ for (int i = 0; i < 3; i++) {
+ if (i < parts.size() && parts[i] >= 0) {
+ compatibility_maximum[i] = parts[i];
+ } else {
+ // If a version part is missing, set the maximum to an arbitrary high value.
+ compatibility_maximum[i] = 9999;
+ }
+ }
+
+ compatible = true;
+ if (VERSION_MAJOR != compatibility_maximum[0]) {
+ compatible = VERSION_MAJOR < compatibility_maximum[0];
+ } else if (VERSION_MINOR != compatibility_maximum[1]) {
+ compatible = VERSION_MINOR < compatibility_maximum[1];
+ }
+#if VERSION_PATCH
+ // #if check to avoid -Wtype-limits warning when 0.
+ else {
+ compatible = VERSION_PATCH <= compatibility_maximum[2];
+ }
+#endif
+
+ if (!compatible) {
+ ERR_PRINT(vformat("GDExtension only compatible with Godot version %s or earlier: %s", compat_string, p_path));
+ return ERR_INVALID_DATA;
+ }
+ }
+
+ library_path = find_extension_library(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
+
+ if (library_path.is_empty()) {
+ const String os_arch = OS::get_singleton()->get_name().to_lower() + "." + Engine::get_singleton()->get_architecture_name();
+ ERR_PRINT(vformat("No GDExtension library found for current OS and architecture (%s) in configuration file: %s", os_arch, p_path));
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ is_static_library = library_path.ends_with(".a") || library_path.ends_with(".xcframework");
+
+ if (!library_path.is_resource_file() && !library_path.is_absolute_path()) {
+ library_path = p_path.get_base_dir().path_join(library_path);
+ }
+
+#ifdef TOOLS_ENABLED
+ is_reloadable = config->get_value("configuration", "reloadable", false);
+
+ update_last_modified_time(
+ FileAccess::get_modified_time(resource_path),
+ FileAccess::get_modified_time(library_path));
+#endif
+
+ library_dependencies = find_extension_dependencies(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
+
+ // Handle icons if any are specified.
+ if (config->has_section("icons")) {
+ List keys;
+ config->get_section_keys("icons", &keys);
+ for (const String &key : keys) {
+ String icon_path = config->get_value("icons", key);
+ if (icon_path.is_relative_path()) {
+ icon_path = p_path.get_base_dir().path_join(icon_path);
+ }
+
+ class_icon_paths[key] = icon_path;
+ }
+ }
+
+ return OK;
+}
diff --git a/core/extension/gdextension_library_loader.h b/core/extension/gdextension_library_loader.h
new file mode 100644
index 000000000000..f781611b3058
--- /dev/null
+++ b/core/extension/gdextension_library_loader.h
@@ -0,0 +1,85 @@
+/**************************************************************************/
+/* gdextension_library_loader.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef GDEXTENSION_LIBRARY_LOADER_H
+#define GDEXTENSION_LIBRARY_LOADER_H
+
+#include
+
+#include "core/extension/gdextension_loader.h"
+#include "core/io/config_file.h"
+#include "core/os/shared_object.h"
+
+class GDExtensionLibraryLoader : public GDExtensionLoader {
+ friend class GDExtensionManager;
+ friend class GDExtension;
+
+private:
+ String resource_path;
+
+ void *library = nullptr; // pointer if valid.
+ String library_path;
+ String entry_symbol;
+
+ bool is_static_library = false;
+
+#ifdef TOOLS_ENABLED
+ bool is_reloadable = false;
+#endif
+
+ Vector library_dependencies;
+
+ HashMap class_icon_paths;
+
+#ifdef TOOLS_ENABLED
+ uint64_t resource_last_modified_time = 0;
+ uint64_t library_last_modified_time = 0;
+
+ void update_last_modified_time(uint64_t p_resource_last_modified_time, uint64_t p_library_last_modified_time) {
+ resource_last_modified_time = p_resource_last_modified_time;
+ library_last_modified_time = p_library_last_modified_time;
+ }
+#endif
+
+public:
+ static String find_extension_library(const String &p_path, Ref p_config, std::function p_has_feature, PackedStringArray *r_tags = nullptr);
+ static Vector find_extension_dependencies(const String &p_path, Ref p_config, std::function p_has_feature);
+
+ virtual Error open_library(const String &p_path) override;
+ virtual Error initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref &p_extension, GDExtensionInitialization *r_initialization) override;
+ virtual void close_library() override;
+ virtual bool is_library_open() const override;
+ virtual bool has_library_changed() const override;
+ virtual bool library_exists() const override;
+
+ Error parse_gdextension_file(const String &p_path);
+};
+
+#endif // GDEXTENSION_LIBRARY_LOADER_H
diff --git a/core/extension/gdextension_loader.h b/core/extension/gdextension_loader.h
new file mode 100644
index 000000000000..22895503291d
--- /dev/null
+++ b/core/extension/gdextension_loader.h
@@ -0,0 +1,48 @@
+/**************************************************************************/
+/* gdextension_loader.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef GDEXTENSION_LOADER_H
+#define GDEXTENSION_LOADER_H
+
+#include "core/object/ref_counted.h"
+
+class GDExtension;
+
+class GDExtensionLoader : public RefCounted {
+public:
+ virtual Error open_library(const String &p_path) = 0;
+ virtual Error initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref &p_extension, GDExtensionInitialization *r_initialization) = 0;
+ virtual void close_library() = 0;
+ virtual bool is_library_open() const = 0;
+ virtual bool has_library_changed() const = 0;
+ virtual bool library_exists() const = 0;
+};
+
+#endif // GDEXTENSION_LOADER_H
diff --git a/core/extension/gdextension_manager.cpp b/core/extension/gdextension_manager.cpp
index 1ee9de077689..fff938858fd3 100644
--- a/core/extension/gdextension_manager.cpp
+++ b/core/extension/gdextension_manager.cpp
@@ -31,14 +31,19 @@
#include "gdextension_manager.h"
#include "core/extension/gdextension_compat_hashes.h"
+#include "core/extension/gdextension_library_loader.h"
+#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "core/object/script_language.h"
-GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref &p_extension) {
+GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref &p_extension, bool p_first_load) {
if (level >= 0) { // Already initialized up to some level.
- int32_t minimum_level = p_extension->get_minimum_library_initialization_level();
- if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) {
- return LOAD_STATUS_NEEDS_RESTART;
+ int32_t minimum_level = 0;
+ if (!p_first_load) {
+ minimum_level = p_extension->get_minimum_library_initialization_level();
+ if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) {
+ return LOAD_STATUS_NEEDS_RESTART;
+ }
}
// Initialize up to current level.
for (int32_t i = minimum_level; i <= level; i++) {
@@ -50,10 +55,20 @@ GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(cons
gdextension_class_icon_paths[kv.key] = kv.value;
}
+#ifdef TOOLS_ENABLED
+ // Signals that a new extension is loaded so GDScript can register new class names.
+ emit_signal("extension_loaded", p_extension);
+#endif
+
return LOAD_STATUS_OK;
}
GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(const Ref &p_extension) {
+#ifdef TOOLS_ENABLED
+ // Signals that a new extension is unloading so GDScript can unregister class names.
+ emit_signal("extension_unloading", p_extension);
+#endif
+
if (level >= 0) { // Already initialized up to some level.
// Deinitialize down from current level.
for (int32_t i = level; i >= GDExtension::INITIALIZATION_LEVEL_CORE; i--) {
@@ -69,19 +84,31 @@ GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(co
}
GDExtensionManager::LoadStatus GDExtensionManager::load_extension(const String &p_path) {
+ Ref loader;
+ loader.instantiate();
+ return GDExtensionManager::get_singleton()->load_extension_with_loader(p_path, loader);
+}
+
+GDExtensionManager::LoadStatus GDExtensionManager::load_extension_with_loader(const String &p_path, const Ref &p_loader) {
+ DEV_ASSERT(p_loader.is_valid());
+
if (gdextension_map.has(p_path)) {
return LOAD_STATUS_ALREADY_LOADED;
}
- Ref extension = ResourceLoader::load(p_path);
- if (extension.is_null()) {
+
+ Ref extension;
+ extension.instantiate();
+ Error err = extension->open_library(p_path, p_loader);
+ if (err != OK) {
return LOAD_STATUS_FAILED;
}
- LoadStatus status = _load_extension_internal(extension);
+ LoadStatus status = _load_extension_internal(extension, true);
if (status != LOAD_STATUS_OK) {
return status;
}
+ extension->set_path(p_path);
gdextension_map[p_path] = extension;
return LOAD_STATUS_OK;
}
@@ -117,12 +144,12 @@ GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String
extension->close_library();
}
- Error err = GDExtensionResourceLoader::load_gdextension_resource(p_path, extension);
+ Error err = extension->open_library(p_path, extension->loader);
if (err != OK) {
return LOAD_STATUS_FAILED;
}
- status = _load_extension_internal(extension);
+ status = _load_extension_internal(extension, false);
if (status != LOAD_STATUS_OK) {
return status;
}
@@ -261,6 +288,72 @@ void GDExtensionManager::reload_extensions() {
#endif
}
+bool GDExtensionManager::ensure_extensions_loaded(const HashSet &p_extensions) {
+ Vector extensions_added;
+ Vector extensions_removed;
+
+ for (const String &E : p_extensions) {
+ if (!is_extension_loaded(E)) {
+ extensions_added.push_back(E);
+ }
+ }
+
+ Vector loaded_extensions = get_loaded_extensions();
+ for (const String &loaded_extension : loaded_extensions) {
+ if (!p_extensions.has(loaded_extension)) {
+ // The extension may not have a .gdextension file.
+ const Ref extension = GDExtensionManager::get_singleton()->get_extension(loaded_extension);
+ if (!extension->get_loader()->library_exists()) {
+ extensions_removed.push_back(loaded_extension);
+ }
+ }
+ }
+
+ String extension_list_config_file = GDExtension::get_extension_list_config_file();
+ if (p_extensions.size()) {
+ if (extensions_added.size() || extensions_removed.size()) {
+ // Extensions were added or removed.
+ Ref f = FileAccess::open(extension_list_config_file, FileAccess::WRITE);
+ for (const String &E : p_extensions) {
+ f->store_line(E);
+ }
+ }
+ } else {
+ if (loaded_extensions.size() || FileAccess::exists(extension_list_config_file)) {
+ // Extensions were removed.
+ Ref da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ da->remove(extension_list_config_file);
+ }
+ }
+
+ bool needs_restart = false;
+ for (const String &extension : extensions_added) {
+ GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->load_extension(extension);
+ if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) {
+ needs_restart = true;
+ }
+ }
+
+ for (const String &extension : extensions_removed) {
+ GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->unload_extension(extension);
+ if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) {
+ needs_restart = true;
+ }
+ }
+
+#ifdef TOOLS_ENABLED
+ if (extensions_added.size() || extensions_removed.size()) {
+ // Emitting extensions_reloaded so EditorNode can reload Inspector and regenerate documentation.
+ emit_signal("extensions_reloaded");
+
+ // Reload all scripts to clear out old references.
+ callable_mp_static(&GDExtensionManager::_reload_all_scripts).call_deferred();
+ }
+#endif
+
+ return needs_restart;
+}
+
GDExtensionManager *GDExtensionManager::get_singleton() {
return singleton;
}
@@ -281,6 +374,8 @@ void GDExtensionManager::_bind_methods() {
BIND_ENUM_CONSTANT(LOAD_STATUS_NEEDS_RESTART);
ADD_SIGNAL(MethodInfo("extensions_reloaded"));
+ ADD_SIGNAL(MethodInfo("extension_loaded", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension")));
+ ADD_SIGNAL(MethodInfo("extension_unloading", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension")));
}
GDExtensionManager *GDExtensionManager::singleton = nullptr;
diff --git a/core/extension/gdextension_manager.h b/core/extension/gdextension_manager.h
index 9386e356bbec..39a600474cda 100644
--- a/core/extension/gdextension_manager.h
+++ b/core/extension/gdextension_manager.h
@@ -54,7 +54,7 @@ class GDExtensionManager : public Object {
};
private:
- LoadStatus _load_extension_internal(const Ref &p_extension);
+ LoadStatus _load_extension_internal(const Ref &p_extension, bool p_first_load);
LoadStatus _unload_extension_internal(const Ref &p_extension);
#ifdef TOOLS_ENABLED
@@ -63,6 +63,7 @@ class GDExtensionManager : public Object {
public:
LoadStatus load_extension(const String &p_path);
+ LoadStatus load_extension_with_loader(const String &p_path, const Ref &p_loader);
LoadStatus reload_extension(const String &p_path);
LoadStatus unload_extension(const String &p_path);
bool is_extension_loaded(const String &p_path) const;
@@ -84,6 +85,7 @@ class GDExtensionManager : public Object {
void load_extensions();
void reload_extensions();
+ bool ensure_extensions_loaded(const HashSet &p_extensions);
GDExtensionManager();
~GDExtensionManager();
diff --git a/core/extension/make_wrappers.py b/core/extension/make_wrappers.py
index 54f4fd5579d2..665b6f0f91d5 100644
--- a/core/extension/make_wrappers.py
+++ b/core/extension/make_wrappers.py
@@ -55,10 +55,10 @@ def generate_mod_version(argcount, const=False, returns=False):
proto_ex = """
#define EXBIND$VER($RETTYPE m_name$ARG) \\
-GDVIRTUAL$VER($RETTYPE_##m_name$ARG)\\
+GDVIRTUAL$VER_REQUIRED($RETTYPE_##m_name$ARG)\\
virtual $RETVAL m_name($FUNCARGS) $CONST override { \\
$RETPRE\\
- GDVIRTUAL_REQUIRED_CALL(_##m_name$CALLARGS$RETREF);\\
+ GDVIRTUAL_CALL(_##m_name$CALLARGS$RETREF);\\
$RETPOST\\
}
"""
diff --git a/core/input/SCsub b/core/input/SCsub
index d8e6f33156b2..521f7702e4e2 100644
--- a/core/input/SCsub
+++ b/core/input/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/input/godotcontrollerdb.txt b/core/input/godotcontrollerdb.txt
index f5158bfabbfb..8e8ec4c7186a 100644
--- a/core/input/godotcontrollerdb.txt
+++ b/core/input/godotcontrollerdb.txt
@@ -8,7 +8,7 @@ __XINPUT_DEVICE__,XInput Gamepad,a:b12,b:b13,x:b14,y:b15,start:b4,guide:b10,back
Default Android Gamepad,Default Controller,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a4,x:b2,dpup:h0.1,back:b4,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a5,b:b1,platform:Android,
# Web
-standard,Standard Gamepad Mapping,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,leftstick:b10,rightstick:b11,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,guide:b16,leftstick:b10,rightstick:b11,platform:Web,
+standard,Standard Gamepad Mapping,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:+a4,righttrigger:+a5,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,leftstick:b10,rightstick:b11,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,guide:b16,leftstick:b10,rightstick:b11,platform:Web,
Linux24c6581a,PowerA Xbox One Cabled,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web,
Linux0e6f0301,Logic 3 Controller (xbox compatible),a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web,
Linux045e028e,Microsoft X-Box 360 pad,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web,
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 91378591b0a0..eba7ded267ba 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -513,21 +513,49 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, const String &p_
Vector3 Input::get_gravity() const {
_THREAD_SAFE_METHOD_
+
+#ifdef DEBUG_ENABLED
+ if (!gravity_enabled) {
+ WARN_PRINT_ONCE("`input_devices/sensors/enable_gravity` is not enabled in project settings.");
+ }
+#endif
+
return gravity;
}
Vector3 Input::get_accelerometer() const {
_THREAD_SAFE_METHOD_
+
+#ifdef DEBUG_ENABLED
+ if (!accelerometer_enabled) {
+ WARN_PRINT_ONCE("`input_devices/sensors/enable_accelerometer` is not enabled in project settings.");
+ }
+#endif
+
return accelerometer;
}
Vector3 Input::get_magnetometer() const {
_THREAD_SAFE_METHOD_
+
+#ifdef DEBUG_ENABLED
+ if (!magnetometer_enabled) {
+ WARN_PRINT_ONCE("`input_devices/sensors/enable_magnetometer` is not enabled in project settings.");
+ }
+#endif
+
return magnetometer;
}
Vector3 Input::get_gyroscope() const {
_THREAD_SAFE_METHOD_
+
+#ifdef DEBUG_ENABLED
+ if (!gyroscope_enabled) {
+ WARN_PRINT_ONCE("`input_devices/sensors/enable_gyroscope` is not enabled in project settings.");
+ }
+#endif
+
return gyroscope;
}
@@ -1683,6 +1711,11 @@ Input::Input() {
// Always use standard behavior in the editor.
legacy_just_pressed_behavior = false;
}
+
+ accelerometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_accelerometer", false);
+ gravity_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gravity", false);
+ gyroscope_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gyroscope", false);
+ magnetometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_magnetometer", false);
}
Input::~Input() {
diff --git a/core/input/input.h b/core/input/input.h
index 89e48f53d740..95dd623cc032 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -92,9 +92,13 @@ class Input : public Object {
RBSet joy_buttons_pressed;
RBMap _joy_axis;
//RBMap custom_action_press;
+ bool gravity_enabled = false;
Vector3 gravity;
+ bool accelerometer_enabled = false;
Vector3 accelerometer;
+ bool magnetometer_enabled = false;
Vector3 magnetometer;
+ bool gyroscope_enabled = false;
Vector3 gyroscope;
Vector2 mouse_pos;
int64_t mouse_window = 0;
diff --git a/core/input/input_builders.py b/core/input/input_builders.py
index ae848f4e7cd5..3685e726b468 100644
--- a/core/input/input_builders.py
+++ b/core/input/input_builders.py
@@ -33,7 +33,7 @@ def make_default_controller_mappings(target, source, env):
guid = line_parts[0]
if guid in platform_mappings[current_platform]:
g.write(
- "// WARNING - DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format(
+ "// WARNING: DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format(
src_path, current_platform, platform_mappings[current_platform][guid]
)
)
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index de3efa7a3a4f..905526bbbd88 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -1088,7 +1088,7 @@ void InputEventMouseMotion::_bind_methods() {
///////////////////////////////////
void InputEventJoypadMotion::set_axis(JoyAxis p_axis) {
- ERR_FAIL_COND(p_axis < JoyAxis::LEFT_X || p_axis > JoyAxis::MAX);
+ ERR_FAIL_COND(p_axis < JoyAxis::INVALID || p_axis > JoyAxis::MAX);
axis = p_axis;
emit_changed();
diff --git a/core/object/object.compat.inc b/core/input/input_map.compat.inc
similarity index 84%
rename from core/object/object.compat.inc
rename to core/input/input_map.compat.inc
index bf1e99fc9b3c..da4bd962b6fb 100644
--- a/core/object/object.compat.inc
+++ b/core/input/input_map.compat.inc
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* object.compat.inc */
+/* input_map.compat.inc */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -30,11 +30,12 @@
#ifndef DISABLE_DEPRECATED
-#include "core/object/class_db.h"
+void InputMap::_add_action_bind_compat_97281(const StringName &p_action, float p_deadzone) {
+ add_action(p_action, p_deadzone);
+}
-void Object::_bind_compatibility_methods() {
- ClassDB::bind_compatibility_method(D_METHOD("tr", "message", "context"), &Object::tr, DEFVAL(""));
- ClassDB::bind_compatibility_method(D_METHOD("tr_n", "message", "plural_message", "n", "context"), &Object::tr_n, DEFVAL(""));
+void InputMap::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::_add_action_bind_compat_97281, DEFVAL(0.5f));
}
-#endif
+#endif // DISABLE_DEPRECATED
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index ddeee9d765a2..27a50c79f670 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "input_map.h"
+#include "input_map.compat.inc"
#include "core/config/project_settings.h"
#include "core/input/input.h"
@@ -43,7 +44,7 @@ int InputMap::ALL_DEVICES = -1;
void InputMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_action", "action"), &InputMap::has_action);
ClassDB::bind_method(D_METHOD("get_actions"), &InputMap::_get_actions);
- ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(0.5f));
+ ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(0.2f));
ClassDB::bind_method(D_METHOD("erase_action", "action"), &InputMap::erase_action);
ClassDB::bind_method(D_METHOD("action_set_deadzone", "action", "deadzone"), &InputMap::action_set_deadzone);
@@ -306,7 +307,7 @@ void InputMap::load_from_project_settings() {
String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length());
Dictionary action = GLOBAL_GET(pi.name);
- float deadzone = action.has("deadzone") ? (float)action["deadzone"] : 0.5f;
+ float deadzone = action.has("deadzone") ? (float)action["deadzone"] : 0.2f;
Array events = action["events"];
add_action(name, deadzone);
@@ -400,6 +401,7 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = {
{ "ui_filedialog_refresh", TTRC("Refresh") },
{ "ui_filedialog_show_hidden", TTRC("Show Hidden") },
{ "ui_swap_input_direction ", TTRC("Swap Input Direction") },
+ { "ui_unicode_start", TTRC("Start Unicode Character Input") },
{ "", ""}
/* clang-format on */
};
@@ -754,6 +756,10 @@ const HashMap>> &InputMap::get_builtins() {
inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));
default_builtin_cache.insert("ui_text_submit", inputs);
+ inputs = List[>();
+ inputs.push_back(InputEventKey::create_reference(Key::U | KeyModifierMask::CTRL | KeyModifierMask::SHIFT));
+ default_builtin_cache.insert("ui_unicode_start", inputs);
+
// ///// UI Graph Shortcuts /////
inputs = List][>();
diff --git a/core/input/input_map.h b/core/input/input_map.h
index 3774a131e6fe..b29687d144f5 100644
--- a/core/input/input_map.h
+++ b/core/input/input_map.h
@@ -69,12 +69,17 @@ class InputMap : public Object {
protected:
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ void _add_action_bind_compat_97281(const StringName &p_action, float p_deadzone = 0.5);
+ static void _bind_compatibility_methods();
+#endif // DISABLE_DEPRECATED
+
public:
static _FORCE_INLINE_ InputMap *get_singleton() { return singleton; }
bool has_action(const StringName &p_action) const;
List get_actions() const;
- void add_action(const StringName &p_action, float p_deadzone = 0.5);
+ void add_action(const StringName &p_action, float p_deadzone = 0.2);
void erase_action(const StringName &p_action);
float action_get_deadzone(const StringName &p_action);
diff --git a/core/io/SCsub b/core/io/SCsub
index 19a65492252d..ab811758940f 100644
--- a/core/io/SCsub
+++ b/core/io/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/io/dtls_server.cpp b/core/io/dtls_server.cpp
index 07d62d3a8d47..7638328dc33e 100644
--- a/core/io/dtls_server.cpp
+++ b/core/io/dtls_server.cpp
@@ -33,12 +33,12 @@
#include "core/config/project_settings.h"
#include "core/io/file_access.h"
-DTLSServer *(*DTLSServer::_create)() = nullptr;
+DTLSServer *(*DTLSServer::_create)(bool p_notify_postinitialize) = nullptr;
bool DTLSServer::available = false;
-DTLSServer *DTLSServer::create() {
+DTLSServer *DTLSServer::create(bool p_notify_postinitialize) {
if (_create) {
- return _create();
+ return _create(p_notify_postinitialize);
}
return nullptr;
}
diff --git a/core/io/dtls_server.h b/core/io/dtls_server.h
index f3fbde3c152a..5ffed1ecc381 100644
--- a/core/io/dtls_server.h
+++ b/core/io/dtls_server.h
@@ -38,14 +38,14 @@ class DTLSServer : public RefCounted {
GDCLASS(DTLSServer, RefCounted);
protected:
- static DTLSServer *(*_create)();
+ static DTLSServer *(*_create)(bool p_notify_postinitialize);
static void _bind_methods();
static bool available;
public:
static bool is_available();
- static DTLSServer *create();
+ static DTLSServer *create(bool p_notify_postinitialize = true);
virtual Error setup(Ref p_options) = 0;
virtual void stop() = 0;
diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp
index 1cf388b33a79..d919243e6b7a 100644
--- a/core/io/file_access.cpp
+++ b/core/io/file_access.cpp
@@ -59,11 +59,9 @@ bool FileAccess::exists(const String &p_name) {
return true;
}
- Ref f = open(p_name, READ);
- if (f.is_null()) {
- return false;
- }
- return true;
+ // Using file_exists because it's faster than trying to open the file.
+ Ref ret = create_for_path(p_name);
+ return ret->file_exists(p_name);
}
void FileAccess::_set_access_type(AccessType p_access) {
@@ -225,59 +223,44 @@ String FileAccess::fix_path(const String &p_path) const {
}
/* these are all implemented for ease of porting, then can later be optimized */
+uint8_t FileAccess::get_8() const {
+ uint8_t data = 0;
+ get_buffer(&data, sizeof(uint8_t));
-uint16_t FileAccess::get_16() const {
- uint16_t res;
- uint8_t a, b;
+ return data;
+}
- a = get_8();
- b = get_8();
+uint16_t FileAccess::get_16() const {
+ uint16_t data = 0;
+ get_buffer(reinterpret_cast(&data), sizeof(uint16_t));
if (big_endian) {
- SWAP(a, b);
+ data = BSWAP16(data);
}
- res = b;
- res <<= 8;
- res |= a;
-
- return res;
+ return data;
}
uint32_t FileAccess::get_32() const {
- uint32_t res;
- uint16_t a, b;
-
- a = get_16();
- b = get_16();
+ uint32_t data = 0;
+ get_buffer(reinterpret_cast(&data), sizeof(uint32_t));
if (big_endian) {
- SWAP(a, b);
+ data = BSWAP32(data);
}
- res = b;
- res <<= 16;
- res |= a;
-
- return res;
+ return data;
}
uint64_t FileAccess::get_64() const {
- uint64_t res;
- uint32_t a, b;
-
- a = get_32();
- b = get_32();
+ uint64_t data = 0;
+ get_buffer(reinterpret_cast(&data), sizeof(uint64_t));
if (big_endian) {
- SWAP(a, b);
+ data = BSWAP64(data);
}
- res = b;
- res <<= 32;
- res |= a;
-
- return res;
+ return data;
}
float FileAccess::get_float() const {
@@ -467,17 +450,6 @@ String FileAccess::get_as_text(bool p_skip_cr) const {
return text;
}
-uint64_t FileAccess::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
- ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
-
- uint64_t i = 0;
- for (i = 0; i < p_length && !eof_reached(); i++) {
- p_dst[i] = get_8();
- }
-
- return i;
-}
-
Vector FileAccess::get_buffer(int64_t p_length) const {
Vector data;
@@ -490,7 +462,7 @@ Vector FileAccess::get_buffer(int64_t p_length) const {
ERR_FAIL_COND_V_MSG(err != OK, data, "Can't resize data to " + itos(p_length) + " elements.");
uint8_t *w = data.ptrw();
- int64_t len = get_buffer(&w[0], p_length);
+ int64_t len = get_buffer(w, p_length);
if (len < p_length) {
data.resize(len);
@@ -514,46 +486,32 @@ String FileAccess::get_as_utf8_string(bool p_skip_cr) const {
return s;
}
-void FileAccess::store_16(uint16_t p_dest) {
- uint8_t a, b;
-
- a = p_dest & 0xFF;
- b = p_dest >> 8;
+void FileAccess::store_8(uint8_t p_dest) {
+ store_buffer(&p_dest, sizeof(uint8_t));
+}
+void FileAccess::store_16(uint16_t p_dest) {
if (big_endian) {
- SWAP(a, b);
+ p_dest = BSWAP16(p_dest);
}
- store_8(a);
- store_8(b);
+ store_buffer(reinterpret_cast(&p_dest), sizeof(uint16_t));
}
void FileAccess::store_32(uint32_t p_dest) {
- uint16_t a, b;
-
- a = p_dest & 0xFFFF;
- b = p_dest >> 16;
-
if (big_endian) {
- SWAP(a, b);
+ p_dest = BSWAP32(p_dest);
}
- store_16(a);
- store_16(b);
+ store_buffer(reinterpret_cast(&p_dest), sizeof(uint32_t));
}
void FileAccess::store_64(uint64_t p_dest) {
- uint32_t a, b;
-
- a = p_dest & 0xFFFFFFFF;
- b = p_dest >> 32;
-
if (big_endian) {
- SWAP(a, b);
+ p_dest = BSWAP64(p_dest);
}
- store_32(a);
- store_32(b);
+ store_buffer(reinterpret_cast(&p_dest), sizeof(uint64_t));
}
void FileAccess::store_real(real_t p_real) {
@@ -710,22 +668,11 @@ void FileAccess::store_csv_line(const Vector &p_values, const String &p_
store_line(line);
}
-void FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) {
- ERR_FAIL_COND(!p_src && p_length > 0);
- for (uint64_t i = 0; i < p_length; i++) {
- store_8(p_src[i]);
- }
-}
-
void FileAccess::store_buffer(const Vector &p_buffer) {
uint64_t len = p_buffer.size();
- if (len == 0) {
- return;
- }
-
const uint8_t *r = p_buffer.ptr();
- store_buffer(&r[0], len);
+ store_buffer(r, len);
}
void FileAccess::store_var(const Variant &p_var, bool p_full_objects) {
diff --git a/core/io/file_access.h b/core/io/file_access.h
index 2ab84db4b63d..2f4d1a860435 100644
--- a/core/io/file_access.h
+++ b/core/io/file_access.h
@@ -137,7 +137,7 @@ class FileAccess : public RefCounted {
virtual bool eof_reached() const = 0; ///< reading passed EOF
- virtual uint8_t get_8() const = 0; ///< get a byte
+ virtual uint8_t get_8() const; ///< get a byte
virtual uint16_t get_16() const; ///< get 16 bits uint
virtual uint32_t get_32() const; ///< get 32 bits uint
virtual uint64_t get_64() const; ///< get 64 bits uint
@@ -148,7 +148,7 @@ class FileAccess : public RefCounted {
Variant get_var(bool p_allow_objects = false) const;
- virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const; ///< get an array of bytes
+ virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const = 0; ///< get an array of bytes, needs to be overwritten by children.
Vector get_buffer(int64_t p_length) const;
virtual String get_line() const;
virtual String get_token() const;
@@ -168,7 +168,7 @@ class FileAccess : public RefCounted {
virtual Error resize(int64_t p_length) = 0;
virtual void flush() = 0;
- virtual void store_8(uint8_t p_dest) = 0; ///< store a byte
+ virtual void store_8(uint8_t p_dest); ///< store a byte
virtual void store_16(uint16_t p_dest); ///< store 16 bits uint
virtual void store_32(uint32_t p_dest); ///< store 32 bits uint
virtual void store_64(uint64_t p_dest); ///< store 64 bits uint
@@ -184,7 +184,7 @@ class FileAccess : public RefCounted {
virtual void store_pascal_string(const String &p_string);
virtual String get_pascal_string();
- virtual void store_buffer(const uint8_t *p_src, uint64_t p_length); ///< store an array of bytes
+ virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) = 0; ///< store an array of bytes, needs to be overwritten by children.
void store_buffer(const Vector &p_buffer);
void store_var(const Variant &p_var, bool p_full_objects = false);
diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp
index 0f00bd292cdd..3602baf8c5cc 100644
--- a/core/io/file_access_compressed.cpp
+++ b/core/io/file_access_compressed.cpp
@@ -260,38 +260,6 @@ bool FileAccessCompressed::eof_reached() const {
}
}
-uint8_t FileAccessCompressed::get_8() const {
- ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use.");
- ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode.");
-
- if (at_end) {
- read_eof = true;
- return 0;
- }
-
- uint8_t ret = read_ptr[read_pos];
-
- read_pos++;
- if (read_pos >= read_block_size) {
- read_block++;
-
- if (read_block < read_block_count) {
- //read another block of compressed data
- f->get_buffer(comp_buffer.ptrw(), read_blocks[read_block].csize);
- int total = Compression::decompress(buffer.ptrw(), read_blocks.size() == 1 ? read_total : block_size, comp_buffer.ptr(), read_blocks[read_block].csize, cmode);
- ERR_FAIL_COND_V_MSG(total == -1, 0, "Compressed file is corrupt.");
- read_block_size = read_block == read_block_count - 1 ? read_total % block_size : block_size;
- read_pos = 0;
-
- } else {
- read_block--;
- at_end = true;
- }
- }
-
- return ret;
-}
-
uint64_t FileAccessCompressed::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_COND_V_MSG(f.is_null(), -1, "File must be opened before use.");
@@ -341,12 +309,13 @@ void FileAccessCompressed::flush() {
// compressed files keep data in memory till close()
}
-void FileAccessCompressed::store_8(uint8_t p_dest) {
+void FileAccessCompressed::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use.");
ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode.");
- WRITE_FIT(1);
- write_ptr[write_pos++] = p_dest;
+ WRITE_FIT(p_length);
+ memcpy(write_ptr + write_pos, p_src, p_length);
+ write_pos += p_length;
}
bool FileAccessCompressed::file_exists(const String &p_name) {
diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h
index f706c82f8e6d..ea9837dd0394 100644
--- a/core/io/file_access_compressed.h
+++ b/core/io/file_access_compressed.h
@@ -83,14 +83,13 @@ class FileAccessCompressed : public FileAccess {
virtual bool eof_reached() const override; ///< reading passed EOF
- virtual uint8_t get_8() const override; ///< get a byte
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
virtual Error get_error() const override; ///< get last error
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
virtual void flush() override;
- virtual void store_8(uint8_t p_dest) override; ///< store a byte
+ virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override;
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp
index b689f5b6284e..13d1e0c8fc31 100644
--- a/core/io/file_access_encrypted.cpp
+++ b/core/io/file_access_encrypted.cpp
@@ -37,7 +37,7 @@
#include
Error FileAccessEncrypted::open_and_parse(Ref p_base, const Vector &p_key, Mode p_mode, bool p_with_magic) {
- ERR_FAIL_COND_V_MSG(file != nullptr, ERR_ALREADY_IN_USE, "Can't open file while another file from path '" + file->get_path_absolute() + "' is open.");
+ ERR_FAIL_COND_V_MSG(file.is_valid(), ERR_ALREADY_IN_USE, "Can't open file while another file from path '" + file->get_path_absolute() + "' is open.");
ERR_FAIL_COND_V(p_key.size() != 32, ERR_INVALID_PARAMETER);
pos = 0;
@@ -162,7 +162,7 @@ void FileAccessEncrypted::_close() {
}
bool FileAccessEncrypted::is_open() const {
- return file != nullptr;
+ return file.is_valid();
}
String FileAccessEncrypted::get_path() const {
@@ -206,26 +206,13 @@ bool FileAccessEncrypted::eof_reached() const {
return eofed;
}
-uint8_t FileAccessEncrypted::get_8() const {
- ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode.");
- if (pos >= get_length()) {
- eofed = true;
- return 0;
- }
-
- uint8_t b = data[pos];
- pos++;
- return b;
-}
-
uint64_t FileAccessEncrypted::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_COND_V_MSG(writing, -1, "File has not been opened in read mode.");
uint64_t to_copy = MIN(p_length, get_length() - pos);
- for (uint64_t i = 0; i < to_copy; i++) {
- p_dst[i] = data[pos++];
- }
+ memcpy(p_dst, data.ptr() + pos, to_copy);
+ pos += to_copy;
if (to_copy < p_length) {
eofed = true;
@@ -242,17 +229,12 @@ void FileAccessEncrypted::store_buffer(const uint8_t *p_src, uint64_t p_length)
ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode.");
ERR_FAIL_COND(!p_src && p_length > 0);
- if (pos < get_length()) {
- for (uint64_t i = 0; i < p_length; i++) {
- store_8(p_src[i]);
- }
- } else if (pos == get_length()) {
+ if (pos + p_length >= get_length()) {
data.resize(pos + p_length);
- for (uint64_t i = 0; i < p_length; i++) {
- data.write[pos + i] = p_src[i];
- }
- pos += p_length;
}
+
+ memcpy(data.ptrw() + pos, p_src, p_length);
+ pos += p_length;
}
void FileAccessEncrypted::flush() {
@@ -261,18 +243,6 @@ void FileAccessEncrypted::flush() {
// encrypted files keep data in memory till close()
}
-void FileAccessEncrypted::store_8(uint8_t p_dest) {
- ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode.");
-
- if (pos < get_length()) {
- data.write[pos] = p_dest;
- pos++;
- } else if (pos == get_length()) {
- data.push_back(p_dest);
- pos++;
- }
-}
-
bool FileAccessEncrypted::file_exists(const String &p_name) {
Ref fa = FileAccess::open(p_name, FileAccess::READ);
if (fa.is_null()) {
diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h
index 42afe49a5e7b..5f8c803d6022 100644
--- a/core/io/file_access_encrypted.h
+++ b/core/io/file_access_encrypted.h
@@ -73,14 +73,12 @@ class FileAccessEncrypted : public FileAccess {
virtual bool eof_reached() const override; ///< reading passed EOF
- virtual uint8_t get_8() const override; ///< get a byte
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
virtual Error get_error() const override; ///< get last error
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
virtual void flush() override;
- virtual void store_8(uint8_t p_dest) override; ///< store a byte
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp
index 9521a4f666d1..1541a5ed4a4d 100644
--- a/core/io/file_access_memory.cpp
+++ b/core/io/file_access_memory.cpp
@@ -122,16 +122,6 @@ bool FileAccessMemory::eof_reached() const {
return pos >= length;
}
-uint8_t FileAccessMemory::get_8() const {
- uint8_t ret = 0;
- if (pos < length) {
- ret = data[pos];
- }
- ++pos;
-
- return ret;
-}
-
uint64_t FileAccessMemory::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_NULL_V(data, -1);
@@ -157,16 +147,12 @@ void FileAccessMemory::flush() {
ERR_FAIL_NULL(data);
}
-void FileAccessMemory::store_8(uint8_t p_byte) {
- ERR_FAIL_NULL(data);
- ERR_FAIL_COND(pos >= length);
- data[pos++] = p_byte;
-}
-
void FileAccessMemory::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_COND(!p_src && p_length > 0);
+
uint64_t left = length - pos;
uint64_t write = MIN(p_length, left);
+
if (write < p_length) {
WARN_PRINT("Writing less data than requested");
}
diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h
index e9fbc26d7515..39e1528d9782 100644
--- a/core/io/file_access_memory.h
+++ b/core/io/file_access_memory.h
@@ -55,15 +55,12 @@ class FileAccessMemory : public FileAccess {
virtual bool eof_reached() const override; ///< reading passed EOF
- virtual uint8_t get_8() const override; ///< get a byte
-
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; ///< get an array of bytes
virtual Error get_error() const override; ///< get last error
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
virtual void flush() override;
- virtual void store_8(uint8_t p_byte) override; ///< store a byte
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp
index 02bf0a603935..1340382eaa44 100644
--- a/core/io/file_access_pack.cpp
+++ b/core/io/file_access_pack.cpp
@@ -102,6 +102,22 @@ void PackedData::add_pack_source(PackSource *p_source) {
}
}
+uint8_t *PackedData::get_file_hash(const String &p_path) {
+ PathMD5 pmd5(p_path.md5_buffer());
+ HashMap::Iterator E = files.find(pmd5);
+ if (!E || E->value.offset == 0) {
+ return nullptr;
+ }
+
+ return E->value.md5;
+}
+
+void PackedData::clear() {
+ files.clear();
+ _free_packed_dirs(root);
+ root = memnew(PackedDir);
+}
+
PackedData *PackedData::singleton = nullptr;
PackedData::PackedData() {
@@ -313,17 +329,6 @@ bool FileAccessPack::eof_reached() const {
return eof;
}
-uint8_t FileAccessPack::get_8() const {
- ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use.");
- if (pos >= pf.size) {
- eof = true;
- return 0;
- }
-
- pos++;
- return f->get_8();
-}
-
uint64_t FileAccessPack::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V_MSG(f.is_null(), -1, "File must be opened before use.");
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
@@ -366,10 +371,6 @@ void FileAccessPack::flush() {
ERR_FAIL();
}
-void FileAccessPack::store_8(uint8_t p_dest) {
- ERR_FAIL();
-}
-
void FileAccessPack::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL();
}
diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h
index 594ac8f089b3..57b7a5f87f5d 100644
--- a/core/io/file_access_pack.h
+++ b/core/io/file_access_pack.h
@@ -111,6 +111,7 @@ class PackedData {
public:
void add_pack_source(PackSource *p_source);
void add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted = false); // for PackSource
+ uint8_t *get_file_hash(const String &p_path);
void set_disabled(bool p_disabled) { disabled = p_disabled; }
_FORCE_INLINE_ bool is_disabled() const { return disabled; }
@@ -118,6 +119,8 @@ class PackedData {
static PackedData *get_singleton() { return singleton; }
Error add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset);
+ void clear();
+
_FORCE_INLINE_ Ref try_open_path(const String &p_path);
_FORCE_INLINE_ bool has_path(const String &p_path);
@@ -169,8 +172,6 @@ class FileAccessPack : public FileAccess {
virtual bool eof_reached() const override;
- virtual uint8_t get_8() const override;
-
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
virtual void set_big_endian(bool p_big_endian) override;
@@ -179,8 +180,6 @@ class FileAccessPack : public FileAccess {
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
virtual void flush() override;
- virtual void store_8(uint8_t p_dest) override;
-
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override;
virtual bool file_exists(const String &p_name) override;
diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp
index c0d1afc8e187..b33b7b35c31d 100644
--- a/core/io/file_access_zip.cpp
+++ b/core/io/file_access_zip.cpp
@@ -291,12 +291,6 @@ bool FileAccessZip::eof_reached() const {
return at_eof;
}
-uint8_t FileAccessZip::get_8() const {
- uint8_t ret = 0;
- get_buffer(&ret, 1);
- return ret;
-}
-
uint64_t FileAccessZip::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_NULL_V(zfile, -1);
@@ -328,7 +322,7 @@ void FileAccessZip::flush() {
ERR_FAIL();
}
-void FileAccessZip::store_8(uint8_t p_dest) {
+void FileAccessZip::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL();
}
diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h
index 88b63e93e2dc..1e11e050dfb9 100644
--- a/core/io/file_access_zip.h
+++ b/core/io/file_access_zip.h
@@ -95,14 +95,13 @@ class FileAccessZip : public FileAccess {
virtual bool eof_reached() const override; ///< reading passed EOF
- virtual uint8_t get_8() const override; ///< get a byte
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
virtual Error get_error() const override; ///< get last error
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
virtual void flush() override;
- virtual void store_8(uint8_t p_dest) override; ///< store a byte
+ virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override;
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 833fd1adc381..fc91341bedda 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -42,9 +42,9 @@ const char *HTTPClient::_methods[METHOD_MAX] = {
"PATCH"
};
-HTTPClient *HTTPClient::create() {
+HTTPClient *HTTPClient::create(bool p_notify_postinitialize) {
if (_create) {
- return _create();
+ return _create(p_notify_postinitialize);
}
return nullptr;
}
diff --git a/core/io/http_client.h b/core/io/http_client.h
index 9e018182e379..594529112213 100644
--- a/core/io/http_client.h
+++ b/core/io/http_client.h
@@ -158,12 +158,12 @@ class HTTPClient : public RefCounted {
Error _request_raw(Method p_method, const String &p_url, const Vector &p_headers, const Vector &p_body);
Error _request(Method p_method, const String &p_url, const Vector &p_headers, const String &p_body = String());
- static HTTPClient *(*_create)();
+ static HTTPClient *(*_create)(bool p_notify_postinitialize);
static void _bind_methods();
public:
- static HTTPClient *create();
+ static HTTPClient *create(bool p_notify_postinitialize = true);
String query_string_from_dict(const Dictionary &p_dict);
Error verify_headers(const Vector &p_headers);
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
index 2f45238951be..70fcad543a1f 100644
--- a/core/io/http_client_tcp.cpp
+++ b/core/io/http_client_tcp.cpp
@@ -35,8 +35,8 @@
#include "core/io/stream_peer_tls.h"
#include "core/version.h"
-HTTPClient *HTTPClientTCP::_create_func() {
- return memnew(HTTPClientTCP);
+HTTPClient *HTTPClientTCP::_create_func(bool p_notify_postinitialize) {
+ return static_cast(ClassDB::creator(p_notify_postinitialize));
}
Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, Ref p_options) {
@@ -792,6 +792,6 @@ HTTPClientTCP::HTTPClientTCP() {
request_buffer.instantiate();
}
-HTTPClient *(*HTTPClient::_create)() = HTTPClientTCP::_create_func;
+HTTPClient *(*HTTPClient::_create)(bool p_notify_postinitialize) = HTTPClientTCP::_create_func;
#endif // WEB_ENABLED
diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h
index 6060c975bcb8..dd6cc6b84f04 100644
--- a/core/io/http_client_tcp.h
+++ b/core/io/http_client_tcp.h
@@ -76,7 +76,7 @@ class HTTPClientTCP : public HTTPClient {
Error _get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received);
public:
- static HTTPClient *_create_func();
+ static HTTPClient *_create_func(bool p_notify_postinitialize);
Error request(Method p_method, const String &p_url, const Vector &p_headers, const uint8_t *p_body, int p_body_size) override;
diff --git a/core/io/image.cpp b/core/io/image.cpp
index d0598e4dc6c7..aa391b77dd43 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -30,6 +30,7 @@
#include "image.h"
+#include "core/config/project_settings.h"
#include "core/error/error_list.h"
#include "core/error/error_macros.h"
#include "core/io/image_loader.h"
@@ -501,6 +502,38 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p
}
}
+template
+static void _convert_fast(int p_width, int p_height, const T *p_src, T *p_dst) {
+ uint32_t dst_count = 0;
+ uint32_t src_count = 0;
+
+ const int resolution = p_width * p_height;
+
+ for (int i = 0; i < resolution; i++) {
+ memcpy(p_dst + dst_count, p_src + src_count, MIN(read_channels, write_channels) * sizeof(T));
+
+ if constexpr (write_channels > read_channels) {
+ const T def_value[4] = { def_zero, def_zero, def_zero, def_one };
+ memcpy(p_dst + dst_count + read_channels, &def_value[read_channels], (write_channels - read_channels) * sizeof(T));
+ }
+
+ dst_count += write_channels;
+ src_count += read_channels;
+ }
+}
+
+static bool _are_formats_compatible(Image::Format p_format0, Image::Format p_format1) {
+ if (p_format0 <= Image::FORMAT_RGBA8 && p_format1 <= Image::FORMAT_RGBA8) {
+ return true;
+ } else if (p_format0 <= Image::FORMAT_RGBAH && p_format0 >= Image::FORMAT_RH && p_format1 <= Image::FORMAT_RGBAH && p_format1 >= Image::FORMAT_RH) {
+ return true;
+ } else if (p_format0 <= Image::FORMAT_RGBAF && p_format0 >= Image::FORMAT_RF && p_format1 <= Image::FORMAT_RGBAF && p_format1 >= Image::FORMAT_RF) {
+ return true;
+ }
+
+ return false;
+}
+
void Image::convert(Format p_new_format) {
ERR_FAIL_INDEX_MSG(p_new_format, FORMAT_MAX, "The Image format specified (" + itos(p_new_format) + ") is out of range. See Image's Format enum.");
if (data.size() == 0) {
@@ -517,7 +550,7 @@ void Image::convert(Format p_new_format) {
if (Image::is_format_compressed(format) || Image::is_format_compressed(p_new_format)) {
ERR_FAIL_MSG("Cannot convert to <-> from compressed formats. Use compress() and decompress() instead.");
- } else if (format > FORMAT_RGBA8 || p_new_format > FORMAT_RGBA8) {
+ } else if (!_are_formats_compatible(format, p_new_format)) {
//use put/set pixel which is slower but works with non byte formats
Image new_img(width, height, mipmaps, p_new_format);
@@ -648,6 +681,78 @@ void Image::convert(Format p_new_format) {
case FORMAT_RGBA8 | (FORMAT_RGB8 << 8):
_convert<3, true, 3, false, false, false>(mip_width, mip_height, rptr, wptr);
break;
+ case FORMAT_RH | (FORMAT_RGH << 8):
+ _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
+ break;
+ case FORMAT_RH | (FORMAT_RGBH << 8):
+ _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
+ break;
+ case FORMAT_RH | (FORMAT_RGBAH << 8):
+ _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
+ break;
+ case FORMAT_RGH | (FORMAT_RH << 8):
+ _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
+ break;
+ case FORMAT_RGH | (FORMAT_RGBH << 8):
+ _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
+ break;
+ case FORMAT_RGH | (FORMAT_RGBAH << 8):
+ _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
+ break;
+ case FORMAT_RGBH | (FORMAT_RH << 8):
+ _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
+ break;
+ case FORMAT_RGBH | (FORMAT_RGH << 8):
+ _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
+ break;
+ case FORMAT_RGBH | (FORMAT_RGBAH << 8):
+ _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
+ break;
+ case FORMAT_RGBAH | (FORMAT_RH << 8):
+ _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
+ break;
+ case FORMAT_RGBAH | (FORMAT_RGH << 8):
+ _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
+ break;
+ case FORMAT_RGBAH | (FORMAT_RGBH << 8):
+ _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
+ break;
+ case FORMAT_RF | (FORMAT_RGF << 8):
+ _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr);
+ break;
+ case FORMAT_RF | (FORMAT_RGBF << 8):
+ _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr);
+ break;
+ case FORMAT_RF | (FORMAT_RGBAF << 8):
+ _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr);
+ break;
+ case FORMAT_RGF | (FORMAT_RF << 8):
+ _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr);
+ break;
+ case FORMAT_RGF | (FORMAT_RGBF << 8):
+ _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr);
+ break;
+ case FORMAT_RGF | (FORMAT_RGBAF << 8):
+ _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr);
+ break;
+ case FORMAT_RGBF | (FORMAT_RF << 8):
+ _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr);
+ break;
+ case FORMAT_RGBF | (FORMAT_RGF << 8):
+ _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr);
+ break;
+ case FORMAT_RGBF | (FORMAT_RGBAF << 8):
+ _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr);
+ break;
+ case FORMAT_RGBAF | (FORMAT_RF << 8):
+ _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr);
+ break;
+ case FORMAT_RGBAF | (FORMAT_RGF << 8):
+ _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr);
+ break;
+ case FORMAT_RGBAF | (FORMAT_RGBF << 8):
+ _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr);
+ break;
}
}
@@ -761,12 +866,10 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_
template
static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
- enum {
- FRAC_BITS = 8,
- FRAC_LEN = (1 << FRAC_BITS),
- FRAC_HALF = (FRAC_LEN >> 1),
- FRAC_MASK = FRAC_LEN - 1
- };
+ constexpr uint32_t FRAC_BITS = 8;
+ constexpr uint32_t FRAC_LEN = (1 << FRAC_BITS);
+ constexpr uint32_t FRAC_HALF = (FRAC_LEN >> 1);
+ constexpr uint32_t FRAC_MASK = FRAC_LEN - 1;
for (uint32_t i = 0; i < p_dst_height; i++) {
// Add 0.5 in order to interpolate based on pixel center
@@ -2630,6 +2733,40 @@ Error Image::compress(CompressMode p_mode, CompressSource p_source, ASTCFormat p
Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels, ASTCFormat p_astc_format) {
ERR_FAIL_COND_V(data.is_empty(), ERR_INVALID_DATA);
+ // RenderingDevice only.
+ if (GLOBAL_GET("rendering/textures/vram_compression/compress_with_gpu")) {
+ switch (p_mode) {
+ case COMPRESS_BPTC: {
+ // BC7 is unsupported currently.
+ if ((format >= FORMAT_RF && format <= FORMAT_RGBE9995) && _image_compress_bptc_rd_func) {
+ Error result = _image_compress_bptc_rd_func(this, p_channels);
+
+ // If the image was compressed successfully, we return here. If not, we fall back to the default compression scheme.
+ if (result == OK) {
+ return OK;
+ }
+ }
+
+ } break;
+
+ case COMPRESS_S3TC: {
+ // BC3 is unsupported currently.
+ if ((p_channels == USED_CHANNELS_RGB || p_channels == USED_CHANNELS_L) && _image_compress_bc_rd_func) {
+ Error result = _image_compress_bc_rd_func(this, p_channels);
+
+ // If the image was compressed successfully, we return here. If not, we fall back to the default compression scheme.
+ if (result == OK) {
+ return OK;
+ }
+ }
+
+ } break;
+
+ default: {
+ }
+ }
+ }
+
switch (p_mode) {
case COMPRESS_S3TC: {
ERR_FAIL_NULL_V(_image_compress_bc_func, ERR_UNAVAILABLE);
@@ -3011,6 +3148,8 @@ void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr
void (*Image::_image_compress_etc1_func)(Image *) = nullptr;
void (*Image::_image_compress_etc2_func)(Image *, Image::UsedChannels) = nullptr;
void (*Image::_image_compress_astc_func)(Image *, Image::ASTCFormat) = nullptr;
+Error (*Image::_image_compress_bptc_rd_func)(Image *, Image::UsedChannels) = nullptr;
+Error (*Image::_image_compress_bc_rd_func)(Image *, Image::UsedChannels) = nullptr;
void (*Image::_image_decompress_bc)(Image *) = nullptr;
void (*Image::_image_decompress_bptc)(Image *) = nullptr;
void (*Image::_image_decompress_etc1)(Image *) = nullptr;
@@ -3460,6 +3599,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("fix_alpha_edges"), &Image::fix_alpha_edges);
ClassDB::bind_method(D_METHOD("premultiply_alpha"), &Image::premultiply_alpha);
ClassDB::bind_method(D_METHOD("srgb_to_linear"), &Image::srgb_to_linear);
+ ClassDB::bind_method(D_METHOD("linear_to_srgb"), &Image::linear_to_srgb);
ClassDB::bind_method(D_METHOD("normal_map_to_xy"), &Image::normal_map_to_xy);
ClassDB::bind_method(D_METHOD("rgbe_to_srgb"), &Image::rgbe_to_srgb);
ClassDB::bind_method(D_METHOD("bump_map_to_normal_map", "bump_scale"), &Image::bump_map_to_normal_map, DEFVAL(1.0));
@@ -3696,6 +3836,33 @@ void Image::bump_map_to_normal_map(float bump_scale) {
data = result_image;
}
+bool Image::detect_signed(bool p_include_mips) const {
+ ERR_FAIL_COND_V(is_compressed(), false);
+
+ if (format >= Image::FORMAT_RH && format <= Image::FORMAT_RGBAH) {
+ const uint16_t *img_data = reinterpret_cast(data.ptr());
+ const uint64_t img_size = p_include_mips ? (data.size() / 2) : (width * height * get_format_pixel_size(format) / 2);
+
+ for (uint64_t i = 0; i < img_size; i++) {
+ if ((img_data[i] & 0x8000) != 0 && (img_data[i] & 0x7fff) != 0) {
+ return true;
+ }
+ }
+
+ } else if (format >= Image::FORMAT_RF && format <= Image::FORMAT_RGBAF) {
+ const uint32_t *img_data = reinterpret_cast(data.ptr());
+ const uint64_t img_size = p_include_mips ? (data.size() / 4) : (width * height * get_format_pixel_size(format) / 4);
+
+ for (uint64_t i = 0; i < img_size; i++) {
+ if ((img_data[i] & 0x80000000) != 0 && (img_data[i] & 0x7fffffff) != 0) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
void Image::srgb_to_linear() {
if (data.size() == 0) {
return;
@@ -3727,6 +3894,37 @@ void Image::srgb_to_linear() {
}
}
+void Image::linear_to_srgb() {
+ if (data.size() == 0) {
+ return;
+ }
+
+ static const uint8_t lin2srgb[256] = { 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255 };
+
+ ERR_FAIL_COND(format != FORMAT_RGB8 && format != FORMAT_RGBA8);
+
+ if (format == FORMAT_RGBA8) {
+ int len = data.size() / 4;
+ uint8_t *data_ptr = data.ptrw();
+
+ for (int i = 0; i < len; i++) {
+ data_ptr[(i << 2) + 0] = lin2srgb[data_ptr[(i << 2) + 0]];
+ data_ptr[(i << 2) + 1] = lin2srgb[data_ptr[(i << 2) + 1]];
+ data_ptr[(i << 2) + 2] = lin2srgb[data_ptr[(i << 2) + 2]];
+ }
+
+ } else if (format == FORMAT_RGB8) {
+ int len = data.size() / 3;
+ uint8_t *data_ptr = data.ptrw();
+
+ for (int i = 0; i < len; i++) {
+ data_ptr[(i * 3) + 0] = lin2srgb[data_ptr[(i * 3) + 0]];
+ data_ptr[(i * 3) + 1] = lin2srgb[data_ptr[(i * 3) + 1]];
+ data_ptr[(i * 3) + 2] = lin2srgb[data_ptr[(i * 3) + 2]];
+ }
+ }
+}
+
void Image::premultiply_alpha() {
if (data.size() == 0) {
return;
@@ -4039,7 +4237,7 @@ Dictionary Image::compute_image_metrics(const Ref p_compared_image, bool
result["root_mean_squared"] = INFINITY;
result["peak_snr"] = 0.0f;
- ERR_FAIL_NULL_V(p_compared_image, result);
+ ERR_FAIL_COND_V(p_compared_image.is_null(), result);
Error err = OK;
Ref compared_image = duplicate(true);
if (compared_image->is_compressed()) {
diff --git a/core/io/image.h b/core/io/image.h
index d55cc39dbb57..78757246e06a 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -159,6 +159,9 @@ class Image : public Resource {
static void (*_image_compress_etc2_func)(Image *, UsedChannels p_channels);
static void (*_image_compress_astc_func)(Image *, ASTCFormat p_format);
+ static Error (*_image_compress_bptc_rd_func)(Image *, UsedChannels p_channels);
+ static Error (*_image_compress_bc_rd_func)(Image *, UsedChannels p_channels);
+
static void (*_image_decompress_bc)(Image *);
static void (*_image_decompress_bptc)(Image *);
static void (*_image_decompress_etc1)(Image *);
@@ -383,11 +386,14 @@ class Image : public Resource {
void fix_alpha_edges();
void premultiply_alpha();
void srgb_to_linear();
+ void linear_to_srgb();
void normal_map_to_xy();
Ref rgbe_to_srgb();
Ref get_image_from_mipmap(int p_mipmap) const;
void bump_map_to_normal_map(float bump_scale = 1.0);
+ bool detect_signed(bool p_include_mips = true) const;
+
void blit_rect(const Ref &p_src, const Rect2i &p_src_rect, const Point2i &p_dest);
void blit_rect_mask(const Ref &p_src, const Ref &p_mask, const Rect2i &p_src_rect, const Point2i &p_dest);
void blend_rect(const Ref &p_src, const Rect2i &p_src_rect, const Point2i &p_dest);
diff --git a/core/io/ip.cpp b/core/io/ip.cpp
index f20d65bef901..38c71b19fa31 100644
--- a/core/io/ip.cpp
+++ b/core/io/ip.cpp
@@ -81,17 +81,17 @@ struct _IP_ResolverPrivate {
continue;
}
- mutex.lock();
+ MutexLock lock(mutex);
List response;
String hostname = queue[i].hostname;
IP::Type type = queue[i].type;
- mutex.unlock();
+ lock.temp_unlock();
// We should not lock while resolving the hostname,
// only when modifying the queue.
IP::get_singleton()->_resolve_hostname(response, hostname, type);
- MutexLock lock(mutex);
+ lock.temp_relock();
// Could have been completed by another function, or deleted.
if (queue[i].status.get() != IP::RESOLVER_STATUS_WAITING) {
continue;
@@ -131,21 +131,22 @@ PackedStringArray IP::resolve_hostname_addresses(const String &p_hostname, Type
List]