Merge branch 'godotengine:master' into master

This commit is contained in:
周公不解梦 2024-09-30 09:24:52 +08:00 committed by GitHub
commit d4ca15c458
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1007 changed files with 238775 additions and 12373 deletions

View File

@ -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 ###
# 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
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,6 +61,7 @@ AllowAllParametersOfDeclarationOnNextLine: false
# - __capability
# BinPackArguments: true
# BinPackParameters: true
# BitFieldColonSpacing: Both
# BraceWrapping:
# AfterCaseLabel: false
# AfterClass: false
@ -50,31 +81,29 @@ AllowAllParametersOfDeclarationOnNextLine: 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
# CompactNamespaces: false
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
# DeriveLineEnding: true
# DerivePointerAlignment: false
# DisableFormat: false
# EmptyLineAfterAccessModifier: Never
# EmptyLineBeforeAccessModifier: LogicalBlock
# ExperimentalAutoDetectBinPacking: false
# PackConstructorInitializers: BinPack
ConstructorInitializerAllOnOneLineOrOnePerLine: true
# AllowAllConstructorInitializersOnNextLine: true
# FixNamespaceComments: true
# ForEachMacros:
# - foreach
@ -84,116 +113,136 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
# - KJ_IF_MAYBE
# IncludeBlocks: Preserve
IncludeCategories:
- Regex: '".*"'
- Regex: ^".*"$
Priority: 1
- Regex: '^<.*\.h>'
- Regex: ^<.*\.h>$
Priority: 2
- Regex: '^<.*'
- Regex: ^<.*>$
Priority: 3
# IncludeIsMainRegex: '(Test)?$'
# IncludeIsMainRegex: (Test)?$
# IncludeIsMainSourceRegex: ''
# IndentAccessModifiers: false
IndentCaseLabels: true
# IndentCaseBlocks: false
IndentCaseLabels: true
# IndentExternBlock: AfterExternBlock
# IndentGotoLabels: true
# IndentPPDirectives: None
# IndentExternBlock: AfterExternBlock
# IndentRequires: false
# 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: ''
# 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
# RemoveBracesLLVM: false
# RemoveParentheses: Leave
# RemoveSemicolon: false
# RequiresClausePosition: OwnLine
# RequiresExpressionIndentation: OuterScope
# SeparateDefinitionBlocks: Leave
# ShortNamespaceLines: 1
# 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
# 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
# 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
# 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
# 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

View File

@ -54,3 +54,6 @@ df61dc4b2bd54a5a40c515493c76f5a458e5b541
# Enforce template syntax `typename` over `class`
9903e6779b70fc03aae70a37b9cf053f4f355b91
# Style: Apply new `clang-format` fixes
b37fc1014abf7adda70dc30b0822d775b3a4433f

86
.github/CODEOWNERS vendored
View File

@ -4,11 +4,7 @@
# Buildsystem
.* @godotengine/buildsystem
.github/ @godotengine/buildsystem
*.py @godotengine/buildsystem
SConstruct @godotengine/buildsystem
SCsub @godotengine/buildsystem
* @godotengine/buildsystem
# Core
@ -21,7 +17,7 @@ SCsub @godotengine/buildsystem
# Doc
/doc/ @godotengine/documentation
doc_classes/* @godotengine/documentation
**/doc_classes/ @godotengine/documentation
# Drivers
@ -76,24 +72,33 @@ doc_classes/* @godotengine/documentation
# Modules
## Audio (+ video)
/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/opus/ @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/webm/ @godotengine/audio
/modules/vorbis/doc_classes/ @godotengine/audio @godotengine/documentation
## 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/etc/ @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/pvr/ @godotengine/import
/modules/ktx/ @godotengine/import
/modules/squish/ @godotengine/import
/modules/svg/ @godotengine/import
/modules/tga/ @godotengine/import
@ -102,56 +107,98 @@ doc_classes/* @godotengine/documentation
## 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/raycast/ @godotengine/rendering
/modules/vhacd/ @godotengine/rendering
/modules/xatlas_unwrap/ @godotengine/rendering
## Scripting
/modules/gdscript/ @godotengine/gdscript
/modules/jsonrpc/ @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/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/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/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/opensimplex/ @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/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/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
@ -162,6 +209,7 @@ doc_classes/* @godotengine/documentation
/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
@ -181,3 +229,9 @@ doc_classes/* @godotengine/documentation
# Thirdparty
/thirdparty/ @godotengine/buildsystem
# Buildsystem (After everything to catch all)
*.py @godotengine/buildsystem
SConstruct @godotengine/buildsystem
SCsub @godotengine/buildsystem

View File

@ -1,7 +1,7 @@
name: Bug report
description: Report a bug in Godot
body:
body:
- type: markdown
attributes:
value: |

View File

@ -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

View File

@ -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/*

View File

@ -1,9 +1,10 @@
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
@ -13,20 +14,21 @@ inputs:
required: false
sconsflags:
description: Additional SCons flags.
default: ""
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 }}

View File

@ -3,12 +3,13 @@ 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

View File

@ -3,12 +3,13 @@ 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -14,7 +14,7 @@ concurrency:
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,7 +86,7 @@ jobs:
run: |
cd platform/android/java
./gradlew generateGodotEditor
./gradlew generateGodotMetaEditor
./gradlew generateGodotHorizonOSEditor
cd ../../..
ls -l bin/android_editor_builds/

View File

@ -7,7 +7,7 @@ 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.3'
GODOT_CPP_BRANCH: 4.3
concurrency:
group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-cpp-tests
@ -15,54 +15,50 @@ concurrency:
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:
sparse-checkout: .github
- name: Checkout godot-cpp
uses: actions/checkout@v4
with:
submodules: recursive
repository: godotengine/godot-cpp
ref: ${{ env.GODOT_CPP_BRANCH }}
path: godot-cpp
- name: Setup Python and SCons
uses: ./.github/actions/godot-deps
# Checkout godot-cpp
- name: Checkout godot-cpp
uses: actions/checkout@v4
with:
repository: godotengine/godot-cpp
ref: ${{ env.GODOT_CPP_BRANCH }}
submodules: 'recursive'
path: 'godot-cpp'
- 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
gdextension-c-compile:
runs-on: "ubuntu-20.04"
name: "Check GDExtension header with a C compiler"
steps:
- uses: actions/checkout@v4
- name: "Run C compiler on gdextension_interface.h"
run: |
gcc -c core/extension/gdextension_interface.h
# - name: Save Godot build cache
# uses: ./.github/actions/godot-cache-save
# with:
# cache-name: godot-cpp
# continue-on-error: true

View File

@ -14,11 +14,12 @@ concurrency:
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

View File

@ -17,7 +17,7 @@ concurrency:
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 }}

View File

@ -14,7 +14,7 @@ concurrency:
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

View File

@ -1,5 +1,5 @@
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
@ -9,44 +9,38 @@ 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

View File

@ -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

View File

@ -8,7 +8,7 @@ env:
GODOT_BASE_BRANCH: master
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
@ -16,7 +16,7 @@ concurrency:
jobs:
web-template:
runs-on: "ubuntu-22.04"
runs-on: ubuntu-22.04
name: ${{ matrix.name }}
strategy:
fail-fast: false
@ -37,7 +37,8 @@ jobs:
artifact: true
steps:
- uses: actions/checkout@v4
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
@ -77,6 +78,6 @@ jobs:
- name: Upload artifact
uses: ./.github/actions/upload-artifact
if: ${{ matrix.artifact }}
if: matrix.artifact
with:
name: ${{ matrix.cache-name }}

View File

@ -17,7 +17,7 @@ concurrency:
jobs:
build-windows:
# Windows 10 with latest image
runs-on: "windows-latest"
runs-on: windows-latest
name: ${{ matrix.name }}
strategy:
fail-fast: false
@ -29,7 +29,7 @@ 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: Editor w/ clang-cl (target=editor, tests=yes, use_llvm=yes)
@ -39,16 +39,17 @@ jobs:
sconsflags: debug_symbols=no windows_subsystem=console use_llvm=yes
bin: ./bin/godot.windows.editor.x86_64.llvm.exe
- name: Template (target=template_release)
- 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
@ -104,7 +105,7 @@ jobs:
name: ${{ matrix.cache-name }}
- name: Unit tests
if: ${{ matrix.tests }}
if: matrix.tests
run: |
${{ matrix.bin }} --version
${{ matrix.bin }} --help

View File

@ -84,6 +84,7 @@ Jean-Michel Bernard <jmb462@gmail.com>
Jérôme Gully <jerome.gully0@gmail.com>
JFonS <joan.fonssanchez@gmail.com>
jitspoe <jitspoe@yahoo.com> <jitspoeAyahoooDcom>
Johan Aires Rastén <johan@oljud.se>
Juan Linietsky <reduzio@gmail.com>
Juan Linietsky <reduzio@gmail.com> <juan@godotengine.org>
Juan Linietsky <reduzio@gmail.com> <juan@okamstudio.com>
@ -97,6 +98,7 @@ karroffel <therzog@mail.de> <thomas.herzog@simedis.com>
Kelly Thomas <kelly.thomas@hotmail.com.au>
Kongfa Waroros <gongpha@hotmail.com>
K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com>
K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com> <fire@users.noreply.github.com>
kleonc <9283098+kleonc@users.noreply.github.com> <kleonc@users.noreply.github.com>
Leon Krause <lk@leonkrause.com> <eska@eska.me>
Leon Krause <lk@leonkrause.com> <eska014@users.noreply.github.com>
@ -142,6 +144,7 @@ Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
Pieter-Jan Briers <pieterjan.briers+git@gmail.com> <pieterjan.briers@gmail.com>
Poommetee Ketson <poommetee@protonmail.com>
Przemysław Gołąb (n-pigeon) <golab.przemyslaw@gmail.com>
Radiant <69520693+RadiantUwU@users.noreply.github.com> <i.like.using.discord@gmail.com>
Rafał Mikrut <mikrutrafal@protonmail.com>
Rafał Mikrut <mikrutrafal@protonmail.com> <mikrutrafal54@gmail.com>
Ralf Hölzemer <r.hoelzemer@posteo.de> <rollenrolm@posteo.de>

View File

@ -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)$
@ -39,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$
@ -96,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
@ -123,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
@ -131,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
@ -179,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,

View File

@ -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)

View File

@ -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
@ -93,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

View File

@ -12,12 +12,12 @@ generous deed immortalized in the next stable release of Godot Engine.
## Patrons
Khronos® Group <https://www.khronos.org/>
OSS Capital <https://oss.capital/>
Re-Logic <https://re-logic.com/>
## Platinum sponsors
Google Play <https://play.google.com/>
Google Play <https://play.google.com>
Ramatak <https://ramatak.com/>
V-Sekai <https://github.com/V-Sekai>
W4 Games <https://w4games.com/>
@ -25,53 +25,56 @@ generous deed immortalized in the next stable release of Godot Engine.
## Gold sponsors
Mega Crit <https://www.megacrit.com/>
Pirate Software <https://gopiratesoftware.com/>
Prehensile Tales <https://prehensile-tales.com/>
Pirate Software <https://gopiratesoftware.com>
Prehensile Tales <https://prehensile-tales.com>
Robot Gentleman <http://robotgentleman.com/>
## Silver sponsors
Broken Rules <https://brokenrul.es/>
Chasing Carrots <https://www.chasing-carrots.com/>
Broken Rules <https://brokenrul.es>
Chasing Carrots <https://www.chasing-carrots.com>
Copia Wealth Studios <https://copiawealthstudios.com/>
Indoor Astronaut <https://indoorastronaut.ch/>
LoadComplete <https://loadcomplete.com/>
Null <https://null.com/>
Orbital Knight <https://www.orbitalknight.com/>
Playful Studios <https://playfulstudios.com/>
Re-Logic <https://re-logic.com/>
## Diamond members
Bippinbits <http://domekeepergame.com/>
Sealow
And 5 anonymous donors
Sylv <https://rankith.itch.io/unnamed-space-idle-prototype>
And 3 anonymous donors
## Titanium members
Adriaan de Jongh <https://adriaan.games/>
Anitya Space <https://www.anitya.space/>
Adriaan de Jongh <https://adriaan.games>
Anitya Space <https://www.anitya.space>
Basically Games
FDG Entertainment <https://www.fdg-entertainment.com/>
Game Dev Artisan <https://gamedevartisan.com/>
FDG Entertainment <https://www.fdg-entertainment.com>
Game Dev Artisan <https://gamedevartisan.com>
Garry Newman
Isaiah Smith <https://www.isaiahsmith.dev/>
Libretrend <https://libretrend.com/>
Kenney <https://kenney.nl/>
Libretrend <https://libretrend.com>
Life Art Studios <https://lifeartstudios.net/>
Lucid Silence Games
Matthew Campbell
PolyMars <https://polymars.dev/>
RPG in a Box <https://www.rpginabox.com/>
Razenpok <https://www.youtube.com/watch?v=-QxI-RP6-HM>
Smirk Software <https://smirk.gg/>
RPG in a Box <https://www.rpginabox.com>
Smirk Software <https://smirk.gg>
Studio Sunshower <https://www.studiosunshower.com/>
TrampolineTales <https://TrampolineTales.com/>
粟二华 (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

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
EnsureSConsVersion(3, 1, 2)
EnsurePythonVersion(3, 6)
@ -298,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))
@ -639,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 ""
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 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:
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 "
@ -677,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."
)
@ -712,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.
@ -789,13 +799,17 @@ 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.

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View File

@ -267,6 +267,14 @@ 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;
}

View File

@ -128,6 +128,9 @@ public:
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;

View File

@ -1016,7 +1016,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#");
@ -1574,6 +1574,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;
}
}

View File

@ -184,6 +184,10 @@ void ResourceSaver::remove_resource_format_saver(Ref<ResourceFormatSaver> 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() {
@ -191,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);
@ -1419,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);
}
@ -1615,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()) {
@ -1636,6 +1646,8 @@ 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));
@ -1669,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
@ -1835,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);
}
@ -1903,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");

View File

@ -116,6 +116,8 @@ public:
void add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front);
void remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver);
ResourceUID::ID get_resource_id_for_path(const String &p_path, bool p_generate = false);
ResourceSaver() { singleton = this; }
};
@ -447,6 +449,14 @@ protected:
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;
@ -455,6 +465,7 @@ public:
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<Dictionary> class_get_signal_list(const StringName &p_class, bool p_no_inheritance = false) const;
@ -558,6 +569,9 @@ public:
// `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;
@ -634,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

View File

@ -677,6 +677,7 @@ void register_global_constants() {
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);

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View File

@ -1017,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

View File

@ -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\\
}
"""

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View File

@ -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]
)
)

View File

@ -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();

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View File

@ -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<PathMD5, PackedFile, PathMD5>::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() {

View File

@ -111,6 +111,7 @@ private:
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 @@ public:
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<FileAccess> try_open_path(const String &p_path);
_FORCE_INLINE_ bool has_path(const String &p_path);

View File

@ -866,12 +866,10 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_
template <int CC, typename T>
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
@ -2751,6 +2749,19 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels
} 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: {
}
}
@ -3138,6 +3149,7 @@ 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;

View File

@ -160,6 +160,7 @@ public:
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 *);

View File

@ -299,7 +299,7 @@ Ref<StreamPeer> PacketPeerStream::get_stream_peer() const {
void PacketPeerStream::set_input_buffer_max_size(int p_max_size) {
ERR_FAIL_COND_MSG(p_max_size < 0, "Max size of input buffer size cannot be smaller than 0.");
//warning may lose packets
// WARNING: May lose packets.
ERR_FAIL_COND_MSG(ring_buffer.data_left(), "Buffer in use, resizing would cause loss of data.");
ring_buffer.resize(nearest_shift(next_power_of_2(p_max_size + 4)) - 1);
input_buffer.resize(next_power_of_2(p_max_size + 4));

View File

@ -47,12 +47,12 @@ static int _get_pad(int p_alignment, int p_n) {
}
void PCKPacker::_bind_methods() {
ClassDB::bind_method(D_METHOD("pck_start", "pck_name", "alignment", "key", "encrypt_directory"), &PCKPacker::pck_start, DEFVAL(32), DEFVAL("0000000000000000000000000000000000000000000000000000000000000000"), DEFVAL(false));
ClassDB::bind_method(D_METHOD("pck_start", "pck_path", "alignment", "key", "encrypt_directory"), &PCKPacker::pck_start, DEFVAL(32), DEFVAL("0000000000000000000000000000000000000000000000000000000000000000"), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_file", "pck_path", "source_path", "encrypt"), &PCKPacker::add_file, DEFVAL(false));
ClassDB::bind_method(D_METHOD("flush", "verbose"), &PCKPacker::flush, DEFVAL(false));
}
Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &p_key, bool p_encrypt_directory) {
Error PCKPacker::pck_start(const String &p_pck_path, int p_alignment, const String &p_key, bool p_encrypt_directory) {
ERR_FAIL_COND_V_MSG((p_key.is_empty() || !p_key.is_valid_hex_number(false) || p_key.length() != 64), ERR_CANT_CREATE, "Invalid Encryption Key (must be 64 characters long).");
ERR_FAIL_COND_V_MSG(p_alignment <= 0, ERR_CANT_CREATE, "Invalid alignment, must be greater then 0.");
@ -83,8 +83,8 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &
}
enc_dir = p_encrypt_directory;
file = FileAccess::open(p_file, FileAccess::WRITE);
ERR_FAIL_COND_V_MSG(file.is_null(), ERR_CANT_CREATE, "Can't open file to write: " + String(p_file) + ".");
file = FileAccess::open(p_pck_path, FileAccess::WRITE);
ERR_FAIL_COND_V_MSG(file.is_null(), ERR_CANT_CREATE, "Can't open file to write: " + String(p_pck_path) + ".");
alignment = p_alignment;
@ -106,7 +106,7 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &
return OK;
}
Error PCKPacker::add_file(const String &p_file, const String &p_src, bool p_encrypt) {
Error PCKPacker::add_file(const String &p_pck_path, const String &p_src, bool p_encrypt) {
ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use.");
Ref<FileAccess> f = FileAccess::open(p_src, FileAccess::READ);
@ -117,7 +117,7 @@ Error PCKPacker::add_file(const String &p_file, const String &p_src, bool p_encr
File pf;
// Simplify path here and on every 'files' access so that paths that have extra '/'
// symbols in them still match to the MD5 hash for the saved path.
pf.path = p_file.simplify_path();
pf.path = p_pck_path.simplify_path();
pf.src_path = p_src;
pf.ofs = ofs;
pf.size = f->get_length();

View File

@ -58,8 +58,8 @@ class PCKPacker : public RefCounted {
Vector<File> files;
public:
Error pck_start(const String &p_file, int p_alignment = 32, const String &p_key = "0000000000000000000000000000000000000000000000000000000000000000", bool p_encrypt_directory = false);
Error add_file(const String &p_file, const String &p_src, bool p_encrypt = false);
Error pck_start(const String &p_pck_path, int p_alignment = 32, const String &p_key = "0000000000000000000000000000000000000000000000000000000000000000", bool p_encrypt_directory = false);
Error add_file(const String &p_pck_path, const String &p_src, bool p_encrypt = false);
Error flush(bool p_verbose = false);
PCKPacker() {}

View File

@ -96,6 +96,7 @@ String Resource::get_path() const {
void Resource::set_path_cache(const String &p_path) {
path_cache = p_path;
GDVIRTUAL_CALL(_set_path_cache, p_path);
}
String Resource::generate_scene_unique_id() {
@ -188,6 +189,7 @@ void Resource::disconnect_changed(const Callable &p_callable) {
}
void Resource::reset_state() {
GDVIRTUAL_CALL(_reset_state);
}
Error Resource::copy_from(const Ref<Resource> &p_resource) {
@ -495,9 +497,9 @@ void Resource::set_as_translation_remapped(bool p_remapped) {
}
}
#ifdef TOOLS_ENABLED
//helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored
void Resource::set_id_for_path(const String &p_path, const String &p_id) {
#ifdef TOOLS_ENABLED
if (p_id.is_empty()) {
ResourceCache::path_cache_lock.write_lock();
ResourceCache::resource_path_cache[p_path].erase(get_path());
@ -507,9 +509,11 @@ void Resource::set_id_for_path(const String &p_path, const String &p_id) {
ResourceCache::resource_path_cache[p_path][get_path()] = p_id;
ResourceCache::path_cache_lock.write_unlock();
}
#endif
}
String Resource::get_id_for_path(const String &p_path) const {
#ifdef TOOLS_ENABLED
ResourceCache::path_cache_lock.read_lock();
if (ResourceCache::resource_path_cache[p_path].has(get_path())) {
String result = ResourceCache::resource_path_cache[p_path][get_path()];
@ -519,13 +523,16 @@ String Resource::get_id_for_path(const String &p_path) const {
ResourceCache::path_cache_lock.read_unlock();
return "";
}
}
#else
return "";
#endif
}
void Resource::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_path", "path"), &Resource::_set_path);
ClassDB::bind_method(D_METHOD("take_over_path", "path"), &Resource::_take_over_path);
ClassDB::bind_method(D_METHOD("get_path"), &Resource::get_path);
ClassDB::bind_method(D_METHOD("set_path_cache", "path"), &Resource::set_path_cache);
ClassDB::bind_method(D_METHOD("set_name", "name"), &Resource::set_name);
ClassDB::bind_method(D_METHOD("get_name"), &Resource::get_name);
ClassDB::bind_method(D_METHOD("get_rid"), &Resource::get_rid);
@ -533,6 +540,12 @@ void Resource::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_local_to_scene"), &Resource::is_local_to_scene);
ClassDB::bind_method(D_METHOD("get_local_scene"), &Resource::get_local_scene);
ClassDB::bind_method(D_METHOD("setup_local_to_scene"), &Resource::setup_local_to_scene);
ClassDB::bind_method(D_METHOD("reset_state"), &Resource::reset_state);
ClassDB::bind_method(D_METHOD("set_id_for_path", "path", "id"), &Resource::set_id_for_path);
ClassDB::bind_method(D_METHOD("get_id_for_path", "path"), &Resource::get_id_for_path);
ClassDB::bind_method(D_METHOD("is_built_in"), &Resource::is_built_in);
ClassDB::bind_static_method("Resource", D_METHOD("generate_scene_unique_id"), &Resource::generate_scene_unique_id);
ClassDB::bind_method(D_METHOD("set_scene_unique_id", "id"), &Resource::set_scene_unique_id);
@ -552,6 +565,8 @@ void Resource::_bind_methods() {
GDVIRTUAL_BIND(_setup_local_to_scene);
GDVIRTUAL_BIND(_get_rid);
GDVIRTUAL_BIND(_reset_state);
GDVIRTUAL_BIND(_set_path_cache, "path");
}
Resource::Resource() :

View File

@ -89,6 +89,9 @@ protected:
GDVIRTUAL0RC(RID, _get_rid);
GDVIRTUAL1C(_set_path_cache, String);
GDVIRTUAL0(_reset_state);
public:
static Node *(*_get_local_scene_func)(); //used by editor
static void (*_update_configuration_warning)(); //used by editor
@ -144,11 +147,9 @@ public:
virtual RID get_rid() const; // some resources may offer conversion to RID
#ifdef TOOLS_ENABLED
//helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored
void set_id_for_path(const String &p_path, const String &p_id);
String get_id_for_path(const String &p_path) const;
#endif
Resource();
~Resource();

View File

@ -507,7 +507,7 @@ bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) con
for (int i = 0; i < importers.size(); i++) {
if (importers[i]->get_importer_name() == pat.importer) {
if (!importers[i]->are_import_settings_valid(p_path)) { //importer thinks this is not valid
if (!importers[i]->are_import_settings_valid(p_path, pat.metadata)) { //importer thinks this is not valid
return false;
}
}

View File

@ -153,7 +153,7 @@ public:
virtual void import_threaded_end() {}
virtual Error import_group_file(const String &p_group_file, const HashMap<String, HashMap<StringName, Variant>> &p_source_file_options, const HashMap<String, String> &p_base_paths) { return ERR_UNAVAILABLE; }
virtual bool are_import_settings_valid(const String &p_path) const { return true; }
virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const { return true; }
virtual String get_import_settings_string() const { return String(); }
};

View File

@ -303,10 +303,6 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
return res;
}
if (r_error) {
*r_error = ERR_FILE_UNRECOGNIZED;
}
ERR_FAIL_COND_V_MSG(found, Ref<Resource>(),
vformat("Failed loading resource: %s. Make sure resources have been imported by opening the project in the editor at least once.", p_path));
@ -860,7 +856,7 @@ Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro
}
}
core_bind::Semaphore done;
MessageQueue::get_main_singleton()->push_callable(callable_mp(&done, &core_bind::Semaphore::post));
MessageQueue::get_main_singleton()->push_callable(callable_mp(&done, &core_bind::Semaphore::post).bind(1));
done.wait();
}
}

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View File

@ -319,11 +319,11 @@ Vector3 AStar3D::get_closest_position_in_segment(const Vector3 &p_point) const {
return closest_point;
}
bool AStar3D::_solve(Point *begin_point, Point *end_point) {
bool AStar3D::_solve(Point *begin_point, Point *end_point, bool p_allow_partial_path) {
last_closest_point = nullptr;
pass++;
if (!end_point->enabled) {
if (!end_point->enabled && !p_allow_partial_path) {
return false;
}
@ -443,7 +443,7 @@ Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool
Point *begin_point = a;
Point *end_point = b;
bool found_route = _solve(begin_point, end_point);
bool found_route = _solve(begin_point, end_point, p_allow_partial_path);
if (!found_route) {
if (!p_allow_partial_path || last_closest_point == nullptr) {
return Vector<Vector3>();
@ -497,7 +497,7 @@ Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_
Point *begin_point = a;
Point *end_point = b;
bool found_route = _solve(begin_point, end_point);
bool found_route = _solve(begin_point, end_point, p_allow_partial_path);
if (!found_route) {
if (!p_allow_partial_path || last_closest_point == nullptr) {
return Vector<int64_t>();
@ -726,7 +726,7 @@ Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool
AStar3D::Point *begin_point = a;
AStar3D::Point *end_point = b;
bool found_route = _solve(begin_point, end_point);
bool found_route = _solve(begin_point, end_point, p_allow_partial_path);
if (!found_route) {
if (!p_allow_partial_path || astar.last_closest_point == nullptr) {
return Vector<Vector2>();
@ -780,7 +780,7 @@ Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_
AStar3D::Point *begin_point = a;
AStar3D::Point *end_point = b;
bool found_route = _solve(begin_point, end_point);
bool found_route = _solve(begin_point, end_point, p_allow_partial_path);
if (!found_route) {
if (!p_allow_partial_path || astar.last_closest_point == nullptr) {
return Vector<int64_t>();
@ -816,11 +816,11 @@ Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_
return path;
}
bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point) {
bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point, bool p_allow_partial_path) {
astar.last_closest_point = nullptr;
astar.pass++;
if (!end_point->enabled) {
if (!end_point->enabled && !p_allow_partial_path) {
return false;
}

View File

@ -115,7 +115,7 @@ class AStar3D : public RefCounted {
HashSet<Segment, Segment> segments;
Point *last_closest_point = nullptr;
bool _solve(Point *begin_point, Point *end_point);
bool _solve(Point *begin_point, Point *end_point, bool p_allow_partial_path);
protected:
static void _bind_methods();
@ -171,7 +171,7 @@ class AStar2D : public RefCounted {
GDCLASS(AStar2D, RefCounted);
AStar3D astar;
bool _solve(AStar3D::Point *begin_point, AStar3D::Point *end_point);
bool _solve(AStar3D::Point *begin_point, AStar3D::Point *end_point, bool p_allow_partial_path);
protected:
static void _bind_methods();

View File

@ -491,11 +491,11 @@ void AStarGrid2D::_get_nbors(Point *p_point, LocalVector<Point *> &r_nbors) {
}
}
bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) {
bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point, bool p_allow_partial_path) {
last_closest_point = nullptr;
pass++;
if (_get_solid_unchecked(p_end_point->id)) {
if (_get_solid_unchecked(p_end_point->id) && !p_allow_partial_path) {
return false;
}
@ -647,7 +647,7 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec
Point *begin_point = a;
Point *end_point = b;
bool found_route = _solve(begin_point, end_point);
bool found_route = _solve(begin_point, end_point, p_allow_partial_path);
if (!found_route) {
if (!p_allow_partial_path || last_closest_point == nullptr) {
return Vector<Vector2>();
@ -700,7 +700,7 @@ TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const V
Point *begin_point = a;
Point *end_point = b;
bool found_route = _solve(begin_point, end_point);
bool found_route = _solve(begin_point, end_point, p_allow_partial_path);
if (!found_route) {
if (!p_allow_partial_path || last_closest_point == nullptr) {
return TypedArray<Vector2i>();

View File

@ -159,8 +159,8 @@ private: // Internal routines.
void _get_nbors(Point *p_point, LocalVector<Point *> &r_nbors);
Point *_jump(Point *p_from, Point *p_to);
bool _solve(Point *p_begin_point, Point *p_end_point, bool p_allow_partial_path);
Point *_forced_successor(int32_t p_x, int32_t p_y, int32_t p_dx, int32_t p_dy, bool p_inclusive = false);
bool _solve(Point *p_begin_point, Point *p_end_point);
protected:
static void _bind_methods();

View File

@ -41,11 +41,11 @@ struct [[nodiscard]] Basis {
Vector3(0, 0, 1)
};
_FORCE_INLINE_ const Vector3 &operator[](int p_axis) const {
return rows[p_axis];
_FORCE_INLINE_ const Vector3 &operator[](int p_row) const {
return rows[p_row];
}
_FORCE_INLINE_ Vector3 &operator[](int p_axis) {
return rows[p_axis];
_FORCE_INLINE_ Vector3 &operator[](int p_row) {
return rows[p_row];
}
void invert();

View File

@ -204,7 +204,7 @@ public:
static Int128 mul(uint64_t a, uint64_t b);
Int128 operator-() const {
return Int128((uint64_t) - (int64_t)low, ~high + (low == 0));
return Int128(uint64_t(-int64_t(low)), ~high + (low == 0));
}
Int128 operator+(const Int128 &b) const {

View File

@ -39,16 +39,19 @@
class String;
struct [[nodiscard]] Transform2D {
// Warning #1: basis of Transform2D is stored differently from Basis. In terms of columns array, the basis matrix looks like "on paper":
// WARNING: The basis of Transform2D is stored differently from Basis.
// In terms of columns array, the basis matrix looks like "on paper":
// M = (columns[0][0] columns[1][0])
// (columns[0][1] columns[1][1])
// This is such that the columns, which can be interpreted as basis vectors of the coordinate system "painted" on the object, can be accessed as columns[i].
// Note that this is the opposite of the indices in mathematical texts, meaning: $M_{12}$ in a math book corresponds to columns[1][0] here.
// This is such that the columns, which can be interpreted as basis vectors
// of the coordinate system "painted" on the object, can be accessed as columns[i].
// NOTE: This is the opposite of the indices in mathematical texts,
// meaning: $M_{12}$ in a math book corresponds to columns[1][0] here.
// This requires additional care when working with explicit indices.
// See https://en.wikipedia.org/wiki/Row-_and_column-major_order for further reading.
// Warning #2: 2D be aware that unlike 3D code, 2D code uses a left-handed coordinate system: Y-axis points down,
// and angle is measure from +X to +Y in a clockwise-fashion.
// WARNING: Be aware that unlike 3D code, 2D code uses a left-handed coordinate system:
// Y-axis points down, and angle is measure from +X to +Y in a clockwise-fashion.
Vector2 columns[3];

View File

@ -55,16 +55,16 @@ struct [[nodiscard]] Vector4 {
real_t z;
real_t w;
};
real_t components[4] = { 0, 0, 0, 0 };
real_t coord[4] = { 0, 0, 0, 0 };
};
_FORCE_INLINE_ real_t &operator[](int p_axis) {
DEV_ASSERT((unsigned int)p_axis < 4);
return components[p_axis];
return coord[p_axis];
}
_FORCE_INLINE_ const real_t &operator[](int p_axis) const {
DEV_ASSERT((unsigned int)p_axis < 4);
return components[p_axis];
return coord[p_axis];
}
Vector4::Axis min_axis_index() const;

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View File

@ -37,6 +37,8 @@
#include "core/variant/binder_common.h"
#include "core/variant/callable.h"
#include <type_traits>
class CallableCustomMethodPointerBase : public CallableCustom {
uint32_t *comp_ptr = nullptr;
uint32_t comp_size;
@ -77,59 +79,8 @@ public:
virtual uint32_t hash() const;
};
template <typename T, typename... P>
class CallableCustomMethodPointer : public CallableCustomMethodPointerBase {
struct Data {
T *instance;
uint64_t object_id;
void (T::*method)(P...);
} data;
public:
virtual ObjectID get_object() const {
if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) {
return ObjectID();
}
return data.instance->get_instance_id();
}
virtual int get_argument_count(bool &r_is_valid) const {
r_is_valid = true;
return sizeof...(P);
}
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error);
}
CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) {
memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
data.instance = p_instance;
data.object_id = p_instance->get_instance_id();
data.method = p_method;
_setup((uint32_t *)&data, sizeof(Data));
}
};
template <typename T, typename... P>
Callable create_custom_callable_function_pointer(T *p_instance,
#ifdef DEBUG_METHODS_ENABLED
const char *p_func_text,
#endif
void (T::*p_method)(P...)) {
typedef CallableCustomMethodPointer<T, P...> CCMP; // Messes with memnew otherwise.
CCMP *ccmp = memnew(CCMP(p_instance, p_method));
#ifdef DEBUG_METHODS_ENABLED
ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
#endif
return Callable(ccmp);
}
// VERSION WITH RETURN
template <typename T, typename R, typename... P>
class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase {
class CallableCustomMethodPointer : public CallableCustomMethodPointerBase {
struct Data {
T *instance;
uint64_t object_id;
@ -152,10 +103,14 @@ public:
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
if constexpr (std::is_same<R, void>::value) {
call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error);
} else {
call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
}
CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) {
CallableCustomMethodPointer(T *p_instance, R (T::*p_method)(P...)) {
memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
data.instance = p_instance;
data.object_id = p_instance->get_instance_id();
@ -164,13 +119,13 @@ public:
}
};
template <typename T, typename R, typename... P>
template <typename T, typename... P>
Callable create_custom_callable_function_pointer(T *p_instance,
#ifdef DEBUG_METHODS_ENABLED
const char *p_func_text,
#endif
R (T::*p_method)(P...)) {
typedef CallableCustomMethodPointerRet<T, R, P...> CCMP; // Messes with memnew otherwise.
void (T::*p_method)(P...)) {
typedef CallableCustomMethodPointer<T, void, P...> CCMP; // Messes with memnew otherwise.
CCMP *ccmp = memnew(CCMP(p_instance, p_method));
#ifdef DEBUG_METHODS_ENABLED
ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
@ -178,10 +133,24 @@ Callable create_custom_callable_function_pointer(T *p_instance,
return Callable(ccmp);
}
// CONST VERSION WITH RETURN
template <typename T, typename R, typename... P>
Callable create_custom_callable_function_pointer(T *p_instance,
#ifdef DEBUG_METHODS_ENABLED
const char *p_func_text,
#endif
R (T::*p_method)(P...)) {
typedef CallableCustomMethodPointer<T, R, P...> CCMP; // Messes with memnew otherwise.
CCMP *ccmp = memnew(CCMP(p_instance, p_method));
#ifdef DEBUG_METHODS_ENABLED
ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
#endif
return Callable(ccmp);
}
// CONST VERSION
template <typename T, typename R, typename... P>
class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase {
class CallableCustomMethodPointerC : public CallableCustomMethodPointerBase {
struct Data {
T *instance;
uint64_t object_id;
@ -204,10 +173,14 @@ public:
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
if constexpr (std::is_same<R, void>::value) {
call_with_variant_argsc(data.instance, data.method, p_arguments, p_argcount, r_call_error);
} else {
call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
}
CallableCustomMethodPointerRetC(T *p_instance, R (T::*p_method)(P...) const) {
CallableCustomMethodPointerC(T *p_instance, R (T::*p_method)(P...) const) {
memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
data.instance = p_instance;
data.object_id = p_instance->get_instance_id();
@ -216,13 +189,27 @@ public:
}
};
template <typename T, typename... P>
Callable create_custom_callable_function_pointer(T *p_instance,
#ifdef DEBUG_METHODS_ENABLED
const char *p_func_text,
#endif
void (T::*p_method)(P...) const) {
typedef CallableCustomMethodPointerC<T, void, P...> CCMP; // Messes with memnew otherwise.
CCMP *ccmp = memnew(CCMP(p_instance, p_method));
#ifdef DEBUG_METHODS_ENABLED
ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
#endif
return Callable(ccmp);
}
template <typename T, typename R, typename... P>
Callable create_custom_callable_function_pointer(T *p_instance,
#ifdef DEBUG_METHODS_ENABLED
const char *p_func_text,
#endif
R (T::*p_method)(P...) const) {
typedef CallableCustomMethodPointerRetC<T, R, P...> CCMP; // Messes with memnew otherwise.
typedef CallableCustomMethodPointerC<T, R, P...> CCMP; // Messes with memnew otherwise.
CCMP *ccmp = memnew(CCMP(p_instance, p_method));
#ifdef DEBUG_METHODS_ENABLED
ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
@ -238,54 +225,8 @@ Callable create_custom_callable_function_pointer(T *p_instance,
// STATIC VERSIONS
template <typename... P>
class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase {
struct Data {
void (*method)(P...);
} data;
public:
virtual bool is_valid() const override {
return true;
}
virtual ObjectID get_object() const override {
return ObjectID();
}
virtual int get_argument_count(bool &r_is_valid) const override {
r_is_valid = true;
return sizeof...(P);
}
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error);
r_return_value = Variant();
}
CallableCustomStaticMethodPointer(void (*p_method)(P...)) {
memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
data.method = p_method;
_setup((uint32_t *)&data, sizeof(Data));
}
};
template <typename T, typename... P>
Callable create_custom_callable_static_function_pointer(
#ifdef DEBUG_METHODS_ENABLED
const char *p_func_text,
#endif
void (*p_method)(P...)) {
typedef CallableCustomStaticMethodPointer<P...> CCMP; // Messes with memnew otherwise.
CCMP *ccmp = memnew(CCMP(p_method));
#ifdef DEBUG_METHODS_ENABLED
ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
#endif
return Callable(ccmp);
}
template <typename R, typename... P>
class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerBase {
class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase {
struct Data {
R(*method)
(P...);
@ -306,23 +247,41 @@ public:
}
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
if constexpr (std::is_same<R, void>::value) {
call_with_variant_args_static(data.method, p_arguments, p_argcount, r_call_error);
} else {
call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
}
CallableCustomStaticMethodPointerRet(R (*p_method)(P...)) {
CallableCustomStaticMethodPointer(R (*p_method)(P...)) {
memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
data.method = p_method;
_setup((uint32_t *)&data, sizeof(Data));
}
};
template <typename... P>
Callable create_custom_callable_static_function_pointer(
#ifdef DEBUG_METHODS_ENABLED
const char *p_func_text,
#endif
void (*p_method)(P...)) {
typedef CallableCustomStaticMethodPointer<void, P...> CCMP; // Messes with memnew otherwise.
CCMP *ccmp = memnew(CCMP(p_method));
#ifdef DEBUG_METHODS_ENABLED
ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
#endif
return Callable(ccmp);
}
template <typename R, typename... P>
Callable create_custom_callable_static_function_pointer(
#ifdef DEBUG_METHODS_ENABLED
const char *p_func_text,
#endif
R (*p_method)(P...)) {
typedef CallableCustomStaticMethodPointerRet<R, P...> CCMP; // Messes with memnew otherwise.
typedef CallableCustomStaticMethodPointer<R, P...> CCMP; // Messes with memnew otherwise.
CCMP *ccmp = memnew(CCMP(p_method));
#ifdef DEBUG_METHODS_ENABLED
ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.

View File

@ -2,7 +2,6 @@ proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
StringName _gdvirtual_##m_name##_sn = #m_name;\\
mutable bool _gdvirtual_##m_name##_initialized = false;\\
mutable void *_gdvirtual_##m_name = nullptr;\\
template <bool required>\\
_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST {\\
ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\
if (_script_instance) {\\
@ -36,10 +35,8 @@ proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
}\\
return true;\\
}\\
if (required) {\\
ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\
$REQCHECK\\
$RVOID\\
}\\
return false;\\
}\\
_FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const {\\
@ -73,10 +70,11 @@ proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
"""
def generate_version(argcount, const=False, returns=False):
def generate_version(argcount, const=False, returns=False, required=False):
s = proto
sproto = str(argcount)
method_info = ""
method_flags = "METHOD_FLAG_VIRTUAL"
if returns:
sproto += "R"
s = s.replace("$RET", "m_ret,")
@ -86,17 +84,27 @@ def generate_version(argcount, const=False, returns=False):
method_info += "\t\tmethod_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;"
else:
s = s.replace("$RET ", "")
s = s.replace("\t\t\t$RVOID\\\n", "")
s = s.replace("\t\t$RVOID\\\n", "")
s = s.replace("\t\t\t$CALLPTRRETDEF\\\n", "")
if const:
sproto += "C"
method_flags += " | METHOD_FLAG_CONST"
s = s.replace("$CONST", "const")
s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL | METHOD_FLAG_CONST")
else:
s = s.replace("$CONST ", "")
s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL")
if required:
sproto += "_REQUIRED"
method_flags += " | METHOD_FLAG_VIRTUAL_REQUIRED"
s = s.replace(
"$REQCHECK",
'ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");',
)
else:
s = s.replace("\t\t$REQCHECK\\\n", "")
s = s.replace("$METHOD_FLAGS", method_flags)
s = s.replace("$VER", sproto)
argtext = ""
callargtext = ""
@ -198,6 +206,10 @@ def run(target, source, env):
txt += generate_version(i, False, True)
txt += generate_version(i, True, False)
txt += generate_version(i, True, True)
txt += generate_version(i, False, False, True)
txt += generate_version(i, False, True, True)
txt += generate_version(i, True, False, True)
txt += generate_version(i, True, True, True)
txt += "#endif // GDVIRTUAL_GEN_H\n"

View File

@ -44,14 +44,17 @@
#ifdef DEBUG_ENABLED
struct _ObjectDebugLock {
Object *obj;
ObjectID obj_id;
_ObjectDebugLock(Object *p_obj) {
obj = p_obj;
obj->_lock_index.ref();
obj_id = p_obj->get_instance_id();
p_obj->_lock_index.ref();
}
~_ObjectDebugLock() {
obj->_lock_index.unref();
Object *obj_ptr = ObjectDB::get_instance(obj_id);
if (likely(obj_ptr)) {
obj_ptr->_lock_index.unref();
}
}
};
@ -1454,6 +1457,24 @@ bool Object::is_connected(const StringName &p_signal, const Callable &p_callable
return s->slot_map.has(*p_callable.get_base_comparator());
}
bool Object::has_connections(const StringName &p_signal) const {
const SignalData *s = signal_map.getptr(p_signal);
if (!s) {
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal);
if (signal_is_valid) {
return false;
}
if (!script.is_null() && Ref<Script>(script)->has_script_signal(p_signal)) {
return false;
}
ERR_FAIL_V_MSG(false, "Nonexistent signal: " + p_signal + ".");
}
return !s->slot_map.is_empty();
}
void Object::disconnect(const StringName &p_signal, const Callable &p_callable) {
_disconnect(p_signal, p_callable);
}
@ -1524,21 +1545,21 @@ void Object::initialize_class() {
initialized = true;
}
StringName Object::get_translation_domain() const {
return _translation_domain;
}
void Object::set_translation_domain(const StringName &p_domain) {
_translation_domain = p_domain;
}
String Object::tr(const StringName &p_message, const StringName &p_context) const {
if (!_can_translate || !TranslationServer::get_singleton()) {
return p_message;
}
if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) {
String tr_msg = TranslationServer::get_singleton()->extractable_translate(p_message, p_context);
if (!tr_msg.is_empty() && tr_msg != p_message) {
return tr_msg;
}
return TranslationServer::get_singleton()->tool_translate(p_message, p_context);
}
return TranslationServer::get_singleton()->translate(p_message, p_context);
const Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_or_add_domain(get_translation_domain());
return domain->translate(p_message, p_context);
}
String Object::tr_n(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
@ -1550,16 +1571,8 @@ String Object::tr_n(const StringName &p_message, const StringName &p_message_plu
return p_message_plural;
}
if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) {
String tr_msg = TranslationServer::get_singleton()->extractable_translate_plural(p_message, p_message_plural, p_n, p_context);
if (!tr_msg.is_empty() && tr_msg != p_message && tr_msg != p_message_plural) {
return tr_msg;
}
return TranslationServer::get_singleton()->tool_translate_plural(p_message, p_message_plural, p_n, p_context);
}
return TranslationServer::get_singleton()->translate_plural(p_message, p_message_plural, p_n, p_context);
const Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_or_add_domain(get_translation_domain());
return domain->translate_plural(p_message, p_message_plural, p_n, p_context);
}
void Object::_clear_internal_resource_paths(const Variant &p_var) {
@ -1702,6 +1715,7 @@ void Object::_bind_methods() {
ClassDB::bind_method(D_METHOD("connect", "signal", "callable", "flags"), &Object::connect, DEFVAL(0));
ClassDB::bind_method(D_METHOD("disconnect", "signal", "callable"), &Object::disconnect);
ClassDB::bind_method(D_METHOD("is_connected", "signal", "callable"), &Object::is_connected);
ClassDB::bind_method(D_METHOD("has_connections", "signal"), &Object::has_connections);
ClassDB::bind_method(D_METHOD("set_block_signals", "enable"), &Object::set_block_signals);
ClassDB::bind_method(D_METHOD("is_blocking_signals"), &Object::is_blocking_signals);
@ -1711,6 +1725,8 @@ void Object::_bind_methods() {
ClassDB::bind_method(D_METHOD("can_translate_messages"), &Object::can_translate_messages);
ClassDB::bind_method(D_METHOD("tr", "message", "context"), &Object::tr, DEFVAL(StringName()));
ClassDB::bind_method(D_METHOD("tr_n", "message", "plural_message", "n", "context"), &Object::tr_n, DEFVAL(StringName()));
ClassDB::bind_method(D_METHOD("get_translation_domain"), &Object::get_translation_domain);
ClassDB::bind_method(D_METHOD("set_translation_domain", "domain"), &Object::set_translation_domain);
ClassDB::bind_method(D_METHOD("is_queued_for_deletion"), &Object::is_queued_for_deletion);
ClassDB::bind_method(D_METHOD("cancel_free"), &Object::cancel_free);

View File

@ -87,6 +87,7 @@ enum PropertyHint {
PROPERTY_HINT_PASSWORD,
PROPERTY_HINT_LAYERS_AVOIDANCE,
PROPERTY_HINT_DICTIONARY_TYPE,
PROPERTY_HINT_TOOL_BUTTON,
PROPERTY_HINT_MAX,
};
@ -215,6 +216,7 @@ enum MethodFlags {
METHOD_FLAG_VARARG = 16,
METHOD_FLAG_STATIC = 32,
METHOD_FLAG_OBJECT_CORE = 64,
METHOD_FLAG_VIRTUAL_REQUIRED = 128,
METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL,
};
@ -368,11 +370,8 @@ struct ObjectGDExtension {
#endif
};
#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call<false>(__VA_ARGS__)
#define GDVIRTUAL_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call<false>(__VA_ARGS__)
#define GDVIRTUAL_REQUIRED_CALL(m_name, ...) _gdvirtual_##m_name##_call<true>(__VA_ARGS__)
#define GDVIRTUAL_REQUIRED_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call<true>(__VA_ARGS__)
#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call(__VA_ARGS__)
#define GDVIRTUAL_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call(__VA_ARGS__)
#ifdef DEBUG_METHODS_ENABLED
#define GDVIRTUAL_BIND(m_name, ...) ::ClassDB::add_virtual_method(get_class_static(), _gdvirtual_##m_name##_get_method_info(), true, sarray(__VA_ARGS__));
@ -665,6 +664,8 @@ private:
Object(bool p_reference);
protected:
StringName _translation_domain;
_FORCE_INLINE_ bool _instance_binding_reference(bool p_reference) {
bool can_die = true;
if (_instance_bindings) {
@ -930,6 +931,7 @@ public:
MTVIRTUAL Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0);
MTVIRTUAL void disconnect(const StringName &p_signal, const Callable &p_callable);
MTVIRTUAL bool is_connected(const StringName &p_signal, const Callable &p_callable) const;
MTVIRTUAL bool has_connections(const StringName &p_signal) const;
template <typename... VarArgs>
void call_deferred(const StringName &p_name, VarArgs... p_args) {
@ -954,6 +956,9 @@ public:
_FORCE_INLINE_ void set_message_translation(bool p_enable) { _can_translate = p_enable; }
_FORCE_INLINE_ bool can_translate_messages() const { return _can_translate; }
virtual StringName get_translation_domain() const;
virtual void set_translation_domain(const StringName &p_domain);
#ifdef TOOLS_ENABLED
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const;
void editor_set_section_unfold(const String &p_section, bool p_unfolded);

View File

@ -174,6 +174,8 @@ void Script::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_tool"), &Script::is_tool);
ClassDB::bind_method(D_METHOD("is_abstract"), &Script::is_abstract);
ClassDB::bind_method(D_METHOD("get_rpc_config"), &Script::get_rpc_config);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "source_code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_source_code", "get_source_code");
}
@ -189,7 +191,17 @@ void Script::reload_from_file() {
set_source_code(rel->get_source_code());
set_last_modified_time(rel->get_last_modified_time());
reload();
// Only reload the script when there are no compilation errors to prevent printing the error messages twice.
if (rel->is_valid()) {
if (Engine::get_singleton()->is_editor_hint() && is_tool()) {
get_language()->reload_tool_script(this, true);
} else {
// It's important to set p_keep_state to true in order to manage reloading scripts
// that are currently instantiated.
reload(true);
}
}
#else
Resource::reload_from_file();
#endif

View File

@ -112,7 +112,10 @@ class Script : public Resource {
OBJ_SAVE_TYPE(Script);
protected:
virtual bool editor_can_reload_from_file() override { return false; } // this is handled by editor better
// Scripts are reloaded via the Script Editor when edited in Godot,
// the LSP server when edited in a connected external editor, or
// through EditorFileSystem::_update_script_documentation when updated directly on disk.
virtual bool editor_can_reload_from_file() override { return false; }
void _notification(int p_what);
static void _bind_methods();
@ -182,7 +185,7 @@ public:
virtual bool is_placeholder_fallback_enabled() const { return false; }
virtual const Variant get_rpc_config() const = 0;
virtual Variant get_rpc_config() const = 0;
Script() {}
};

View File

@ -57,16 +57,16 @@ public:
EXBIND1RC(bool, inherits_script, const Ref<Script> &)
EXBIND0RC(StringName, get_instance_base_type)
GDVIRTUAL1RC(GDExtensionPtr<void>, _instance_create, Object *)
GDVIRTUAL1RC_REQUIRED(GDExtensionPtr<void>, _instance_create, Object *)
virtual ScriptInstance *instance_create(Object *p_this) override {
GDExtensionPtr<void> ret = nullptr;
GDVIRTUAL_REQUIRED_CALL(_instance_create, p_this, ret);
GDVIRTUAL_CALL(_instance_create, p_this, ret);
return reinterpret_cast<ScriptInstance *>(ret.operator void *());
}
GDVIRTUAL1RC(GDExtensionPtr<void>, _placeholder_instance_create, Object *)
GDVIRTUAL1RC_REQUIRED(GDExtensionPtr<void>, _placeholder_instance_create, Object *)
PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this) override {
GDExtensionPtr<void> ret = nullptr;
GDVIRTUAL_REQUIRED_CALL(_placeholder_instance_create, p_this, ret);
GDVIRTUAL_CALL(_placeholder_instance_create, p_this, ret);
return reinterpret_cast<PlaceHolderScriptInstance *>(ret.operator void *());
}
@ -76,12 +76,12 @@ public:
EXBIND1(set_source_code, const String &)
EXBIND1R(Error, reload, bool)
GDVIRTUAL0RC(TypedArray<Dictionary>, _get_documentation)
GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_documentation)
GDVIRTUAL0RC(String, _get_class_icon_path)
#ifdef TOOLS_ENABLED
virtual Vector<DocData::ClassDoc> get_documentation() const override {
TypedArray<Dictionary> doc;
GDVIRTUAL_REQUIRED_CALL(_get_documentation, doc);
GDVIRTUAL_CALL(_get_documentation, doc);
Vector<DocData::ClassDoc> class_doc;
for (int i = 0; i < doc.size(); i++) {
@ -114,10 +114,10 @@ public:
return Script::get_script_method_argument_count(p_method, r_is_valid);
}
GDVIRTUAL1RC(Dictionary, _get_method_info, const StringName &)
GDVIRTUAL1RC_REQUIRED(Dictionary, _get_method_info, const StringName &)
virtual MethodInfo get_method_info(const StringName &p_method) const override {
Dictionary mi;
GDVIRTUAL_REQUIRED_CALL(_get_method_info, p_method, mi);
GDVIRTUAL_CALL(_get_method_info, p_method, mi);
return MethodInfo::from_dict(mi);
}
@ -133,47 +133,47 @@ public:
EXBIND0RC(ScriptLanguage *, get_language)
EXBIND1RC(bool, has_script_signal, const StringName &)
GDVIRTUAL0RC(TypedArray<Dictionary>, _get_script_signal_list)
GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_script_signal_list)
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const override {
TypedArray<Dictionary> sl;
GDVIRTUAL_REQUIRED_CALL(_get_script_signal_list, sl);
GDVIRTUAL_CALL(_get_script_signal_list, sl);
for (int i = 0; i < sl.size(); i++) {
r_signals->push_back(MethodInfo::from_dict(sl[i]));
}
}
GDVIRTUAL1RC(bool, _has_property_default_value, const StringName &)
GDVIRTUAL1RC(Variant, _get_property_default_value, const StringName &)
GDVIRTUAL1RC_REQUIRED(bool, _has_property_default_value, const StringName &)
GDVIRTUAL1RC_REQUIRED(Variant, _get_property_default_value, const StringName &)
virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const override {
bool has_dv = false;
if (!GDVIRTUAL_REQUIRED_CALL(_has_property_default_value, p_property, has_dv) || !has_dv) {
if (!GDVIRTUAL_CALL(_has_property_default_value, p_property, has_dv) || !has_dv) {
return false;
}
Variant ret;
GDVIRTUAL_REQUIRED_CALL(_get_property_default_value, p_property, ret);
GDVIRTUAL_CALL(_get_property_default_value, p_property, ret);
r_value = ret;
return true;
}
EXBIND0(update_exports)
GDVIRTUAL0RC(TypedArray<Dictionary>, _get_script_method_list)
GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_script_method_list)
virtual void get_script_method_list(List<MethodInfo> *r_methods) const override {
TypedArray<Dictionary> sl;
GDVIRTUAL_REQUIRED_CALL(_get_script_method_list, sl);
GDVIRTUAL_CALL(_get_script_method_list, sl);
for (int i = 0; i < sl.size(); i++) {
r_methods->push_back(MethodInfo::from_dict(sl[i]));
}
}
GDVIRTUAL0RC(TypedArray<Dictionary>, _get_script_property_list)
GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_script_property_list)
virtual void get_script_property_list(List<PropertyInfo> *r_propertys) const override {
TypedArray<Dictionary> sl;
GDVIRTUAL_REQUIRED_CALL(_get_script_property_list, sl);
GDVIRTUAL_CALL(_get_script_property_list, sl);
for (int i = 0; i < sl.size(); i++) {
r_propertys->push_back(PropertyInfo::from_dict(sl[i]));
}
@ -181,21 +181,21 @@ public:
EXBIND1RC(int, get_member_line, const StringName &)
GDVIRTUAL0RC(Dictionary, _get_constants)
GDVIRTUAL0RC_REQUIRED(Dictionary, _get_constants)
virtual void get_constants(HashMap<StringName, Variant> *p_constants) override {
Dictionary constants;
GDVIRTUAL_REQUIRED_CALL(_get_constants, constants);
GDVIRTUAL_CALL(_get_constants, constants);
List<Variant> keys;
constants.get_key_list(&keys);
for (const Variant &K : keys) {
p_constants->insert(K, constants[K]);
}
}
GDVIRTUAL0RC(TypedArray<StringName>, _get_members)
GDVIRTUAL0RC_REQUIRED(TypedArray<StringName>, _get_members)
virtual void get_members(HashSet<StringName> *p_members) override {
TypedArray<StringName> members;
GDVIRTUAL_REQUIRED_CALL(_get_members, members);
GDVIRTUAL_CALL(_get_members, members);
for (int i = 0; i < members.size(); i++) {
p_members->insert(members[i]);
}
@ -203,11 +203,11 @@ public:
EXBIND0RC(bool, is_placeholder_fallback_enabled)
GDVIRTUAL0RC(Variant, _get_rpc_config)
GDVIRTUAL0RC_REQUIRED(Variant, _get_rpc_config)
virtual const Variant get_rpc_config() const override {
virtual Variant get_rpc_config() const override {
Variant ret;
GDVIRTUAL_REQUIRED_CALL(_get_rpc_config, ret);
GDVIRTUAL_CALL(_get_rpc_config, ret);
return ret;
}
@ -233,22 +233,22 @@ public:
/* EDITOR FUNCTIONS */
GDVIRTUAL0RC(Vector<String>, _get_reserved_words)
GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_reserved_words)
virtual void get_reserved_words(List<String> *p_words) const override {
Vector<String> ret;
GDVIRTUAL_REQUIRED_CALL(_get_reserved_words, ret);
GDVIRTUAL_CALL(_get_reserved_words, ret);
for (int i = 0; i < ret.size(); i++) {
p_words->push_back(ret[i]);
}
}
EXBIND1RC(bool, is_control_flow_keyword, const String &)
GDVIRTUAL0RC(Vector<String>, _get_comment_delimiters)
GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_comment_delimiters)
virtual void get_comment_delimiters(List<String> *p_words) const override {
Vector<String> ret;
GDVIRTUAL_REQUIRED_CALL(_get_comment_delimiters, ret);
GDVIRTUAL_CALL(_get_comment_delimiters, ret);
for (int i = 0; i < ret.size(); i++) {
p_words->push_back(ret[i]);
}
@ -264,11 +264,11 @@ public:
}
}
GDVIRTUAL0RC(Vector<String>, _get_string_delimiters)
GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_string_delimiters)
virtual void get_string_delimiters(List<String> *p_words) const override {
Vector<String> ret;
GDVIRTUAL_REQUIRED_CALL(_get_string_delimiters, ret);
GDVIRTUAL_CALL(_get_string_delimiters, ret);
for (int i = 0; i < ret.size(); i++) {
p_words->push_back(ret[i]);
}
@ -276,11 +276,11 @@ public:
EXBIND3RC(Ref<Script>, make_template, const String &, const String &, const String &)
GDVIRTUAL1RC(TypedArray<Dictionary>, _get_built_in_templates, StringName)
GDVIRTUAL1RC_REQUIRED(TypedArray<Dictionary>, _get_built_in_templates, StringName)
virtual Vector<ScriptTemplate> get_built_in_templates(const StringName &p_object) override {
TypedArray<Dictionary> ret;
GDVIRTUAL_REQUIRED_CALL(_get_built_in_templates, p_object, ret);
GDVIRTUAL_CALL(_get_built_in_templates, p_object, ret);
Vector<ScriptTemplate> stret;
for (int i = 0; i < ret.size(); i++) {
Dictionary d = ret[i];
@ -304,10 +304,10 @@ public:
EXBIND0R(bool, is_using_templates)
GDVIRTUAL6RC(Dictionary, _validate, const String &, const String &, bool, bool, bool, bool)
GDVIRTUAL6RC_REQUIRED(Dictionary, _validate, const String &, const String &, bool, bool, bool, bool)
virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override {
Dictionary ret;
GDVIRTUAL_REQUIRED_CALL(_validate, p_script, p_path, r_functions != nullptr, r_errors != nullptr, r_warnings != nullptr, r_safe_lines != nullptr, ret);
GDVIRTUAL_CALL(_validate, p_script, p_path, r_functions != nullptr, r_errors != nullptr, r_warnings != nullptr, r_safe_lines != nullptr, ret);
if (!ret.has("valid")) {
return false;
}
@ -371,10 +371,10 @@ public:
}
EXBIND1RC(String, validate_path, const String &)
GDVIRTUAL0RC(Object *, _create_script)
GDVIRTUAL0RC_REQUIRED(Object *, _create_script)
Script *create_script() const override {
Object *ret = nullptr;
GDVIRTUAL_REQUIRED_CALL(_create_script, ret);
GDVIRTUAL_CALL(_create_script, ret);
return Object::cast_to<Script>(ret);
}
#ifndef DISABLE_DEPRECATED
@ -400,11 +400,11 @@ public:
return ScriptNameCasing::SCRIPT_NAME_CASING_SNAKE_CASE;
}
GDVIRTUAL3RC(Dictionary, _complete_code, const String &, const String &, Object *)
GDVIRTUAL3RC_REQUIRED(Dictionary, _complete_code, const String &, const String &, Object *)
virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<CodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) override {
Dictionary ret;
GDVIRTUAL_REQUIRED_CALL(_complete_code, p_code, p_path, p_owner, ret);
GDVIRTUAL_CALL(_complete_code, p_code, p_path, p_owner, ret);
if (!ret.has("result")) {
return ERR_UNAVAILABLE;
}
@ -449,11 +449,11 @@ public:
return result;
}
GDVIRTUAL4RC(Dictionary, _lookup_code, const String &, const String &, const String &, Object *)
GDVIRTUAL4RC_REQUIRED(Dictionary, _lookup_code, const String &, const String &, const String &, Object *)
virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) override {
Dictionary ret;
GDVIRTUAL_REQUIRED_CALL(_lookup_code, p_code, p_symbol, p_path, p_owner, ret);
GDVIRTUAL_CALL(_lookup_code, p_code, p_symbol, p_path, p_owner, ret);
if (!ret.has("result")) {
return ERR_UNAVAILABLE;
}
@ -474,10 +474,10 @@ public:
return result;
}
GDVIRTUAL3RC(String, _auto_indent_code, const String &, int, int)
GDVIRTUAL3RC_REQUIRED(String, _auto_indent_code, const String &, int, int)
virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override {
String ret;
GDVIRTUAL_REQUIRED_CALL(_auto_indent_code, p_code, p_from_line, p_to_line, ret);
GDVIRTUAL_CALL(_auto_indent_code, p_code, p_from_line, p_to_line, ret);
p_code = ret;
}
EXBIND2(add_global_constant, const StringName &, const Variant &)
@ -496,10 +496,10 @@ public:
EXBIND1RC(String, debug_get_stack_level_function, int)
EXBIND1RC(String, debug_get_stack_level_source, int)
GDVIRTUAL3R(Dictionary, _debug_get_stack_level_locals, int, int, int)
GDVIRTUAL3R_REQUIRED(Dictionary, _debug_get_stack_level_locals, int, int, int)
virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override {
Dictionary ret;
GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_locals, p_level, p_max_subitems, p_max_depth, ret);
GDVIRTUAL_CALL(_debug_get_stack_level_locals, p_level, p_max_subitems, p_max_depth, ret);
if (ret.size() == 0) {
return;
}
@ -516,10 +516,10 @@ public:
}
}
}
GDVIRTUAL3R(Dictionary, _debug_get_stack_level_members, int, int, int)
GDVIRTUAL3R_REQUIRED(Dictionary, _debug_get_stack_level_members, int, int, int)
virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override {
Dictionary ret;
GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_members, p_level, p_max_subitems, p_max_depth, ret);
GDVIRTUAL_CALL(_debug_get_stack_level_members, p_level, p_max_subitems, p_max_depth, ret);
if (ret.size() == 0) {
return;
}
@ -536,17 +536,17 @@ public:
}
}
}
GDVIRTUAL1R(GDExtensionPtr<void>, _debug_get_stack_level_instance, int)
GDVIRTUAL1R_REQUIRED(GDExtensionPtr<void>, _debug_get_stack_level_instance, int)
virtual ScriptInstance *debug_get_stack_level_instance(int p_level) override {
GDExtensionPtr<void> ret = nullptr;
GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_instance, p_level, ret);
GDVIRTUAL_CALL(_debug_get_stack_level_instance, p_level, ret);
return reinterpret_cast<ScriptInstance *>(ret.operator void *());
}
GDVIRTUAL2R(Dictionary, _debug_get_globals, int, int)
GDVIRTUAL2R_REQUIRED(Dictionary, _debug_get_globals, int, int)
virtual void debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override {
Dictionary ret;
GDVIRTUAL_REQUIRED_CALL(_debug_get_globals, p_max_subitems, p_max_depth, ret);
GDVIRTUAL_CALL(_debug_get_globals, p_max_subitems, p_max_depth, ret);
if (ret.size() == 0) {
return;
}
@ -566,10 +566,10 @@ public:
EXBIND4R(String, debug_parse_stack_level_expression, int, const String &, int, int)
GDVIRTUAL0R(TypedArray<Dictionary>, _debug_get_current_stack_info)
GDVIRTUAL0R_REQUIRED(TypedArray<Dictionary>, _debug_get_current_stack_info)
virtual Vector<StackInfo> debug_get_current_stack_info() override {
TypedArray<Dictionary> ret;
GDVIRTUAL_REQUIRED_CALL(_debug_get_current_stack_info, ret);
GDVIRTUAL_CALL(_debug_get_current_stack_info, ret);
Vector<StackInfo> sret;
for (const Variant &var : ret) {
StackInfo si;
@ -590,29 +590,29 @@ public:
EXBIND2(reload_tool_script, const Ref<Script> &, bool)
/* LOADER FUNCTIONS */
GDVIRTUAL0RC(PackedStringArray, _get_recognized_extensions)
GDVIRTUAL0RC_REQUIRED(PackedStringArray, _get_recognized_extensions)
virtual void get_recognized_extensions(List<String> *p_extensions) const override {
PackedStringArray ret;
GDVIRTUAL_REQUIRED_CALL(_get_recognized_extensions, ret);
GDVIRTUAL_CALL(_get_recognized_extensions, ret);
for (int i = 0; i < ret.size(); i++) {
p_extensions->push_back(ret[i]);
}
}
GDVIRTUAL0RC(TypedArray<Dictionary>, _get_public_functions)
GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_public_functions)
virtual void get_public_functions(List<MethodInfo> *p_functions) const override {
TypedArray<Dictionary> ret;
GDVIRTUAL_REQUIRED_CALL(_get_public_functions, ret);
GDVIRTUAL_CALL(_get_public_functions, ret);
for (const Variant &var : ret) {
MethodInfo mi = MethodInfo::from_dict(var);
p_functions->push_back(mi);
}
}
GDVIRTUAL0RC(Dictionary, _get_public_constants)
GDVIRTUAL0RC_REQUIRED(Dictionary, _get_public_constants)
virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override {
Dictionary ret;
GDVIRTUAL_REQUIRED_CALL(_get_public_constants, ret);
GDVIRTUAL_CALL(_get_public_constants, ret);
for (int i = 0; i < ret.size(); i++) {
Dictionary d = ret[i];
ERR_CONTINUE(!d.has("name"));
@ -620,10 +620,10 @@ public:
p_constants->push_back(Pair<String, Variant>(d["name"], d["value"]));
}
}
GDVIRTUAL0RC(TypedArray<Dictionary>, _get_public_annotations)
GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_public_annotations)
virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override {
TypedArray<Dictionary> ret;
GDVIRTUAL_REQUIRED_CALL(_get_public_annotations, ret);
GDVIRTUAL_CALL(_get_public_annotations, ret);
for (const Variant &var : ret) {
MethodInfo mi = MethodInfo::from_dict(var);
p_annotations->push_back(mi);
@ -634,19 +634,19 @@ public:
EXBIND0(profiling_stop)
EXBIND1(profiling_set_save_native_calls, bool)
GDVIRTUAL2R(int, _profiling_get_accumulated_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int)
GDVIRTUAL2R_REQUIRED(int, _profiling_get_accumulated_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int)
virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override {
int ret = 0;
GDVIRTUAL_REQUIRED_CALL(_profiling_get_accumulated_data, p_info_arr, p_info_max, ret);
GDVIRTUAL_CALL(_profiling_get_accumulated_data, p_info_arr, p_info_max, ret);
return ret;
}
GDVIRTUAL2R(int, _profiling_get_frame_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int)
GDVIRTUAL2R_REQUIRED(int, _profiling_get_frame_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int)
virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override {
int ret = 0;
GDVIRTUAL_REQUIRED_CALL(_profiling_get_frame_data, p_info_arr, p_info_max, ret);
GDVIRTUAL_CALL(_profiling_get_frame_data, p_info_arr, p_info_max, ret);
return ret;
}
@ -654,11 +654,11 @@ public:
EXBIND1RC(bool, handles_global_class_type, const String &)
GDVIRTUAL1RC(Dictionary, _get_global_class_name, const String &)
GDVIRTUAL1RC_REQUIRED(Dictionary, _get_global_class_name, const String &)
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const override {
Dictionary ret;
GDVIRTUAL_REQUIRED_CALL(_get_global_class_name, p_path, ret);
GDVIRTUAL_CALL(_get_global_class_name, p_path, ret);
if (!ret.has("name")) {
return String();
}

View File

@ -48,7 +48,7 @@ void UndoRedo::Operation::delete_reference() {
}
}
void UndoRedo::_discard_redo() {
void UndoRedo::discard_redo() {
if (current_action == actions.size() - 1) {
return;
}
@ -89,7 +89,7 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode, bool p_back
uint64_t ticks = OS::get_singleton()->get_ticks_msec();
if (action_level == 0) {
_discard_redo();
discard_redo();
// Check if the merge operation is valid
if (p_mode != MERGE_DISABLE && actions.size() && actions[actions.size() - 1].name == p_name && actions[actions.size() - 1].backward_undo_ops == p_backward_undo_ops && actions[actions.size() - 1].last_tick + 800 > ticks) {
@ -288,7 +288,7 @@ void UndoRedo::end_force_keep_in_merge_ends() {
}
void UndoRedo::_pop_history_tail() {
_discard_redo();
discard_redo();
if (!actions.size()) {
return;
@ -455,7 +455,7 @@ String UndoRedo::get_action_name(int p_id) {
void UndoRedo::clear_history(bool p_increase_version) {
ERR_FAIL_COND(action_level > 0);
_discard_redo();
discard_redo();
while (actions.size()) {
_pop_history_tail();

View File

@ -129,6 +129,7 @@ public:
int get_current_action();
String get_action_name(int p_id);
void clear_history(bool p_increase_version = true);
void discard_redo();
bool has_undo() const;
bool has_redo() const;

View File

@ -180,13 +180,17 @@ void WorkerThreadPool::_process_task(Task *p_task) {
void WorkerThreadPool::_thread_function(void *p_user) {
ThreadData *thread_data = (ThreadData *)p_user;
while (true) {
Task *task_to_process = nullptr;
{
MutexLock lock(singleton->task_mutex);
if (singleton->exit_threads) {
return;
bool exit = singleton->_handle_runlevel(thread_data, lock);
if (unlikely(exit)) {
break;
}
thread_data->signaled = false;
if (singleton->task_queue.first()) {
@ -194,7 +198,6 @@ void WorkerThreadPool::_thread_function(void *p_user) {
singleton->task_queue.remove(singleton->task_queue.first());
} else {
thread_data->cond_var.wait(lock);
DEV_ASSERT(singleton->exit_threads || thread_data->signaled);
}
}
@ -204,19 +207,24 @@ void WorkerThreadPool::_thread_function(void *p_user) {
}
}
void WorkerThreadPool::_post_tasks_and_unlock(Task **p_tasks, uint32_t p_count, bool p_high_priority) {
void WorkerThreadPool::_post_tasks(Task **p_tasks, uint32_t p_count, bool p_high_priority, MutexLock<BinaryMutex> &p_lock) {
// Fall back to processing on the calling thread if there are no worker threads.
// Separated into its own variable to make it easier to extend this logic
// in custom builds.
bool process_on_calling_thread = threads.size() == 0;
if (process_on_calling_thread) {
task_mutex.unlock();
p_lock.temp_unlock();
for (uint32_t i = 0; i < p_count; i++) {
_process_task(p_tasks[i]);
}
p_lock.temp_relock();
return;
}
while (runlevel == RUNLEVEL_EXIT_LANGUAGES) {
control_cond_var.wait(p_lock);
}
uint32_t to_process = 0;
uint32_t to_promote = 0;
@ -238,8 +246,6 @@ void WorkerThreadPool::_post_tasks_and_unlock(Task **p_tasks, uint32_t p_count,
}
_notify_threads(caller_pool_thread, to_process, to_promote);
task_mutex.unlock();
}
void WorkerThreadPool::_notify_threads(const ThreadData *p_current_thread_data, uint32_t p_process_count, uint32_t p_promote_count) {
@ -323,9 +329,8 @@ WorkerThreadPool::TaskID WorkerThreadPool::add_native_task(void (*p_func)(void *
}
WorkerThreadPool::TaskID WorkerThreadPool::_add_task(const Callable &p_callable, void (*p_func)(void *), void *p_userdata, BaseTemplateUserdata *p_template_userdata, bool p_high_priority, const String &p_description) {
ERR_FAIL_COND_V_MSG(threads.is_empty(), INVALID_TASK_ID, "Can't add a task because the WorkerThreadPool is either not initialized yet or already terminated.");
MutexLock<BinaryMutex> lock(task_mutex);
task_mutex.lock();
// Get a free task
Task *task = task_allocator.alloc();
TaskID id = last_task++;
@ -337,7 +342,7 @@ WorkerThreadPool::TaskID WorkerThreadPool::_add_task(const Callable &p_callable,
task->template_userdata = p_template_userdata;
tasks.insert(id, task);
_post_tasks_and_unlock(&task, 1, p_high_priority);
_post_tasks(&task, 1, p_high_priority, lock);
return id;
}
@ -444,22 +449,34 @@ void WorkerThreadPool::_unlock_unlockable_mutexes() {
void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task) {
// Keep processing tasks until the condition to stop waiting is met.
#define IS_WAIT_OVER (unlikely(p_task == ThreadData::YIELDING) ? p_caller_pool_thread->yield_is_over : p_task->completed)
while (true) {
Task *task_to_process = nullptr;
bool relock_unlockables = false;
{
MutexLock lock(task_mutex);
bool was_signaled = p_caller_pool_thread->signaled;
p_caller_pool_thread->signaled = false;
if (IS_WAIT_OVER) {
if (unlikely(p_task == ThreadData::YIELDING)) {
p_caller_pool_thread->yield_is_over = false;
bool exit = _handle_runlevel(p_caller_pool_thread, lock);
if (unlikely(exit)) {
break;
}
if (!exit_threads && was_signaled) {
bool wait_is_over = false;
if (unlikely(p_task == ThreadData::YIELDING)) {
if (p_caller_pool_thread->yield_is_over) {
p_caller_pool_thread->yield_is_over = false;
wait_is_over = true;
}
} else {
if (p_task->completed) {
wait_is_over = true;
}
}
if (wait_is_over) {
if (was_signaled) {
// This thread was awaken for some additional reason, but it's about to exit.
// Let's find out what may be pending and forward the requests.
uint32_t to_process = task_queue.first() ? 1 : 0;
@ -474,7 +491,6 @@ void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, T
break;
}
if (!exit_threads) {
if (p_caller_pool_thread->current_task->low_priority && low_priority_task_queue.first()) {
if (_try_promote_low_priority_task()) {
_notify_threads(p_caller_pool_thread, 1, 0);
@ -491,13 +507,12 @@ void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, T
_unlock_unlockable_mutexes();
relock_unlockables = true;
p_caller_pool_thread->cond_var.wait(lock);
DEV_ASSERT(exit_threads || p_caller_pool_thread->signaled || IS_WAIT_OVER);
p_caller_pool_thread->awaited_task = nullptr;
}
}
}
if (relock_unlockables) {
_lock_unlockable_mutexes();
@ -509,16 +524,65 @@ void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, T
}
}
void WorkerThreadPool::_switch_runlevel(Runlevel p_runlevel) {
DEV_ASSERT(p_runlevel > runlevel);
runlevel = p_runlevel;
memset(&runlevel_data, 0, sizeof(runlevel_data));
for (uint32_t i = 0; i < threads.size(); i++) {
threads[i].cond_var.notify_one();
threads[i].signaled = true;
}
control_cond_var.notify_all();
}
// Returns whether threads have to exit. This may perform the check about handling needed.
bool WorkerThreadPool::_handle_runlevel(ThreadData *p_thread_data, MutexLock<BinaryMutex> &p_lock) {
bool exit = false;
switch (runlevel) {
case RUNLEVEL_NORMAL: {
} break;
case RUNLEVEL_PRE_EXIT_LANGUAGES: {
if (!p_thread_data->pre_exited_languages) {
if (!task_queue.first() && !low_priority_task_queue.first()) {
p_thread_data->pre_exited_languages = true;
runlevel_data.pre_exit_languages.num_idle_threads++;
control_cond_var.notify_all();
}
}
} break;
case RUNLEVEL_EXIT_LANGUAGES: {
if (!p_thread_data->exited_languages) {
p_lock.temp_unlock();
ScriptServer::thread_exit();
p_lock.temp_relock();
p_thread_data->exited_languages = true;
runlevel_data.exit_languages.num_exited_threads++;
control_cond_var.notify_all();
}
} break;
case RUNLEVEL_EXIT: {
exit = true;
} break;
}
return exit;
}
void WorkerThreadPool::yield() {
int th_index = get_thread_index();
ERR_FAIL_COND_MSG(th_index == -1, "This function can only be called from a worker thread.");
_wait_collaboratively(&threads[th_index], ThreadData::YIELDING);
task_mutex.lock();
if (runlevel < RUNLEVEL_EXIT_LANGUAGES) {
// If this long-lived task started before the scripting server was initialized,
// now is a good time to have scripting languages ready for the current thread.
// Otherwise, such a piece of setup won't happen unless another task has been
// run during the collaborative wait.
task_mutex.unlock();
ScriptServer::thread_enter();
} else {
task_mutex.unlock();
}
}
void WorkerThreadPool::notify_yield_over(TaskID p_task_id) {
@ -543,13 +607,13 @@ void WorkerThreadPool::notify_yield_over(TaskID p_task_id) {
}
WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) {
ERR_FAIL_COND_V_MSG(threads.is_empty(), INVALID_TASK_ID, "Can't add a group task because the WorkerThreadPool is either not initialized yet or already terminated.");
ERR_FAIL_COND_V(p_elements < 0, INVALID_TASK_ID);
if (p_tasks < 0) {
p_tasks = MAX(1u, threads.size());
}
task_mutex.lock();
MutexLock<BinaryMutex> lock(task_mutex);
Group *group = group_allocator.alloc();
GroupID id = last_task++;
group->max = p_elements;
@ -584,7 +648,7 @@ WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_ca
groups[id] = group;
_post_tasks_and_unlock(tasks_posted, p_tasks, p_high_priority);
_post_tasks(tasks_posted, p_tasks, p_high_priority, lock);
return id;
}
@ -687,6 +751,9 @@ void WorkerThreadPool::thread_exit_unlock_allowance_zone(uint32_t p_zone_id) {
void WorkerThreadPool::init(int p_thread_count, float p_low_priority_task_ratio) {
ERR_FAIL_COND(threads.size() > 0);
runlevel = RUNLEVEL_NORMAL;
if (p_thread_count < 0) {
p_thread_count = OS::get_singleton()->get_default_thread_pool_size();
}
@ -704,6 +771,26 @@ void WorkerThreadPool::init(int p_thread_count, float p_low_priority_task_ratio)
}
}
void WorkerThreadPool::exit_languages_threads() {
if (threads.size() == 0) {
return;
}
MutexLock lock(task_mutex);
// Wait until all threads are idle.
_switch_runlevel(RUNLEVEL_PRE_EXIT_LANGUAGES);
while (runlevel_data.pre_exit_languages.num_idle_threads != threads.size()) {
control_cond_var.wait(lock);
}
// Wait until all threads have detached from scripting languages.
_switch_runlevel(RUNLEVEL_EXIT_LANGUAGES);
while (runlevel_data.exit_languages.num_exited_threads != threads.size()) {
control_cond_var.wait(lock);
}
}
void WorkerThreadPool::finish() {
if (threads.size() == 0) {
return;
@ -716,15 +803,10 @@ void WorkerThreadPool::finish() {
print_error("Task waiting was never re-claimed: " + E->self()->description);
E = E->next();
}
_switch_runlevel(RUNLEVEL_EXIT);
}
{
MutexLock lock(task_mutex);
exit_threads = true;
}
for (ThreadData &data : threads) {
data.cond_var.notify_one();
}
for (ThreadData &data : threads) {
data.thread.wait_to_finish();
}
@ -755,5 +837,5 @@ WorkerThreadPool::WorkerThreadPool() {
}
WorkerThreadPool::~WorkerThreadPool() {
DEV_ASSERT(threads.size() == 0 && "finish() hasn't been called!");
finish();
}

View File

@ -114,17 +114,35 @@ private:
Thread thread;
bool signaled : 1;
bool yield_is_over : 1;
bool pre_exited_languages : 1;
bool exited_languages : 1;
Task *current_task = nullptr;
Task *awaited_task = nullptr; // Null if not awaiting the condition variable, or special value (YIELDING).
ConditionVariable cond_var;
ThreadData() :
signaled(false),
yield_is_over(false) {}
yield_is_over(false),
pre_exited_languages(false),
exited_languages(false) {}
};
TightLocalVector<ThreadData> threads;
bool exit_threads = false;
enum Runlevel {
RUNLEVEL_NORMAL,
RUNLEVEL_PRE_EXIT_LANGUAGES, // Block adding new tasks
RUNLEVEL_EXIT_LANGUAGES, // All threads detach from scripting threads.
RUNLEVEL_EXIT,
} runlevel = RUNLEVEL_NORMAL;
union { // Cleared on every runlevel change.
struct {
uint32_t num_idle_threads;
} pre_exit_languages;
struct {
uint32_t num_exited_threads;
} exit_languages;
} runlevel_data;
ConditionVariable control_cond_var;
HashMap<Thread::ID, int> thread_ids;
HashMap<
@ -152,7 +170,7 @@ private:
void _process_task(Task *task);
void _post_tasks_and_unlock(Task **p_tasks, uint32_t p_count, bool p_high_priority);
void _post_tasks(Task **p_tasks, uint32_t p_count, bool p_high_priority, MutexLock<BinaryMutex> &p_lock);
void _notify_threads(const ThreadData *p_current_thread_data, uint32_t p_process_count, uint32_t p_promote_count);
bool _try_promote_low_priority_task();
@ -193,6 +211,9 @@ private:
void _wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task);
void _switch_runlevel(Runlevel p_runlevel);
bool _handle_runlevel(ThreadData *p_thread_data, MutexLock<BinaryMutex> &p_lock);
#ifdef THREADS_ENABLED
static uint32_t _thread_enter_unlock_allowance_zone(THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &p_ulock);
#endif
@ -256,6 +277,7 @@ public:
#endif
void init(int p_thread_count = -1, float p_low_priority_task_ratio = 0.3);
void exit_languages_threads();
void finish();
WorkerThreadPool();
~WorkerThreadPool();

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View File

@ -107,6 +107,8 @@ static Time *_time = nullptr;
static core_bind::Geometry2D *_geometry_2d = nullptr;
static core_bind::Geometry3D *_geometry_3d = nullptr;
static WorkerThreadPool *worker_thread_pool = nullptr;
extern Mutex _global_mutex;
static GDExtensionManager *gdextension_manager = nullptr;
@ -231,6 +233,7 @@ void register_core_types() {
GDREGISTER_CLASS(MainLoop);
GDREGISTER_CLASS(Translation);
GDREGISTER_CLASS(TranslationDomain);
GDREGISTER_CLASS(OptimizedTranslation);
GDREGISTER_CLASS(UndoRedo);
GDREGISTER_CLASS(TriangleMesh);
@ -295,6 +298,8 @@ void register_core_types() {
GDREGISTER_NATIVE_STRUCT(AudioFrame, "float left;float right");
GDREGISTER_NATIVE_STRUCT(ScriptLanguageExtensionProfilingInfo, "StringName signature;uint64_t call_count;uint64_t total_time;uint64_t self_time");
worker_thread_pool = memnew(WorkerThreadPool);
OS::get_singleton()->benchmark_end_measure("Core", "Register Types");
}
@ -345,7 +350,7 @@ void register_core_singletons() {
Engine::get_singleton()->add_singleton(Engine::Singleton("Time", Time::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("GDExtensionManager", GDExtensionManager::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceUID", ResourceUID::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("WorkerThreadPool", WorkerThreadPool::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("WorkerThreadPool", worker_thread_pool));
OS::get_singleton()->benchmark_end_measure("Core", "Register Singletons");
}
@ -378,6 +383,8 @@ void unregister_core_types() {
// Destroy singletons in reverse order to ensure dependencies are not broken.
memdelete(worker_thread_pool);
memdelete(_engine_debugger);
memdelete(_marshalls);
memdelete(_classdb);

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View File

@ -0,0 +1,165 @@
/**************************************************************************/
/* translation_domain.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 "translation_domain.h"
#include "core/string/translation.h"
#include "core/string/translation_server.h"
StringName TranslationDomain::get_message_from_translations(const String &p_locale, const StringName &p_message, const StringName &p_context) const {
StringName res;
int best_score = 0;
for (const Ref<Translation> &E : translations) {
ERR_CONTINUE(E.is_null());
int score = TranslationServer::get_singleton()->compare_locales(p_locale, E->get_locale());
if (score > 0 && score >= best_score) {
const StringName r = E->get_message(p_message, p_context);
if (!r) {
continue;
}
res = r;
best_score = score;
if (score == 10) {
break; // Exact match, skip the rest.
}
}
}
return res;
}
StringName TranslationDomain::get_message_from_translations(const String &p_locale, const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
StringName res;
int best_score = 0;
for (const Ref<Translation> &E : translations) {
ERR_CONTINUE(E.is_null());
int score = TranslationServer::get_singleton()->compare_locales(p_locale, E->get_locale());
if (score > 0 && score >= best_score) {
const StringName r = E->get_plural_message(p_message, p_message_plural, p_n, p_context);
if (!r) {
continue;
}
res = r;
best_score = score;
if (score == 10) {
break; // Exact match, skip the rest.
}
}
}
return res;
}
PackedStringArray TranslationDomain::get_loaded_locales() const {
PackedStringArray locales;
for (const Ref<Translation> &E : translations) {
ERR_CONTINUE(E.is_null());
locales.push_back(E->get_locale());
}
return locales;
}
Ref<Translation> TranslationDomain::get_translation_object(const String &p_locale) const {
Ref<Translation> res;
int best_score = 0;
for (const Ref<Translation> &E : translations) {
ERR_CONTINUE(E.is_null());
int score = TranslationServer::get_singleton()->compare_locales(p_locale, E->get_locale());
if (score > 0 && score >= best_score) {
res = E;
best_score = score;
if (score == 10) {
break; // Exact match, skip the rest.
}
}
}
return res;
}
void TranslationDomain::add_translation(const Ref<Translation> &p_translation) {
translations.insert(p_translation);
}
void TranslationDomain::remove_translation(const Ref<Translation> &p_translation) {
translations.erase(p_translation);
}
void TranslationDomain::clear() {
translations.clear();
}
StringName TranslationDomain::translate(const StringName &p_message, const StringName &p_context) const {
const String &locale = TranslationServer::get_singleton()->get_locale();
StringName res = get_message_from_translations(locale, p_message, p_context);
const String &fallback = TranslationServer::get_singleton()->get_fallback_locale();
if (!res && fallback.length() >= 2) {
res = get_message_from_translations(fallback, p_message, p_context);
}
if (!res) {
return p_message;
}
return res;
}
StringName TranslationDomain::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
const String &locale = TranslationServer::get_singleton()->get_locale();
StringName res = get_message_from_translations(locale, p_message, p_message_plural, p_n, p_context);
const String &fallback = TranslationServer::get_singleton()->get_fallback_locale();
if (!res && fallback.length() >= 2) {
res = get_message_from_translations(fallback, p_message, p_message_plural, p_n, p_context);
}
if (!res) {
if (p_n == 1) {
return p_message;
}
return p_message_plural;
}
return res;
}
void TranslationDomain::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationDomain::get_translation_object);
ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationDomain::add_translation);
ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationDomain::remove_translation);
ClassDB::bind_method(D_METHOD("clear"), &TranslationDomain::clear);
ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationDomain::translate, DEFVAL(StringName()));
ClassDB::bind_method(D_METHOD("translate_plural", "message", "message_plural", "n", "context"), &TranslationDomain::translate_plural, DEFVAL(StringName()));
}
TranslationDomain::TranslationDomain() {
}

View File

@ -0,0 +1,65 @@
/**************************************************************************/
/* translation_domain.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 TRANSLATION_DOMAIN_H
#define TRANSLATION_DOMAIN_H
#include "core/object/ref_counted.h"
class Translation;
class TranslationDomain : public RefCounted {
GDCLASS(TranslationDomain, RefCounted);
HashSet<Ref<Translation>> translations;
protected:
static void _bind_methods();
public:
// Methods in this section are not intended for scripting.
StringName get_message_from_translations(const String &p_locale, const StringName &p_message, const StringName &p_context) const;
StringName get_message_from_translations(const String &p_locale, const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const;
PackedStringArray get_loaded_locales() const;
public:
Ref<Translation> get_translation_object(const String &p_locale) const;
void add_translation(const Ref<Translation> &p_translation);
void remove_translation(const Ref<Translation> &p_translation);
void clear();
StringName translate(const StringName &p_message, const StringName &p_context) const;
StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const;
TranslationDomain();
};
#endif // TRANSLATION_DOMAIN_H

View File

@ -404,69 +404,36 @@ String TranslationServer::get_locale() const {
return locale;
}
PackedStringArray TranslationServer::get_loaded_locales() const {
PackedStringArray locales;
for (const Ref<Translation> &E : translations) {
const Ref<Translation> &t = E;
ERR_FAIL_COND_V(t.is_null(), PackedStringArray());
String l = t->get_locale();
locales.push_back(l);
String TranslationServer::get_fallback_locale() const {
return fallback;
}
return locales;
PackedStringArray TranslationServer::get_loaded_locales() const {
return main_domain->get_loaded_locales();
}
void TranslationServer::add_translation(const Ref<Translation> &p_translation) {
translations.insert(p_translation);
main_domain->add_translation(p_translation);
}
void TranslationServer::remove_translation(const Ref<Translation> &p_translation) {
translations.erase(p_translation);
main_domain->remove_translation(p_translation);
}
Ref<Translation> TranslationServer::get_translation_object(const String &p_locale) {
Ref<Translation> res;
int best_score = 0;
for (const Ref<Translation> &E : translations) {
const Ref<Translation> &t = E;
ERR_FAIL_COND_V(t.is_null(), nullptr);
String l = t->get_locale();
int score = compare_locales(p_locale, l);
if (score > 0 && score >= best_score) {
res = t;
best_score = score;
if (score == 10) {
break; // Exact match, skip the rest.
}
}
}
return res;
return main_domain->get_translation_object(p_locale);
}
void TranslationServer::clear() {
translations.clear();
main_domain->clear();
}
StringName TranslationServer::translate(const StringName &p_message, const StringName &p_context) const {
// Match given message against the translation catalog for the project locale.
if (!enabled) {
return p_message;
}
StringName res = _get_message_from_translations(p_message, p_context, locale, false);
if (!res && fallback.length() >= 2) {
res = _get_message_from_translations(p_message, p_context, fallback, false);
}
if (!res) {
return pseudolocalization_enabled ? pseudolocalize(p_message) : p_message;
}
const StringName res = main_domain->translate(p_message, p_context);
return pseudolocalization_enabled ? pseudolocalize(res) : res;
}
@ -478,51 +445,7 @@ StringName TranslationServer::translate_plural(const StringName &p_message, cons
return p_message_plural;
}
StringName res = _get_message_from_translations(p_message, p_context, locale, true, p_message_plural, p_n);
if (!res && fallback.length() >= 2) {
res = _get_message_from_translations(p_message, p_context, fallback, true, p_message_plural, p_n);
}
if (!res) {
if (p_n == 1) {
return p_message;
}
return p_message_plural;
}
return res;
}
StringName TranslationServer::_get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural, int p_n) const {
StringName res;
int best_score = 0;
for (const Ref<Translation> &E : translations) {
const Ref<Translation> &t = E;
ERR_FAIL_COND_V(t.is_null(), p_message);
String l = t->get_locale();
int score = compare_locales(p_locale, l);
if (score > 0 && score >= best_score) {
StringName r;
if (!plural) {
r = t->get_message(p_message, p_context);
} else {
r = t->get_plural_message(p_message, p_message_plural, p_n, p_context);
}
if (!r) {
continue;
}
res = r;
best_score = score;
if (score == 10) {
break; // Exact match, skip the rest.
}
}
}
return res;
return main_domain->translate_plural(p_message, p_message_plural, p_n, p_context);
}
TranslationServer *TranslationServer::singleton = nullptr;
@ -549,6 +472,34 @@ bool TranslationServer::_load_translations(const String &p_from) {
return false;
}
bool TranslationServer::has_domain(const StringName &p_domain) const {
if (p_domain == StringName()) {
return true;
}
return custom_domains.has(p_domain);
}
Ref<TranslationDomain> TranslationServer::get_or_add_domain(const StringName &p_domain) {
if (p_domain == StringName()) {
return main_domain;
}
const Ref<TranslationDomain> *domain = custom_domains.getptr(p_domain);
if (domain) {
if (domain->is_valid()) {
return *domain;
}
ERR_PRINT("Bug (please report): Found invalid translation domain.");
}
Ref<TranslationDomain> new_domain = memnew(TranslationDomain);
custom_domains[p_domain] = new_domain;
return new_domain;
}
void TranslationServer::remove_domain(const StringName &p_domain) {
ERR_FAIL_COND_MSG(p_domain == StringName(), "Cannot remove main translation domain.");
custom_domains.erase(p_domain);
}
void TranslationServer::setup() {
String test = GLOBAL_DEF("internationalization/locale/test", "");
test = test.strip_edges();
@ -574,140 +525,45 @@ void TranslationServer::setup() {
#endif
}
void TranslationServer::set_tool_translation(const Ref<Translation> &p_translation) {
tool_translation = p_translation;
}
Ref<Translation> TranslationServer::get_tool_translation() const {
return tool_translation;
}
String TranslationServer::get_tool_locale() {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) {
if (TranslationServer::get_singleton()->get_tool_translation().is_valid()) {
return tool_translation->get_locale();
} else {
const PackedStringArray &locales = editor_domain->get_loaded_locales();
if (locales.is_empty()) {
return "en";
}
return locales[0];
} else {
#else
{
#endif
// Look for best matching loaded translation.
String best_locale = "en";
int best_score = 0;
for (const Ref<Translation> &E : translations) {
const Ref<Translation> &t = E;
ERR_FAIL_COND_V(t.is_null(), best_locale);
String l = t->get_locale();
int score = compare_locales(locale, l);
if (score > 0 && score >= best_score) {
best_locale = l;
best_score = score;
if (score == 10) {
break; // Exact match, skip the rest.
Ref<Translation> t = main_domain->get_translation_object(locale);
if (t.is_null()) {
return "en";
}
}
}
return best_locale;
return t->get_locale();
}
}
StringName TranslationServer::tool_translate(const StringName &p_message, const StringName &p_context) const {
if (tool_translation.is_valid()) {
StringName r = tool_translation->get_message(p_message, p_context);
if (r) {
return r;
}
}
return p_message;
return editor_domain->translate(p_message, p_context);
}
StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
if (tool_translation.is_valid()) {
StringName r = tool_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
if (r) {
return r;
}
}
if (p_n == 1) {
return p_message;
}
return p_message_plural;
}
void TranslationServer::set_property_translation(const Ref<Translation> &p_translation) {
property_translation = p_translation;
return editor_domain->translate_plural(p_message, p_message_plural, p_n, p_context);
}
StringName TranslationServer::property_translate(const StringName &p_message, const StringName &p_context) const {
if (property_translation.is_valid()) {
StringName r = property_translation->get_message(p_message, p_context);
if (r) {
return r;
}
}
return p_message;
}
void TranslationServer::set_doc_translation(const Ref<Translation> &p_translation) {
doc_translation = p_translation;
return property_domain->translate(p_message, p_context);
}
StringName TranslationServer::doc_translate(const StringName &p_message, const StringName &p_context) const {
if (doc_translation.is_valid()) {
StringName r = doc_translation->get_message(p_message, p_context);
if (r) {
return r;
}
}
return p_message;
return doc_domain->translate(p_message, p_context);
}
StringName TranslationServer::doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
if (doc_translation.is_valid()) {
StringName r = doc_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
if (r) {
return r;
}
}
if (p_n == 1) {
return p_message;
}
return p_message_plural;
}
void TranslationServer::set_extractable_translation(const Ref<Translation> &p_translation) {
extractable_translation = p_translation;
}
StringName TranslationServer::extractable_translate(const StringName &p_message, const StringName &p_context) const {
if (extractable_translation.is_valid()) {
StringName r = extractable_translation->get_message(p_message, p_context);
if (r) {
return r;
}
}
return p_message;
}
StringName TranslationServer::extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
if (extractable_translation.is_valid()) {
StringName r = extractable_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
if (r) {
return r;
}
}
if (p_n == 1) {
return p_message;
}
return p_message_plural;
return doc_domain->translate_plural(p_message, p_message_plural, p_n, p_context);
}
bool TranslationServer::is_pseudolocalization_enabled() const {
@ -925,6 +781,10 @@ void TranslationServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation);
ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationServer::get_translation_object);
ClassDB::bind_method(D_METHOD("has_domain", "domain"), &TranslationServer::has_domain);
ClassDB::bind_method(D_METHOD("get_or_add_domain", "domain"), &TranslationServer::get_or_add_domain);
ClassDB::bind_method(D_METHOD("remove_domain", "domain"), &TranslationServer::remove_domain);
ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear);
ClassDB::bind_method(D_METHOD("get_loaded_locales"), &TranslationServer::get_loaded_locales);
@ -947,5 +807,9 @@ void TranslationServer::load_translations() {
TranslationServer::TranslationServer() {
singleton = this;
main_domain.instantiate();
editor_domain = get_or_add_domain("godot.editor");
property_domain = get_or_add_domain("godot.properties");
doc_domain = get_or_add_domain("godot.documentation");
init_locale_info();
}

View File

@ -32,6 +32,7 @@
#define TRANSLATION_SERVER_H
#include "core/string/translation.h"
#include "core/string/translation_domain.h"
class TranslationServer : public Object {
GDCLASS(TranslationServer, Object);
@ -39,11 +40,11 @@ class TranslationServer : public Object {
String locale = "en";
String fallback;
HashSet<Ref<Translation>> translations;
Ref<Translation> tool_translation;
Ref<Translation> property_translation;
Ref<Translation> doc_translation;
Ref<Translation> extractable_translation;
Ref<TranslationDomain> main_domain;
Ref<TranslationDomain> editor_domain;
Ref<TranslationDomain> property_domain;
Ref<TranslationDomain> doc_domain;
HashMap<StringName, Ref<TranslationDomain>> custom_domains;
bool enabled = true;
@ -70,8 +71,6 @@ class TranslationServer : public Object {
bool _load_translations(const String &p_from);
String _standardize_locale(const String &p_locale, bool p_add_defaults) const;
StringName _get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural = "", int p_n = 0) const;
static void _bind_methods();
struct LocaleScriptInfo {
@ -99,6 +98,7 @@ public:
void set_locale(const String &p_locale);
String get_locale() const;
String get_fallback_locale() const;
Ref<Translation> get_translation_object(const String &p_locale);
Vector<String> get_all_languages() const;
@ -131,18 +131,15 @@ public:
int compare_locales(const String &p_locale_a, const String &p_locale_b) const;
String get_tool_locale();
void set_tool_translation(const Ref<Translation> &p_translation);
Ref<Translation> get_tool_translation() const;
StringName tool_translate(const StringName &p_message, const StringName &p_context = "") const;
StringName tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
void set_property_translation(const Ref<Translation> &p_translation);
StringName property_translate(const StringName &p_message, const StringName &p_context = "") const;
void set_doc_translation(const Ref<Translation> &p_translation);
StringName doc_translate(const StringName &p_message, const StringName &p_context = "") const;
StringName doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
void set_extractable_translation(const Ref<Translation> &p_translation);
StringName extractable_translate(const StringName &p_message, const StringName &p_context = "") const;
StringName extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
bool has_domain(const StringName &p_domain) const;
Ref<TranslationDomain> get_or_add_domain(const StringName &p_domain);
void remove_domain(const StringName &p_domain);
void setup();

View File

@ -4626,7 +4626,7 @@ bool String::is_absolute_path() const {
String String::validate_ascii_identifier() const {
if (is_empty()) {
return "_"; // Empty string is not a valid identifier;
return "_"; // Empty string is not a valid identifier.
}
String result;
@ -4647,6 +4647,29 @@ String String::validate_ascii_identifier() const {
return result;
}
String String::validate_unicode_identifier() const {
if (is_empty()) {
return "_"; // Empty string is not a valid identifier.
}
String result;
if (is_unicode_identifier_start(operator[](0))) {
result = *this;
} else {
result = "_" + *this;
}
int len = result.length();
char32_t *buffer = result.ptrw();
for (int i = 0; i < len; i++) {
if (!is_unicode_identifier_continue(buffer[i])) {
buffer[i] = '_';
}
}
return result;
}
bool String::is_valid_ascii_identifier() const {
int len = length();

View File

@ -460,6 +460,7 @@ public:
static String get_invalid_node_name_characters(bool p_allow_internal = false);
String validate_node_name() const;
String validate_ascii_identifier() const;
String validate_unicode_identifier() const;
String validate_filename() const;
bool is_valid_ascii_identifier() const;

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View File

@ -369,6 +369,34 @@ int Array::find(const Variant &p_value, int p_from) const {
return ret;
}
int Array::find_custom(const Callable &p_callable, int p_from) const {
int ret = -1;
if (p_from < 0 || size() == 0) {
return ret;
}
const Variant *argptrs[1];
for (int i = p_from; i < size(); i++) {
const Variant &val = _p->array[i];
argptrs[0] = &val;
Variant res;
Callable::CallError ce;
p_callable.callp(argptrs, 1, res, ce);
if (unlikely(ce.error != Callable::CallError::CALL_OK)) {
ERR_FAIL_V_MSG(ret, "Error calling method from 'find_custom': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce));
}
ERR_FAIL_COND_V_MSG(res.get_type() != Variant::Type::BOOL, ret, "Error on method from 'find_custom': Return type of callable must be boolean.");
if (res.operator bool()) {
return i;
}
}
return ret;
}
int Array::rfind(const Variant &p_value, int p_from) const {
if (_p->array.size() == 0) {
return -1;
@ -394,6 +422,41 @@ int Array::rfind(const Variant &p_value, int p_from) const {
return -1;
}
int Array::rfind_custom(const Callable &p_callable, int p_from) const {
if (_p->array.size() == 0) {
return -1;
}
if (p_from < 0) {
// Relative offset from the end.
p_from = _p->array.size() + p_from;
}
if (p_from < 0 || p_from >= _p->array.size()) {
// Limit to array boundaries.
p_from = _p->array.size() - 1;
}
const Variant *argptrs[1];
for (int i = p_from; i >= 0; i--) {
const Variant &val = _p->array[i];
argptrs[0] = &val;
Variant res;
Callable::CallError ce;
p_callable.callp(argptrs, 1, res, ce);
if (unlikely(ce.error != Callable::CallError::CALL_OK)) {
ERR_FAIL_V_MSG(-1, "Error calling method from 'rfind_custom': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce));
}
ERR_FAIL_COND_V_MSG(res.get_type() != Variant::Type::BOOL, -1, "Error on method from 'rfind_custom': Return type of callable must be boolean.");
if (res.operator bool()) {
return i;
}
}
return -1;
}
int Array::count(const Variant &p_value) const {
Variant value = p_value;
ERR_FAIL_COND_V(!_p->typed.validate(value, "count"), 0);
@ -761,7 +824,7 @@ Variant Array::max() const {
return Variant(); //not a valid comparison
}
if (bool(ret)) {
//is less
//is greater
maxval = test;
}
}

View File

@ -152,7 +152,9 @@ public:
void reverse();
int find(const Variant &p_value, int p_from = 0) const;
int find_custom(const Callable &p_callable, int p_from = 0) const;
int rfind(const Variant &p_value, int p_from = -1) const;
int rfind_custom(const Callable &p_callable, int p_from = -1) const;
int count(const Variant &p_value) const;
bool has(const Variant &p_value) const;

View File

@ -466,7 +466,7 @@ void call_with_variant_argsc(T *p_instance, void (T::*p_method)(P...) const, con
return;
}
#endif
call_with_variant_args_helper<T, P...>(p_instance, p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{});
call_with_variant_argsc_helper<T, P...>(p_instance, p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{});
}
template <typename T, typename... P>
@ -830,7 +830,7 @@ void call_with_variant_args_static_ret(R (*p_method)(P...), const Variant **p_ar
}
template <typename... P>
void call_with_variant_args_static_ret(void (*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) {
void call_with_variant_args_static(void (*p_method)(P...), const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
#ifdef DEBUG_METHODS_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;

View File

@ -545,6 +545,13 @@ bool Signal::is_connected(const Callable &p_callable) const {
return obj->is_connected(name, p_callable);
}
bool Signal::has_connections() const {
Object *obj = get_object();
ERR_FAIL_NULL_V(obj, false);
return obj->has_connections(name);
}
Array Signal::get_connections() const {
Object *obj = get_object();
if (!obj) {

View File

@ -192,6 +192,7 @@ public:
Error connect(const Callable &p_callable, uint32_t p_flags = 0);
void disconnect(const Callable &p_callable);
bool is_connected(const Callable &p_callable) const;
bool has_connections() const;
Array get_connections() const;
Signal(const Object *p_object, const StringName &p_name);

View File

@ -83,35 +83,64 @@ Variant Dictionary::get_value_at_index(int p_index) const {
return Variant();
}
// WARNING: This operator does not validate the value type. For scripting/extensions this is
// done in `variant_setget.cpp`. Consider using `set()` if the data might be invalid.
Variant &Dictionary::operator[](const Variant &p_key) {
if (unlikely(_p->read_only)) {
if (likely(_p->variant_map.has(p_key))) {
*_p->read_only = _p->variant_map[p_key];
} else {
*_p->read_only = Variant();
Variant key = p_key;
if (unlikely(!_p->typed_key.validate(key, "use `operator[]`"))) {
if (unlikely(!_p->typed_fallback)) {
_p->typed_fallback = memnew(Variant);
}
VariantInternal::initialize(_p->typed_fallback, _p->typed_value.type);
return *_p->typed_fallback;
} else if (unlikely(_p->read_only)) {
if (likely(_p->variant_map.has(key))) {
*_p->read_only = _p->variant_map[key];
} else {
VariantInternal::initialize(_p->read_only, _p->typed_value.type);
}
return *_p->read_only;
} else {
return _p->variant_map[p_key];
if (unlikely(!_p->variant_map.has(key))) {
VariantInternal::initialize(&_p->variant_map[key], _p->typed_value.type);
}
return _p->variant_map[key];
}
}
const Variant &Dictionary::operator[](const Variant &p_key) const {
// Will not insert key, so no conversion is necessary.
return _p->variant_map[p_key];
Variant key = p_key;
if (unlikely(!_p->typed_key.validate(key, "use `operator[]`"))) {
if (unlikely(!_p->typed_fallback)) {
_p->typed_fallback = memnew(Variant);
}
VariantInternal::initialize(_p->typed_fallback, _p->typed_value.type);
return *_p->typed_fallback;
} else {
// Will not insert key, so no initialization is necessary.
return _p->variant_map[key];
}
}
const Variant *Dictionary::getptr(const Variant &p_key) const {
HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::ConstIterator E(_p->variant_map.find(p_key));
Variant key = p_key;
if (unlikely(!_p->typed_key.validate(key, "getptr"))) {
return nullptr;
}
HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::ConstIterator E(_p->variant_map.find(key));
if (!E) {
return nullptr;
}
return &E->value;
}
// WARNING: This method does not validate the value type.
Variant *Dictionary::getptr(const Variant &p_key) {
HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::Iterator E(_p->variant_map.find(p_key));
Variant key = p_key;
if (unlikely(!_p->typed_key.validate(key, "getptr"))) {
return nullptr;
}
HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::Iterator E(_p->variant_map.find(key));
if (!E) {
return nullptr;
}
@ -158,6 +187,16 @@ Variant Dictionary::get_or_add(const Variant &p_key, const Variant &p_default) {
return *result;
}
bool Dictionary::set(const Variant &p_key, const Variant &p_value) {
ERR_FAIL_COND_V_MSG(_p->read_only, false, "Dictionary is in read-only state.");
Variant key = p_key;
ERR_FAIL_COND_V(!_p->typed_key.validate(key, "set"), false);
Variant value = p_value;
ERR_FAIL_COND_V(!_p->typed_value.validate(value, "set"), false);
_p->variant_map[key] = value;
return true;
}
int Dictionary::size() const {
return _p->variant_map.size();
}

View File

@ -59,6 +59,7 @@ public:
Variant get_valid(const Variant &p_key) const;
Variant get(const Variant &p_key, const Variant &p_default) const;
Variant get_or_add(const Variant &p_key, const Variant &p_default);
bool set(const Variant &p_key, const Variant &p_value);
int size() const;
bool is_empty() const;

View File

@ -657,7 +657,23 @@ static _FORCE_INLINE_ void vc_ptrcall(void (*method)(T *, P...), void *p_base, c
} \
};
#define VARCALL_PACKED_GETTER(m_packed_type, m_return_type) \
static m_return_type func_##m_packed_type##_get(m_packed_type *p_instance, int64_t p_index) { \
return p_instance->get(p_index); \
}
struct _VariantCall {
VARCALL_PACKED_GETTER(PackedByteArray, uint8_t)
VARCALL_PACKED_GETTER(PackedColorArray, Color)
VARCALL_PACKED_GETTER(PackedFloat32Array, float)
VARCALL_PACKED_GETTER(PackedFloat64Array, double)
VARCALL_PACKED_GETTER(PackedInt32Array, int32_t)
VARCALL_PACKED_GETTER(PackedInt64Array, int64_t)
VARCALL_PACKED_GETTER(PackedStringArray, String)
VARCALL_PACKED_GETTER(PackedVector2Array, Vector2)
VARCALL_PACKED_GETTER(PackedVector3Array, Vector3)
VARCALL_PACKED_GETTER(PackedVector4Array, Vector4)
static String func_PackedByteArray_get_string_from_ascii(PackedByteArray *p_instance) {
String s;
if (p_instance->size() > 0) {
@ -2121,6 +2137,7 @@ static void _register_variant_builtin_methods_misc() {
bind_method(Signal, disconnect, sarray("callable"), varray());
bind_method(Signal, is_connected, sarray("callable"), varray());
bind_method(Signal, get_connections, sarray(), varray());
bind_method(Signal, has_connections, sarray(), varray());
bind_custom(Signal, emit, _VariantCall::func_Signal_emit, false, Variant);
@ -2267,6 +2284,7 @@ static void _register_variant_builtin_methods_misc() {
bind_method(Dictionary, duplicate, sarray("deep"), varray(false));
bind_method(Dictionary, get, sarray("key", "default"), varray(Variant()));
bind_method(Dictionary, get_or_add, sarray("key", "default"), varray(Variant()));
bind_method(Dictionary, set, sarray("key", "value"), varray());
bind_method(Dictionary, is_typed, sarray(), varray());
bind_method(Dictionary, is_typed_key, sarray(), varray());
bind_method(Dictionary, is_typed_value, sarray(), varray());
@ -2292,6 +2310,8 @@ static void _register_variant_builtin_methods_array() {
bind_method(Array, clear, sarray(), varray());
bind_method(Array, hash, sarray(), varray());
bind_method(Array, assign, sarray("array"), varray());
bind_method(Array, get, sarray("index"), varray());
bind_method(Array, set, sarray("index", "value"), varray());
bind_method(Array, push_back, sarray("value"), varray());
bind_method(Array, push_front, sarray("value"), varray());
bind_method(Array, append, sarray("value"), varray());
@ -2305,7 +2325,9 @@ static void _register_variant_builtin_methods_array() {
bind_method(Array, back, sarray(), varray());
bind_method(Array, pick_random, sarray(), varray());
bind_method(Array, find, sarray("what", "from"), varray(0));
bind_method(Array, find_custom, sarray("method", "from"), varray(0));
bind_method(Array, rfind, sarray("what", "from"), varray(-1));
bind_method(Array, rfind_custom, sarray("method", "from"), varray(-1));
bind_method(Array, count, sarray("value"), varray());
bind_method(Array, has, sarray("value"), varray());
bind_method(Array, pop_back, sarray(), varray());
@ -2334,6 +2356,18 @@ static void _register_variant_builtin_methods_array() {
bind_method(Array, make_read_only, sarray(), varray());
bind_method(Array, is_read_only, sarray(), varray());
/* Packed*Array get (see VARCALL_PACKED_GETTER macro) */
bind_function(PackedByteArray, get, _VariantCall::func_PackedByteArray_get, sarray("index"), varray());
bind_function(PackedColorArray, get, _VariantCall::func_PackedColorArray_get, sarray("index"), varray());
bind_function(PackedFloat32Array, get, _VariantCall::func_PackedFloat32Array_get, sarray("index"), varray());
bind_function(PackedFloat64Array, get, _VariantCall::func_PackedFloat64Array_get, sarray("index"), varray());
bind_function(PackedInt32Array, get, _VariantCall::func_PackedInt32Array_get, sarray("index"), varray());
bind_function(PackedInt64Array, get, _VariantCall::func_PackedInt64Array_get, sarray("index"), varray());
bind_function(PackedStringArray, get, _VariantCall::func_PackedStringArray_get, sarray("index"), varray());
bind_function(PackedVector2Array, get, _VariantCall::func_PackedVector2Array_get, sarray("index"), varray());
bind_function(PackedVector3Array, get, _VariantCall::func_PackedVector3Array_get, sarray("index"), varray());
bind_function(PackedVector4Array, get, _VariantCall::func_PackedVector4Array_get, sarray("index"), varray());
/* Byte Array */
bind_method(PackedByteArray, size, sarray(), varray());
bind_method(PackedByteArray, is_empty, sarray(), varray());

View File

@ -923,7 +923,10 @@ public:
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
bool valid = true;
String result = do_mod(*VariantGetInternalPtr<S>::get_ptr(left), &valid);
ERR_FAIL_COND_MSG(!valid, result);
if (unlikely(!valid)) {
*VariantGetInternalPtr<String>::get_ptr(r_ret) = *VariantGetInternalPtr<S>::get_ptr(left);
ERR_FAIL_MSG(vformat("String formatting error: %s.", result));
}
*VariantGetInternalPtr<String>::get_ptr(r_ret) = result;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@ -948,7 +951,10 @@ public:
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
bool valid = true;
String result = do_mod(*VariantGetInternalPtr<S>::get_ptr(left), *VariantGetInternalPtr<Array>::get_ptr(right), &valid);
ERR_FAIL_COND_MSG(!valid, result);
if (unlikely(!valid)) {
*VariantGetInternalPtr<String>::get_ptr(r_ret) = *VariantGetInternalPtr<S>::get_ptr(left);
ERR_FAIL_MSG(vformat("String formatting error: %s.", result));
}
*VariantGetInternalPtr<String>::get_ptr(r_ret) = result;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@ -976,7 +982,10 @@ public:
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
bool valid = true;
String result = do_mod(*VariantGetInternalPtr<S>::get_ptr(left), right->get_validated_object(), &valid);
ERR_FAIL_COND_MSG(!valid, result);
if (unlikely(!valid)) {
*VariantGetInternalPtr<String>::get_ptr(r_ret) = *VariantGetInternalPtr<S>::get_ptr(left);
ERR_FAIL_MSG(vformat("String formatting error: %s.", result));
}
*VariantGetInternalPtr<String>::get_ptr(r_ret) = result;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@ -1003,7 +1012,10 @@ public:
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
bool valid = true;
String result = do_mod(*VariantGetInternalPtr<S>::get_ptr(left), *VariantGetInternalPtr<T>::get_ptr(right), &valid);
ERR_FAIL_COND_MSG(!valid, result);
if (unlikely(!valid)) {
*VariantGetInternalPtr<String>::get_ptr(r_ret) = *VariantGetInternalPtr<S>::get_ptr(left);
ERR_FAIL_MSG(vformat("String formatting error: %s.", result));
}
*VariantGetInternalPtr<String>::get_ptr(r_ret) = result;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@ -1492,7 +1504,10 @@ public:
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
Object *l = right->get_validated_object();
ERR_FAIL_NULL(l);
if (unlikely(!l)) {
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = false;
ERR_FAIL_MSG("Invalid base object for 'in'.");
}
const String &a = *VariantGetInternalPtr<String>::get_ptr(left);
bool valid;
@ -1526,7 +1541,10 @@ public:
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
Object *l = right->get_validated_object();
ERR_FAIL_NULL(l);
if (unlikely(!l)) {
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = false;
ERR_FAIL_MSG("Invalid base object for 'in'.");
}
const StringName &a = *VariantGetInternalPtr<StringName>::get_ptr(left);
bool valid;

View File

@ -252,20 +252,7 @@ void Variant::set_named(const StringName &p_member, const Variant &p_value, bool
}
} else if (type == Variant::DICTIONARY) {
Dictionary &dict = *VariantGetInternalPtr<Dictionary>::get_ptr(this);
if (dict.is_read_only()) {
r_valid = false;
return;
}
Variant *v = dict.getptr(p_member);
if (v) {
*v = p_value;
} else {
dict[p_member] = p_value;
}
r_valid = true;
r_valid = dict.set(p_member, p_value);
} else {
r_valid = false;
}
@ -721,26 +708,16 @@ struct VariantIndexedSetGet_Dictionary {
PtrToArg<Variant>::encode(*ptr, member);
}
static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) {
if (VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only()) {
*valid = false;
*oob = true;
return;
}
(*VariantGetInternalPtr<Dictionary>::get_ptr(base))[index] = *value;
*oob = false;
*valid = true;
*valid = VariantGetInternalPtr<Dictionary>::get_ptr(base)->set(index, *value);
*oob = VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only();
}
static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) {
if (VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only()) {
*oob = true;
return;
}
(*VariantGetInternalPtr<Dictionary>::get_ptr(base))[index] = *value;
*oob = false;
VariantGetInternalPtr<Dictionary>::get_ptr(base)->set(index, *value);
*oob = VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only();
}
static void ptr_set(void *base, int64_t index, const void *member) {
Dictionary &v = *reinterpret_cast<Dictionary *>(base);
v[index] = PtrToArg<Variant>::convert(member);
v.set(index, PtrToArg<Variant>::convert(member));
}
static Variant::Type get_index_type() { return Variant::NIL; }
static uint32_t get_index_usage() { return PROPERTY_USAGE_DEFAULT; }
@ -1010,16 +987,11 @@ struct VariantKeyedSetGetDictionary {
PtrToArg<Variant>::encode(*ptr, value);
}
static void set(Variant *base, const Variant *key, const Variant *value, bool *r_valid) {
if (VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only()) {
*r_valid = false;
return;
}
(*VariantGetInternalPtr<Dictionary>::get_ptr(base))[*key] = *value;
*r_valid = true;
*r_valid = VariantGetInternalPtr<Dictionary>::get_ptr(base)->set(*key, *value);
}
static void ptr_set(void *base, const void *key, const void *value) {
Dictionary &v = *reinterpret_cast<Dictionary *>(base);
v[PtrToArg<Variant>::convert(key)] = PtrToArg<Variant>::convert(value);
v.set(PtrToArg<Variant>::convert(key), PtrToArg<Variant>::convert(value));
}
static bool has(const Variant *base, const Variant *key, bool *r_valid) {

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