Compare commits

...
Sign in to create a new pull request.

6 commits

Author SHA1 Message Date
f4cf6b3999 feat: updated engine 2026-07-10 17:04:34 +02:00
cbe99774ff chore: binary files and LFS bullshit 2026-07-10 17:03:49 +02:00
af0131f72a fix: initialize template now removes remote 2026-07-10 16:34:05 +02:00
186bf304de feat: added more macros 2026-07-10 16:29:35 +02:00
375ef84c5f revert ef1b4cf034
revert feat: INITIALISED TEMPLATE
2026-06-03 13:23:35 +00:00
ef1b4cf034 feat: INITIALISED TEMPLATE 2026-06-03 15:20:18 +02:00
6626 changed files with 910183 additions and 430025 deletions

View file

@ -1,3 +1,6 @@
# If you change this file, please format all files of the codebase as part of your PR:
# prek run clang-format --all
# Commented out parameters are those with the same value as base LLVM style. # 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 # 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 18.1.8). # chosen value in case the base style changes (last sync: Clang 18.1.8).
@ -38,7 +41,7 @@ AlignAfterOpenBracket: DontAlign
# AcrossEmptyLines: false # AcrossEmptyLines: false
# AcrossComments: false # AcrossComments: false
# AlignCaseColons: false # AlignCaseColons: false
# AlignEscapedNewlines: Right AlignEscapedNewlines: DontAlign # Aligning leads to long diffs
AlignOperands: DontAlign AlignOperands: DontAlign
AlignTrailingComments: AlignTrailingComments:
Kind: Never Kind: Never
@ -114,14 +117,25 @@ Cpp11BracedListStyle: false
# - BOOST_FOREACH # - BOOST_FOREACH
# IfMacros: # IfMacros:
# - KJ_IF_MAYBE # - KJ_IF_MAYBE
# IncludeBlocks: Preserve IncludeBlocks: Regroup
IncludeCategories: IncludeCategories:
- Regex: ^".*"$ - Regex: ^"(core|drivers|editor|main|scene|servers|tests)/.*"$
Priority: 1 Priority: 20
- Regex: ^<.*\.h>$ - Regex: ^"(modules|platform)/.*"$
Priority: 2 Priority: 30
- Regex: ^<thirdparty/.*>$
Priority: 40
- Regex: ^<(windows|Jolt/Jolt|platform_gl)\.h>$
Priority: 50
- Regex: ^<.*\.(h|hpp)>$
Priority: 60
- Regex: ^<.*>$ - Regex: ^<.*>$
Priority: 3 Priority: 70
- Regex: ^".*\.compat\.inc"$
Priority: 0
SortPriority: 1
- Regex: ^".*"$
Priority: 10
# IncludeIsMainRegex: (Test)?$ # IncludeIsMainRegex: (Test)?$
# IncludeIsMainSourceRegex: "" # IncludeIsMainSourceRegex: ""
# IndentAccessModifiers: false # IndentAccessModifiers: false

View file

@ -1,21 +1,26 @@
# If you change this file, please format all files of the codebase as part of your PR:
# prek run --hook-stage manual clang-tidy --all
Checks: Checks:
- -* - -*
- cppcoreguidelines-pro-type-member-init - performance-move-const-arg
- bugprone-use-after-move
- modernize-deprecated-headers - modernize-deprecated-headers
- modernize-redundant-void-arg - modernize-redundant-void-arg
- modernize-use-bool-literals - modernize-use-bool-literals
- modernize-use-default-member-init # - modernize-use-default-member-init # TODO Re-activate
- modernize-use-nullptr - modernize-use-nullptr
- readability-braces-around-statements - readability-braces-around-statements
- readability-redundant-member-init - readability-redundant-member-init
- readability-operators-representation
HeaderFileExtensions: ["", h, hh, hpp, hxx, inc, glsl] HeaderFileExtensions: ["", h, hh, hpp, hxx, inc, glsl]
ImplementationFileExtensions: [c, cc, cpp, cxx, m, mm, java] ImplementationFileExtensions: [c, cc, cpp, cxx, m, mm, java]
HeaderFilterRegex: (core|doc|drivers|editor|main|modules|platform|scene|servers|tests)/ HeaderFilterRegex: (core|doc|drivers|editor|main|modules|platform|scene|servers|tests)/
FormatStyle: file FormatStyle: file
CheckOptions: CheckOptions:
cppcoreguidelines-pro-type-member-init.IgnoreArrays: true
cppcoreguidelines-pro-type-member-init.UseAssignment: true
modernize-deprecated-headers.CheckHeaderFile: true modernize-deprecated-headers.CheckHeaderFile: true
modernize-use-bool-literals.IgnoreMacros: false modernize-use-bool-literals.IgnoreMacros: false
modernize-use-default-member-init.IgnoreMacros: false modernize-use-default-member-init.IgnoreMacros: false
modernize-use-default-member-init.UseAssignment: true modernize-use-default-member-init.UseAssignment: true
readability-operators-representation.BinaryOperators: "&&;&=;&;|;~;!;!=;||;|=;^;^="
readability-operators-representation.OverloadedOperators: "&&;&=;&;|;~;!;!=;||;|=;^;^="

View file

@ -1,12 +1,21 @@
# https://clangd.llvm.org/config # https://clangd.llvm.org/config
--- ---
# Default conditions, apply everywhere. # Default conditions, apply everywhere.
CompileFlags:
Add:
# Some thirdparty includes seem to require a high -ferror-limit to pass checks.
- -ferror-limit=100
Diagnostics: Diagnostics:
Includes: Includes:
IgnoreHeader: IgnoreHeader:
- \.compat\.inc - \.compat\.inc
--- ---
# Header-specific conditions. # Header-specific conditions.
If: If:
@ -20,11 +29,19 @@ CompileFlags:
- -Wno-unused-const-variable - -Wno-unused-const-variable
- -Wno-unused-function - -Wno-unused-function
- -Wno-unused-variable - -Wno-unused-variable
--- ---
# Suppress all third-party warnings.
# Suppress warnings for third-party or partial code.
If: If:
PathMatch: thirdparty/.* PathMatch:
- bin/build_deps/.*
- misc/dist/apple_embedded_xcode/.*
- tests/compatibility_test/.*
- thirdparty/.*
- .*/thirdparty/.*
- .*-so_wrap.[ch]
Diagnostics: Diagnostics:
Suppress: "*" Suppress: "*"

View file

@ -72,3 +72,18 @@ e06d83860d798b6766b23d6eae48557387a7db85
# Style: Replace header guards with `#pragma once` # Style: Replace header guards with `#pragma once`
324512e11c1b7663c3cf47bec6ddbe65c6b8db2b 324512e11c1b7663c3cf47bec6ddbe65c6b8db2b
# Style: Don't right-align escaped newlines
c5df0cb82bc539eff7dcfb2add99d60771fc50c5
# Style: Convert `*.gen.inc` to `*.gen.h`
7dae5da1982f4a55ba91557814905faef9ce461b
# Move RenderingServer enums to a dedicated RenderingServerEnums (`RSE`) namespace
f5a290ac462765afca34e64dd39f883511510147
# Style: Add `class_db.h` includes explicitly
e380a417526c11f15a9ddb3997292409b10da2af
# Move DisplayServer enums and typedefs to DisplayServerEnums
a447ac95ec170ee117c2eae55f1bfff0d0cf0dce

View file

@ -40,7 +40,8 @@
/drivers/vulkan/ @godotengine/rendering /drivers/vulkan/ @godotengine/rendering
## OS ## OS
/drivers/apple*/ @godotengine/macos /drivers/apple/ @godotengine/macos @godotengine/ios
/drivers/apple_embedded/ @godotengine/ios
/drivers/unix/ @godotengine/linux-bsd /drivers/unix/ @godotengine/linux-bsd
/drivers/windows/ @godotengine/windows /drivers/windows/ @godotengine/windows
@ -113,7 +114,6 @@
/modules/hdr/ @godotengine/asset-pipeline /modules/hdr/ @godotengine/asset-pipeline
/modules/jpg/ @godotengine/asset-pipeline /modules/jpg/ @godotengine/asset-pipeline
/modules/ktx/ @godotengine/asset-pipeline /modules/ktx/ @godotengine/asset-pipeline
/modules/squish/ @godotengine/asset-pipeline
/modules/svg/ @godotengine/asset-pipeline /modules/svg/ @godotengine/asset-pipeline
/modules/tga/ @godotengine/asset-pipeline /modules/tga/ @godotengine/asset-pipeline
/modules/tinyexr/ @godotengine/asset-pipeline /modules/tinyexr/ @godotengine/asset-pipeline
@ -147,6 +147,10 @@
/modules/meshoptimizer/ @godotengine/rendering /modules/meshoptimizer/ @godotengine/rendering
/modules/raycast/ @godotengine/rendering /modules/raycast/ @godotengine/rendering
/modules/vhacd/ @godotengine/rendering /modules/vhacd/ @godotengine/rendering
/modules/visual_shader/ @godotengine/shaders
/modules/visual_shader/doc_classes/ @godotengine/shaders @godotengine/documentation
/modules/visual_shader/editor/ @godotengine/shaders @godotengine/editor
/modules/visual_shader/tests/ @godotengine/shaders @godotengine/tests
/modules/xatlas_unwrap/ @godotengine/rendering /modules/xatlas_unwrap/ @godotengine/rendering
## Scripting ## Scripting
@ -158,7 +162,7 @@
/modules/jsonrpc/tests/ @godotengine/gdscript @godotengine/network @godotengine/tests /modules/jsonrpc/tests/ @godotengine/gdscript @godotengine/network @godotengine/tests
/modules/mono/ @godotengine/dotnet /modules/mono/ @godotengine/dotnet
/modules/mono/doc_classes/ @godotengine/dotnet @godotengine/documentation /modules/mono/doc_classes/ @godotengine/dotnet @godotengine/documentation
/modules/mono/editor/ @godotengine/dotnet @godotengine/script-editor /modules/mono/editor/ @godotengine/dotnet @godotengine/editor
## Text ## Text
/modules/freetype/ @godotengine/buildsystem /modules/freetype/ @godotengine/buildsystem
@ -187,6 +191,7 @@
/modules/gridmap/ @godotengine/3d-nodes /modules/gridmap/ @godotengine/3d-nodes
/modules/gridmap/doc_classes/ @godotengine/3d-nodes @godotengine/documentation /modules/gridmap/doc_classes/ @godotengine/3d-nodes @godotengine/documentation
/modules/gridmap/editor/ @godotengine/3d-nodes @godotengine/editor /modules/gridmap/editor/ @godotengine/3d-nodes @godotengine/editor
/modules/gridmap/tests/ @godotengine/3d-nodes @godotengine/tests
/modules/navigation_2d/ @godotengine/navigation /modules/navigation_2d/ @godotengine/navigation
/modules/navigation_2d/editor/ @godotengine/navigation @godotengine/editor /modules/navigation_2d/editor/ @godotengine/navigation @godotengine/editor
/modules/navigation_3d/ @godotengine/navigation /modules/navigation_3d/ @godotengine/navigation
@ -252,7 +257,6 @@
/scene/resources/shader* @godotengine/shaders /scene/resources/shader* @godotengine/shaders
/scene/resources/skeleton* @godotengine/animation /scene/resources/skeleton* @godotengine/animation
/scene/resources/text_* @godotengine/gui-nodes /scene/resources/text_* @godotengine/gui-nodes
/scene/resources/visual_shader* @godotengine/shaders
/scene/theme/ @godotengine/gui-nodes /scene/theme/ @godotengine/gui-nodes
# Servers # Servers

View file

@ -1,8 +1,18 @@
<!-- <!--
Please target the `master` branch in priority. Please target the `master` branch. We will take care of backporting relevant fixes to older versions.
Relevant fixes are cherry-picked for stable branches as needed by maintainers. Before submitting, please read our checklist for contributors:
https://contributing.godotengine.org/en/latest/engine/introduction.html#checklist-for-new-contributors
To speed up the contribution process and avoid CI errors, please set up pre-commit hooks locally: Use of AI must be disclosed and should include a description of how it was used.
https://contributing.godotengine.org/en/latest/engine/guidelines/code_style.html -->
## What problem(s) does this PR solve?
- Closes #
## Additional information
<!--
Provide additional information and explanation of your PR, including areas that you are uncertain of or require special attention from reviewers. For examples, please read our pull request guidelines: https://contributing.godotengine.org/en/latest/pull_requests/pull_request_guidelines.html#explain-your-contributions
--> -->

View file

@ -0,0 +1,30 @@
name: clangd-tidy
description: Setup and run clangd-tidy on relevant files
inputs:
changed-files:
description: A pre-filtered list of changed files.
required: true
type: string
runs:
using: composite
steps:
- name: Setup clangd-tidy
shell: sh
run: |
echo "::group::Prerequisite checks"
if [ ! -f "compile_commands.json" ]; then
echo "::error::clangd-tidy checker requires a compilation database to function"
exit 1
fi
echo "::endgroup::"
echo "::group::Installing clangd-tidy"
# Don't know which python we're using, so just access the global scope.
python -m pip install clangd clangd-tidy clang-tidy==22.1.7
echo "::endgroup::"
echo "::group::Running clangd-tidy"
clangd-tidy ${{ inputs.changed-files }}
echo "::endgroup::"

View file

@ -14,7 +14,7 @@ runs:
using: composite using: composite
steps: steps:
- name: Download Godot Artifact - name: Download Godot Artifact
uses: actions/download-artifact@v4 uses: actions/download-artifact@v8
with: with:
name: ${{ inputs.name }} name: ${{ inputs.name }}
path: ${{ inputs.path }} path: ${{ inputs.path }}

View file

@ -0,0 +1,33 @@
name: Godot hash compatibility test
description: Check if methods with given hashes used by the older GDExtensions still can be loaded with given Godot version.
inputs:
bin:
description: Path to the Godot binary.
required: true
type: string
reftags:
description: Reference tags of Godot versions to check (comma separated).
required: true
type: string
runs:
using: composite
steps:
- name: Extract GDExtension interface
shell: sh
run: |
${{ inputs.bin }} --headless --dump-gdextension-interface
mkdir tests/compatibility_test/src/deps/
mv gdextension_interface.h tests/compatibility_test/src/deps/
- name: Build minimal GDExtension
shell: sh
run: scons --directory=./tests/compatibility_test
- name: Download reference GDExtension API JSON and try to load it
shell: sh
env:
GODOT4_BIN: ${{ inputs.bin }}
REFTAGS: ${{ inputs.reftags }}
run: ./tests/compatibility_test/run_compatibility_test.py

View file

@ -22,7 +22,7 @@ runs:
using: composite using: composite
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
submodules: recursive submodules: recursive
repository: godotengine/godot-cpp repository: godotengine/godot-cpp

View file

@ -0,0 +1,55 @@
name: Export Godot project
description: Export a test Godot project.
inputs:
bin:
description: The path to the Godot executable
required: true
runs:
using: composite
steps:
- name: Import resources and export project
shell: sh
run: |
git clone --depth=1 https://github.com/godotengine/godot-tests.git /tmp/godot-tests
echo "Exporting project for Linux (PCK)"
${{ inputs.bin }} --headless --path /tmp/godot-tests/tests/test_project/ --export-pack "Linux" /tmp/test_project.pck 2>&1 | tee log.txt || true
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
echo "Exporting project for Linux (ZIP)"
${{ inputs.bin }} --headless --path /tmp/godot-tests/tests/test_project/ --export-pack "Linux" /tmp/test_project.zip 2>&1 | tee log.txt || true
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
echo "Exporting project for Linux as dedicated server (PCK)"
${{ inputs.bin }} --headless --path /tmp/godot-tests/tests/test_project/ --export-pack "Linux Server" /tmp/test_project_server.pck 2>&1 | tee log.txt || true
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
- name: Run project files from folder
shell: sh
run: |
xvfb-run ${{ inputs.bin }} --path /tmp/godot-tests/tests/test_project/ --language fr --resolution 64x64 --write-movie /tmp/test_project_folder.png --quit 2>&1 | tee log.txt || true
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
${{ inputs.bin }} --headless --path /tmp/godot-tests/tests/test_project/ --quit 2>&1 | tee log.txt || true
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
- name: Run exported project PCK/ZIP
shell: sh
run: |
xvfb-run ${{ inputs.bin }} --main-pack /tmp/test_project.pck --language fr --resolution 64x64 --write-movie /tmp/test_project_pck.png --quit 2>&1 | tee log.txt || true
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
xvfb-run ${{ inputs.bin }} --main-pack /tmp/test_project.zip --language fr --resolution 64x64 --write-movie /tmp/test_project_zip.png --quit 2>&1 | tee log.txt || true
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
# Headless mode is implied for dedicated server PCKs.
${{ inputs.bin }} --main-pack /tmp/test_project_server.pck --quit 2>&1 | tee log.txt || true
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
echo "Checking whether video output from project folder and exported project match..."
md5sum /tmp/test_project*.png | md5sum --check
echo "Checking whether audio output from project folder and exported project match..."
md5sum /tmp/test_project*.wav | md5sum --check

30
engine/.github/changed_files.yml vendored Normal file
View file

@ -0,0 +1,30 @@
# We lack a convenient means of gathering *all* the changes when specializations are passed, so
# a catch-all variable is the easiest workaround.
everything:
- "**"
# Determines if build actions should occur after static checks are ran. Broadly speaking, these
# files changing would result in SCons rebuilding the engine, or are otherwise pertinent to the
# buildsystem itself.
sources:
- .github/{actions/*,workflows}/*.yml
- "**/{SConstruct,SCsub,*.py}"
- "**/*.{h,hpp,hh,hxx,c,cpp,cc,cxx,m,mm,inc,glsl}"
- misc/extension_api_validation/**
- modules/mono/**/*.{cs,csproj,sln,props,targets}
- platform/android/java/{gradle*,**/*.{jar,java,kt,gradle}}
- platform/web/{package{,-lock}.json,js/**/*.js}
- tests/**
# Determines which files are appropriate for running clangd-tidy checks on. This is a subset out
# of necessity, as we're only evaluating a Linux runner.
clangd:
- "**/*.{h,hpp,hxx,hh,c,cpp,cxx,cc}"
- "!**/thirdparty/**"
- "!**/*-so_wrap.{h,c}"
- "!drivers/{apple*,core*,d3d12,metal,wasapi,windows,winmidi,xaudio2}/**"
- "!editor/shader/shader_baker/shader_baker_export_plugin_platform_{d3d12,metal}.{h,cpp}"
- "!modules/camera/camera_{android,macos,win}.{h,cpp}"
- "!modules/openxr/extensions/platform/openxr_{android,metal}_extension.{h,cpp}"
- "!platform/{android,ios,macos,visionos,web,windows}/**"
- "platform/{android,ios,macos,visionos,web,windows}/{api,export}/*.{h,hpp,hxx,hh,c,cpp,cxx,cc}"

View file

@ -1,6 +1,10 @@
name: 🤖 Android Builds name: 🤖 Android Builds
on: on:
workflow_call: workflow_call:
secrets:
SERVICE_ACCOUNT_KEY:
required: true
workflow_dispatch: workflow_dispatch:
# Global Settings # Global Settings
@ -8,7 +12,6 @@ env:
SCONS_FLAGS: >- SCONS_FLAGS: >-
dev_mode=yes dev_mode=yes
module_text_server_fb_enabled=yes module_text_server_fb_enabled=yes
tests=no
swappy=yes swappy=yes
jobs: jobs:
@ -23,6 +26,7 @@ jobs:
- name: Editor (target=editor) - name: Editor (target=editor)
cache-name: android-editor cache-name: android-editor
target: editor target: editor
instrumented_tests: true
scons-flags: >- scons-flags: >-
arch=arm64 arch=arm64
production=yes production=yes
@ -30,21 +34,23 @@ jobs:
- name: Template arm32 (target=template_debug, arch=arm32) - name: Template arm32 (target=template_debug, arch=arm32)
cache-name: android-template-arm32 cache-name: android-template-arm32
target: template_debug target: template_debug
instrumented_tests: false
scons-flags: arch=arm32 scons-flags: arch=arm32
- name: Template arm64 (target=template_debug, arch=arm64) - name: Template arm64 (target=template_debug, arch=arm64)
cache-name: android-template-arm64 cache-name: android-template-arm64
target: template_debug target: template_debug
instrumented_tests: true
scons-flags: arch=arm64 scons-flags: arch=arm64
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
submodules: recursive submodules: recursive
- name: Set up Java 17 - name: Set up Java 17
uses: actions/setup-java@v4 uses: actions/setup-java@v5
with: with:
distribution: temurin distribution: temurin
java-version: 17 java-version: 17
@ -116,3 +122,56 @@ jobs:
with: with:
name: ${{ matrix.cache-name }}-picoos name: ${{ matrix.cache-name }}-picoos
path: picoos path: picoos
- name: Generate Instrumented test APKs
if: matrix.instrumented_tests && github.repository == 'godotengine/godot' && github.event_name == 'push' && github.ref_name == github.event.repository.default_branch
run: |
cd platform/android/java
if [ "${{ matrix.target }}" = "editor" ]; then
./gradlew :editor:assembleAndroidAndroidTest :editor:assembleAndroidDebug -Pperform_signing=true
else
./gradlew :app:assembleAndroidTest :app:assembleInstrumentedDebug -Pperform_signing=true
fi
cd ../../..
- name: Create credentials file
if: matrix.instrumented_tests && github.repository == 'godotengine/godot' && github.event_name == 'push' && github.ref_name == github.event.repository.default_branch
uses: google-github-actions/auth@v3
with:
credentials_json: ${{ secrets.SERVICE_ACCOUNT_KEY }}
- name: Set up gcloud CLI
if: matrix.instrumented_tests && github.repository == 'godotengine/godot' && github.event_name == 'push' && github.ref_name == github.event.repository.default_branch
uses: google-github-actions/setup-gcloud@v3
- name: Run tests on Firebase Test Lab
if: matrix.instrumented_tests && github.repository == 'godotengine/godot' && github.event_name == 'push' && github.ref_name == github.event.repository.default_branch
run: |
set +e
if [ "${{ matrix.target }}" = "editor" ]; then
APP_APK="platform/android/java/editor/build/outputs/apk/android/debug/android_editor-android-debug.apk"
TEST_APK="platform/android/java/editor/build/outputs/apk/androidTest/android/debug/android_editor-android-debug-androidTest.apk"
else
APP_APK="platform/android/java/app/build/outputs/apk/instrumented/debug/android_debug.apk"
TEST_APK="platform/android/java/app/build/outputs/apk/androidTest/instrumented/debug/app-instrumented-debug-androidTest.apk"
fi
output=$(gcloud firebase test android run \
--type instrumentation \
--app "$APP_APK" \
--test "$TEST_APK" \
--device model=MediumPhone.arm,version=26 \
--device model=java,version=30 \
--device model=MediumPhone.arm,version=32 \
--device model=MediumTablet.arm,version=35 \
--device model=rango,version=36 \
--use-orchestrator \
--timeout 2m 2>&1)
exit_code=$?
echo "$output"
if [[ $exit_code -eq 1 && "$output" == *"TEST_QUOTA_EXCEEDED"* ]]; then
echo "::warning title=Firebase Test Lab::Test quota exceeded."
exit_code=0
fi
exit "$exit_code"

View file

@ -8,26 +8,21 @@ env:
SCONS_FLAGS: >- SCONS_FLAGS: >-
dev_mode=yes dev_mode=yes
module_text_server_fb_enabled=yes module_text_server_fb_enabled=yes
tests=no
debug_symbols=no debug_symbols=no
jobs: jobs:
ios-template: ios-template:
# From https://docs.github.com/en/actions/how-tos/write-workflows/choose-where-workflows-run/choose-the-runner-for-a-job#choosing-github-hosted-runners # From https://docs.github.com/en/actions/how-tos/write-workflows/choose-where-workflows-run/choose-the-runner-for-a-job#choosing-github-hosted-runners
runs-on: macos-latest runs-on: macos-26
name: Template (target=template_release) name: Template (target=template_release)
timeout-minutes: 60 timeout-minutes: 60
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
submodules: recursive submodules: recursive
# From https://github.com/actions/runner-images/blob/main/images/macos
- name: Select Xcode 26
run: sudo xcode-select -s /Applications/Xcode_26.0.1.app
- name: Restore Godot build cache - name: Restore Godot build cache
uses: ./.github/actions/godot-cache-restore uses: ./.github/actions/godot-cache-restore
continue-on-error: true continue-on-error: true

View file

@ -1,6 +1,11 @@
name: 🐧 Linux Builds name: 🐧 Linux Builds
on: on:
workflow_call: workflow_call:
inputs:
changed-files:
description: A list of changed files, pre-filtered for parsing by clangd-tidy.
required: true
type: string
workflow_dispatch: workflow_dispatch:
# Global Settings # Global Settings
@ -8,7 +13,6 @@ env:
SCONS_FLAGS: >- SCONS_FLAGS: >-
dev_mode=yes dev_mode=yes
module_text_server_fb_enabled=yes module_text_server_fb_enabled=yes
"accesskit_sdk_path=${{ github.workspace }}/accesskit-c-0.21.2/"
GODOT_CPP_BRANCH: 4.5 GODOT_CPP_BRANCH: 4.5
DOTNET_NOLOGO: true DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true DOTNET_CLI_TELEMETRY_OPTOUT: true
@ -30,15 +34,17 @@ jobs:
- name: Editor w/ Mono (target=editor) - name: Editor w/ Mono (target=editor)
cache-name: linux-editor-mono cache-name: linux-editor-mono
target: editor target: editor
scons-flags: module_mono_enabled=yes scons-flags: module_mono_enabled=yes compiledb=yes
bin: ./bin/godot.linuxbsd.editor.x86_64.mono bin: ./bin/godot.linuxbsd.editor.x86_64.mono
build-mono: true build-mono: true
doc-test: true doc-test: true
proj-conv: true proj-conv: true
proj-export: true
api-compat: true api-compat: true
artifact: true artifact: true
# Validate godot-cpp compatibility on one arbitrary editor build. # Validate godot-cpp compatibility on one arbitrary editor build.
godot-cpp: true godot-cpp: true
clangd-tidy: true
- name: Editor with doubles and GCC sanitizers (target=editor, dev_build=yes, scu_build=yes, precision=double, use_asan=yes, use_ubsan=yes, linker=mold) - name: Editor with doubles and GCC sanitizers (target=editor, dev_build=yes, scu_build=yes, precision=double, use_asan=yes, use_ubsan=yes, linker=mold)
cache-name: linux-editor-double-sanitizers cache-name: linux-editor-double-sanitizers
@ -54,6 +60,7 @@ jobs:
linker=mold linker=mold
bin: ./bin/godot.linuxbsd.editor.dev.double.x86_64.san bin: ./bin/godot.linuxbsd.editor.dev.double.x86_64.san
proj-test: true proj-test: true
free-space: true
- name: Editor with clang sanitizers (target=editor, dev_build=yes, use_asan=yes, use_ubsan=yes, use_llvm=yes, linker=lld) - name: Editor with clang sanitizers (target=editor, dev_build=yes, use_asan=yes, use_ubsan=yes, use_llvm=yes, linker=lld)
cache-name: linux-editor-llvm-sanitizers cache-name: linux-editor-llvm-sanitizers
@ -67,6 +74,7 @@ jobs:
bin: ./bin/godot.linuxbsd.editor.dev.x86_64.llvm.san bin: ./bin/godot.linuxbsd.editor.dev.x86_64.llvm.san
# Test our oldest supported SCons/Python versions on one arbitrary editor build. # Test our oldest supported SCons/Python versions on one arbitrary editor build.
legacy-scons: true legacy-scons: true
free-space: true
- name: Editor with ThreadSanitizer (target=editor, dev_build=yes, use_tsan=yes, use_llvm=yes, linker=lld) - name: Editor with ThreadSanitizer (target=editor, dev_build=yes, use_tsan=yes, use_llvm=yes, linker=lld)
cache-name: linux-editor-thread-sanitizer cache-name: linux-editor-thread-sanitizer
@ -77,6 +85,7 @@ jobs:
use_llvm=yes use_llvm=yes
linker=lld linker=lld
bin: ./bin/godot.linuxbsd.editor.dev.x86_64.llvm.san bin: ./bin/godot.linuxbsd.editor.dev.x86_64.llvm.san
free-space: true
- name: Template w/ Mono, release (target=template_release) - name: Template w/ Mono, release (target=template_release)
cache-name: linux-template-mono cache-name: linux-template-mono
@ -110,7 +119,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
submodules: recursive submodules: recursive
@ -118,11 +127,12 @@ jobs:
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install libwayland-bin # TODO: Figure out somehow how to embed this one. sudo apt-get install libwayland-bin # TODO: Figure out somehow how to embed this one.
if [ "${{ matrix.proj-test }}" == "true" ]; then if [ "${{ matrix.proj-test }}" == "true" -o "${{ matrix.proj-export }}" == "true" ]; then
sudo apt-get install mesa-vulkan-drivers sudo apt-get install mesa-vulkan-drivers
fi fi
- name: Free disk space on runner - name: Free disk space on runner
if: matrix.free-space
run: | run: |
echo "Disk usage before:" && df -h echo "Disk usage before:" && df -h
sudo rm -rf /usr/local/lib/android sudo rm -rf /usr/local/lib/android
@ -143,8 +153,8 @@ jobs:
uses: ./.github/actions/godot-deps uses: ./.github/actions/godot-deps
with: with:
# Sync with Ensure*Version in SConstruct. # Sync with Ensure*Version in SConstruct.
python-version: 3.8 python-version: 3.9
scons-version: 4.0 scons-version: 4.4
- name: Force remove preinstalled .NET SDKs - name: Force remove preinstalled .NET SDKs
if: matrix.build-mono if: matrix.build-mono
@ -153,21 +163,21 @@ jobs:
- name: Setup older .NET SDK as baseline - name: Setup older .NET SDK as baseline
if: matrix.build-mono if: matrix.build-mono
uses: actions/setup-dotnet@v4 uses: actions/setup-dotnet@v5
with: with:
# Targeting the oldest version we want to support to ensure it still builds. # Targeting the oldest version we want to support to ensure it still builds.
dotnet-version: 8.0.100 dotnet-version: 8.0.100
- name: Download pre-built AccessKit - name: Download pre-built AccessKit
uses: dsaltares/fetch-gh-release-asset@1.1.2 shell: sh
with: id: accesskit-sdk
repo: godotengine/godot-accesskit-c-static run: |
version: tags/0.21.2 if python ./misc/scripts/install_accesskit.py; then
file: accesskit-c-0.21.2.zip echo "ACCESSKIT_ENABLED=yes" >> "$GITHUB_OUTPUT"
target: accesskit-c-0.21.2/accesskit_c.zip else
echo "::warning::AccessKit SDK installation failed, building without AccessKit support."
- name: Extract pre-built AccessKit echo "ACCESSKIT_ENABLED=no" >> "$GITHUB_OUTPUT"
run: unzip -o accesskit-c-0.21.2/accesskit_c.zip fi
- name: Install mold linker - name: Install mold linker
if: matrix.proj-test if: matrix.proj-test
@ -176,10 +186,16 @@ jobs:
- name: Compilation - name: Compilation
uses: ./.github/actions/godot-build uses: ./.github/actions/godot-build
with: with:
scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} accesskit=${{ steps.accesskit-sdk.outputs.ACCESSKIT_ENABLED }}
platform: linuxbsd platform: linuxbsd
target: ${{ matrix.target }} target: ${{ matrix.target }}
- name: Style checks via clangd-tidy
if: matrix.clangd-tidy
uses: ./.github/actions/clangd-tidy
with:
changed-files: ${{ inputs.changed-files }}
- name: Compilation (godot-cpp) - name: Compilation (godot-cpp)
uses: ./.github/actions/godot-cpp-build uses: ./.github/actions/godot-cpp-build
if: matrix.godot-cpp if: matrix.godot-cpp
@ -208,6 +224,7 @@ jobs:
- name: Prepare artifact - name: Prepare artifact
if: matrix.artifact if: matrix.artifact
run: | run: |
rm -rf ./bin/build_deps
strip bin/godot.* strip bin/godot.*
chmod +x bin/godot.* chmod +x bin/godot.*
@ -238,11 +255,18 @@ jobs:
git diff --color --exit-code && ! git ls-files --others --exclude-standard | sed -e 's/^/New doc file missing in PR: /' | grep 'xml$' git diff --color --exit-code && ! git ls-files --others --exclude-standard | sed -e 's/^/New doc file missing in PR: /' | grep 'xml$'
# Check API backwards compatibility # Check API backwards compatibility
- name: Check for GDExtension compatibility - name: Check for GDExtension compatibility JSON check
if: matrix.api-compat if: matrix.api-compat
run: | run: |
./misc/scripts/validate_extension_api.sh "${{ matrix.bin }}" ./misc/scripts/validate_extension_api.sh "${{ matrix.bin }}"
- name: Test GDExtension compatibility load methods
uses: ./.github/actions/godot-compat-test
if: matrix.api-compat
with:
bin: ${{ matrix.bin }}
reftags: "4.5-stable,4.4-stable"
# Download and run the test project # Download and run the test project
- name: Test Godot project - name: Test Godot project
uses: ./.github/actions/godot-project-test uses: ./.github/actions/godot-project-test
@ -250,6 +274,13 @@ jobs:
with: with:
bin: ${{ matrix.bin }} bin: ${{ matrix.bin }}
# Test project export
- name: Test project export
uses: ./.github/actions/godot-project-export
if: matrix.proj-export
with:
bin: ${{ matrix.bin }}
# Test the project converter # Test the project converter
- name: Test project converter - name: Test project converter
uses: ./.github/actions/godot-converter-test uses: ./.github/actions/godot-converter-test

View file

@ -8,12 +8,11 @@ env:
SCONS_FLAGS: >- SCONS_FLAGS: >-
dev_mode=yes dev_mode=yes
module_text_server_fb_enabled=yes module_text_server_fb_enabled=yes
"accesskit_sdk_path=${{ github.workspace }}/accesskit-c-0.21.2/"
jobs: jobs:
build-macos: build-macos:
# From https://docs.github.com/en/actions/how-tos/write-workflows/choose-where-workflows-run/choose-the-runner-for-a-job#choosing-github-hosted-runners # From https://docs.github.com/en/actions/how-tos/write-workflows/choose-where-workflows-run/choose-the-runner-for-a-job#choosing-github-hosted-runners
runs-on: macos-latest runs-on: macos-26
name: ${{ matrix.name }} name: ${{ matrix.name }}
timeout-minutes: 120 timeout-minutes: 120
strategy: strategy:
@ -33,14 +32,10 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
submodules: recursive submodules: recursive
# From https://github.com/actions/runner-images/blob/main/images/macos
- name: Select Xcode 26
run: sudo xcode-select -s /Applications/Xcode_26.0.1.app
- name: Restore Godot build cache - name: Restore Godot build cache
uses: ./.github/actions/godot-cache-restore uses: ./.github/actions/godot-cache-restore
with: with:
@ -50,16 +45,27 @@ jobs:
- name: Setup Python and SCons - name: Setup Python and SCons
uses: ./.github/actions/godot-deps uses: ./.github/actions/godot-deps
- name: Download pre-built AccessKit - name: Download pre-built ANGLE
uses: dsaltares/fetch-gh-release-asset@1.1.2 shell: sh
with: id: angle-sdk
repo: godotengine/godot-accesskit-c-static run: |
version: tags/0.21.2 if python ./misc/scripts/install_angle.py; then
file: accesskit-c-0.21.2.zip echo "ANGLE_ENABLED=yes" >> "$GITHUB_OUTPUT"
target: accesskit-c-0.21.2/accesskit_c.zip else
echo "::warning::ANGLE SDK installation failed, building without ANGLE support."
echo "ANGLE_ENABLED=no" >> "$GITHUB_OUTPUT"
fi
- name: Extract pre-built AccessKit - name: Download pre-built AccessKit
run: unzip -o accesskit-c-0.21.2/accesskit_c.zip shell: sh
id: accesskit-sdk
run: |
if python3 ./misc/scripts/install_accesskit.py; then
echo "ACCESSKIT_ENABLED=yes" >> "$GITHUB_OUTPUT"
else
echo "::warning::AccessKit SDK installation failed, building without AccessKit support."
echo "ACCESSKIT_ENABLED=no" >> "$GITHUB_OUTPUT"
fi
- name: Setup Vulkan SDK - name: Setup Vulkan SDK
id: vulkan-sdk id: vulkan-sdk
@ -75,14 +81,14 @@ jobs:
- name: Compilation (x86_64) - name: Compilation (x86_64)
uses: ./.github/actions/godot-build uses: ./.github/actions/godot-build
with: with:
scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} arch=x86_64 vulkan=${{ steps.vulkan-sdk.outputs.VULKAN_ENABLED }} scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} arch=x86_64 vulkan=${{ steps.vulkan-sdk.outputs.VULKAN_ENABLED }} accesskit=${{ steps.accesskit-sdk.outputs.ACCESSKIT_ENABLED }} angle=${{ steps.angle-sdk.outputs.ANGLE_ENABLED }}
platform: macos platform: macos
target: ${{ matrix.target }} target: ${{ matrix.target }}
- name: Compilation (arm64) - name: Compilation (arm64)
uses: ./.github/actions/godot-build uses: ./.github/actions/godot-build
with: with:
scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} arch=arm64 vulkan=${{ steps.vulkan-sdk.outputs.VULKAN_ENABLED }} scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} arch=arm64 vulkan=${{ steps.vulkan-sdk.outputs.VULKAN_ENABLED }} accesskit=${{ steps.accesskit-sdk.outputs.ACCESSKIT_ENABLED }} angle=${{ steps.angle-sdk.outputs.ANGLE_ENABLED }}
platform: macos platform: macos
target: ${{ matrix.target }} target: ${{ matrix.target }}
@ -96,6 +102,7 @@ jobs:
run: | run: |
lipo -create ./bin/godot.macos.${{ matrix.target }}.x86_64 ./bin/godot.macos.${{ matrix.target }}.arm64 -output ./bin/godot.macos.${{ matrix.target }}.universal lipo -create ./bin/godot.macos.${{ matrix.target }}.x86_64 ./bin/godot.macos.${{ matrix.target }}.arm64 -output ./bin/godot.macos.${{ matrix.target }}.universal
rm ./bin/godot.macos.${{ matrix.target }}.x86_64 ./bin/godot.macos.${{ matrix.target }}.arm64 rm ./bin/godot.macos.${{ matrix.target }}.x86_64 ./bin/godot.macos.${{ matrix.target }}.arm64
rm -rf ./bin/build_deps
strip bin/godot.* strip bin/godot.*
chmod +x bin/godot.* chmod +x bin/godot.*

View file

@ -18,29 +18,39 @@ jobs:
android-build: android-build:
name: 🤖 Android name: 🤖 Android
needs: static-checks needs: static-checks
if: needs.static-checks.outputs.sources-changed == 'true' || github.event_name != 'pull_request'
uses: ./.github/workflows/android_builds.yml uses: ./.github/workflows/android_builds.yml
secrets:
SERVICE_ACCOUNT_KEY: ${{ secrets.SERVICE_ACCOUNT_KEY }}
ios-build: ios-build:
name: 🍏 iOS name: 🍏 iOS
needs: static-checks needs: static-checks
if: needs.static-checks.outputs.sources-changed == 'true' || github.event_name != 'pull_request'
uses: ./.github/workflows/ios_builds.yml uses: ./.github/workflows/ios_builds.yml
linux-build: linux-build:
name: 🐧 Linux name: 🐧 Linux
needs: static-checks needs: static-checks
if: needs.static-checks.outputs.sources-changed == 'true' || github.event_name != 'pull_request'
uses: ./.github/workflows/linux_builds.yml uses: ./.github/workflows/linux_builds.yml
with:
changed-files: ${{ needs.static-checks.outputs.changed-files }}
macos-build: macos-build:
name: 🍎 macOS name: 🍎 macOS
needs: static-checks needs: static-checks
if: needs.static-checks.outputs.sources-changed == 'true' || github.event_name != 'pull_request'
uses: ./.github/workflows/macos_builds.yml uses: ./.github/workflows/macos_builds.yml
windows-build: windows-build:
name: 🏁 Windows name: 🏁 Windows
needs: static-checks needs: static-checks
if: needs.static-checks.outputs.sources-changed == 'true' || github.event_name != 'pull_request'
uses: ./.github/workflows/windows_builds.yml uses: ./.github/workflows/windows_builds.yml
web-build: web-build:
name: 🌐 Web name: 🌐 Web
needs: static-checks needs: static-checks
if: needs.static-checks.outputs.sources-changed == 'true' || github.event_name != 'pull_request'
uses: ./.github/workflows/web_builds.yml uses: ./.github/workflows/web_builds.yml

View file

@ -1,6 +1,13 @@
name: 📊 Static Checks name: 📊 Static Checks
on: on:
workflow_call: workflow_call:
outputs:
changed-files:
description: A list of changed files, pre-filtered for parsing by clangd-tidy.
value: ${{ jobs.static-checks.outputs.changed-files }}
sources-changed:
description: Determines if any source files were changed.
value: ${{ jobs.static-checks.outputs.sources-changed }}
workflow_dispatch: workflow_dispatch:
jobs: jobs:
@ -8,11 +15,15 @@ jobs:
name: Code style, file formatting, and docs name: Code style, file formatting, and docs
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
timeout-minutes: 30 timeout-minutes: 30
outputs:
changed-files: '"${{ steps.changed-files.outputs.clangd_all_changed_files }}"' # Wrap with quotes to bookend internal quote separators.
sources-changed: ${{ steps.changed-files.outputs.sources_any_changed }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
fetch-depth: 2 fetch-depth: 0 # Treeless clone. Slightly less performant than a shallow clone, but makes finding diffs instantaneous.
filter: tree:0 # See: https://github.blog/open-source/git/get-up-to-speed-with-partial-clone-and-shallow-clone/
# This needs to happen before Python and npm execution; it must happen before any extra files are written. # This needs to happen before Python and npm execution; it must happen before any extra files are written.
- name: .gitignore checks (gitignore_check.sh) - name: .gitignore checks (gitignore_check.sh)
@ -20,18 +31,17 @@ jobs:
bash ./misc/scripts/gitignore_check.sh bash ./misc/scripts/gitignore_check.sh
- name: Get changed files - name: Get changed files
env: id: changed-files
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} uses: tj-actions/changed-files@v47
run: |
if [ "${{ github.event_name }}" == "pull_request" ]; then
files=$(git diff-tree --no-commit-id --name-only -r HEAD^1..HEAD 2> /dev/null || true)
elif [ "${{ github.event_name }}" == "push" -a "${{ github.event.forced }}" == "false" -a "${{ github.event.created }}" == "false" ]; then
files=$(git diff-tree --no-commit-id --name-only -r ${{ github.event.before }}..${{ github.event.after }} 2> /dev/null || true)
fi
files=$(echo "$files" | xargs -I {} sh -c 'echo "\"./{}\""' | tr '\n' ' ')
echo "CHANGED_FILES=$files" >> $GITHUB_ENV
- name: Style checks via pre-commit
uses: pre-commit/action@v3.0.1
with: with:
extra_args: --files ${{ env.CHANGED_FILES }} safe_output: false # Output passed to environment variable to avoid command injection.
separator: '" "' # To account for paths with spaces, ensure our items are split by quotes internally.
skip_initial_fetch: true
files_yaml_from_source_file: .github/changed_files.yml
- name: Style checks via prek
uses: j178/prek-action@v2
env:
CHANGED_FILES: '"${{ steps.changed-files.outputs.everything_all_changed_files }}"' # Wrap with quotes to bookend internal quote separators.
with:
extra-args: --files ${{ env.CHANGED_FILES }}

View file

@ -7,7 +7,6 @@ on:
env: env:
SCONS_FLAGS: >- SCONS_FLAGS: >-
dev_mode=yes dev_mode=yes
tests=no
debug_symbols=no debug_symbols=no
use_closure_compiler=yes use_closure_compiler=yes
EM_VERSION: 4.0.11 EM_VERSION: 4.0.11
@ -21,26 +20,26 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- name: Template w/ threads (target=template_release, threads=yes) - name: Template w/ threads, 64-bit (target=template_release, threads=yes, arch=wasm64)
cache-name: web-template cache-name: web-template
target: template_release target: template_release
scons-flags: threads=yes scons-flags: threads=yes arch=wasm64
artifact: true artifact: true
- name: Template w/o threads (target=template_release, threads=no) - name: Template w/o threads, 32-bit (target=template_release, threads=no, arch=wasm32)
cache-name: web-nothreads-template cache-name: web-nothreads-template
target: template_release target: template_release
scons-flags: threads=no scons-flags: threads=no arch=wasm32
artifact: true artifact: true
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
submodules: recursive submodules: recursive
- name: Set up Emscripten latest - name: Set up Emscripten latest
uses: mymindstorm/setup-emsdk@v14 uses: emscripten-core/setup-emsdk@v16
with: with:
version: ${{ env.EM_VERSION }} version: ${{ env.EM_VERSION }}
no-cache: true no-cache: true

View file

@ -9,8 +9,6 @@ env:
dev_mode=yes dev_mode=yes
module_text_server_fb_enabled=yes module_text_server_fb_enabled=yes
debug_symbols=no debug_symbols=no
"angle_libs=${{ github.workspace }}/"
"accesskit_sdk_path=${{ github.workspace }}/accesskit-c-0.21.2/"
SCONS_CACHE_MSVC_CONFIG: true SCONS_CACHE_MSVC_CONFIG: true
PYTHONIOENCODING: utf8 PYTHONIOENCODING: utf8
@ -59,7 +57,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
submodules: recursive submodules: recursive
@ -84,32 +82,37 @@ jobs:
fi fi
continue-on-error: true continue-on-error: true
- name: Download pre-built ANGLE static libraries - name: Download pre-built ANGLE
uses: dsaltares/fetch-gh-release-asset@1.1.2 shell: sh
with: id: angle-sdk
repo: godotengine/godot-angle-static run: |
version: tags/chromium/6601.2 if python ./misc/scripts/install_angle.py; then
file: godot-angle-static-x86_64-${{ matrix.compiler == 'gcc' && 'gcc' || 'msvc' }}-release.zip echo "ANGLE_ENABLED=yes" >> "$GITHUB_OUTPUT"
target: angle/angle.zip else
echo "::warning::ANGLE SDK installation failed, building without ANGLE support."
echo "ANGLE_ENABLED=no" >> "$GITHUB_OUTPUT"
fi
- name: Extract pre-built ANGLE static libraries - name: Download WinRT components
run: Expand-Archive -Force angle/angle.zip ${{ github.workspace }}/ shell: sh
run: python ./misc/scripts/install_winrt.py
continue-on-error: true
- name: Download pre-built AccessKit - name: Download pre-built AccessKit
uses: dsaltares/fetch-gh-release-asset@1.1.2 shell: sh
with: id: accesskit-sdk
repo: godotengine/godot-accesskit-c-static run: |
version: tags/0.21.2 if python ./misc/scripts/install_accesskit.py; then
file: accesskit-c-0.21.2.zip echo "ACCESSKIT_ENABLED=yes" >> "$GITHUB_OUTPUT"
target: accesskit-c-0.21.2/accesskit_c.zip else
echo "::warning::AccessKit SDK installation failed, building without AccessKit support."
- name: Extract pre-built AccessKit echo "ACCESSKIT_ENABLED=no" >> "$GITHUB_OUTPUT"
run: unzip -o accesskit-c-0.21.2/accesskit_c.zip fi
- name: Compilation - name: Compilation
uses: ./.github/actions/godot-build uses: ./.github/actions/godot-build
with: with:
scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} d3d12=${{ steps.d3d12-sdk.outputs.D3D12_ENABLED }} scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} d3d12=${{ steps.d3d12-sdk.outputs.D3D12_ENABLED }} accesskit=${{ steps.accesskit-sdk.outputs.ACCESSKIT_ENABLED }} angle=${{ steps.angle-sdk.outputs.ANGLE_ENABLED }}
platform: windows platform: windows
target: ${{ matrix.target }} target: ${{ matrix.target }}
@ -122,6 +125,7 @@ jobs:
- name: Prepare artifact - name: Prepare artifact
if: matrix.compiler == 'msvc' if: matrix.compiler == 'msvc'
run: | run: |
Remove-Item bin/build_deps -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item bin/* -Include *.exp,*.lib,*.pdb -Force Remove-Item bin/* -Include *.exp,*.lib,*.pdb -Force
- name: Upload artifact - name: Upload artifact

3
engine/.gitignore vendored
View file

@ -23,7 +23,6 @@
########################### ###########################
/custom.py /custom.py
misc/hooks/pre-commit-custom-*
############################# #############################
### Godot generated files ### ### Godot generated files ###
@ -277,6 +276,8 @@ thirdparty/swappy-frame-pacing/armeabi-v7a/abi.json
thirdparty/swappy-frame-pacing/x86/abi.json thirdparty/swappy-frame-pacing/x86/abi.json
thirdparty/swappy-frame-pacing/x86_64/abi.json thirdparty/swappy-frame-pacing/x86_64/abi.json
thirdparty/perfetto/
# Visual Studio 2015/2017 cache/options directory # Visual Studio 2015/2017 cache/options directory
.vs/ .vs/

View file

@ -43,6 +43,7 @@ DmitriySalnikov <salnikov.mine@yandex.ru> <dimokgamer@gmail.com>
Dominik 'dreamsComeTrue' Jasiński <dominikjasinski@o2.pl> Dominik 'dreamsComeTrue' Jasiński <dominikjasinski@o2.pl>
DeeJayLSP <djlsplays@gmail.com> <60024671+DeeJayLSP@users.noreply.github.com> DeeJayLSP <djlsplays@gmail.com> <60024671+DeeJayLSP@users.noreply.github.com>
Emmanuel Barroga <emmanuelbarroga@gmail.com> Emmanuel Barroga <emmanuelbarroga@gmail.com>
Enzo Novoselic <41305715+StarryWorm@users.noreply.github.com>
Eric M <itsjusteza@gmail.com> Eric M <itsjusteza@gmail.com>
Eric Rybicki <info@ericrybicki.com> <stratos695@googlemail.com> Eric Rybicki <info@ericrybicki.com> <stratos695@googlemail.com>
Erik Selecký <35656626+rxlecky@users.noreply.github.com> Erik Selecký <35656626+rxlecky@users.noreply.github.com>
@ -67,7 +68,7 @@ Guilherme Felipe <guilhermefelipecgs@gmail.com>
Hakim <hakim.rouatbi@gmail.com> Hakim <hakim.rouatbi@gmail.com>
Hanif Bin Ariffin <hanif.ariffin.4326@gmail.com> Hanif Bin Ariffin <hanif.ariffin.4326@gmail.com>
HaSa1002 <johawitt@outlook.de> HaSa1002 <johawitt@outlook.de>
Hein-Pieter van Braam-Stewart <hp@tmm.cx> HP van Braam <hp@tmm.cx>
Hugo Locurcio <hugo.locurcio@hugo.pro> <hugo.l@openmailbox.org> Hugo Locurcio <hugo.locurcio@hugo.pro> <hugo.l@openmailbox.org>
Hugo Locurcio <hugo.locurcio@hugo.pro> <Calinou@users.noreply.github.com> Hugo Locurcio <hugo.locurcio@hugo.pro> <Calinou@users.noreply.github.com>
Hugo Locurcio <hugo.locurcio@hugo.pro> Calinou <calinou@opmbx.org> Hugo Locurcio <hugo.locurcio@hugo.pro> Calinou <calinou@opmbx.org>
@ -109,6 +110,7 @@ Kongfa Waroros <gongpha@hotmail.com>
K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com> K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com>
K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com> <fire@users.noreply.github.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> kleonc <9283098+kleonc@users.noreply.github.com> <kleonc@users.noreply.github.com>
LanzaSchneider <schneiderlanza@gmail.com>
Leon Krause <lk@leonkrause.com> <eska@eska.me> Leon Krause <lk@leonkrause.com> <eska@eska.me>
Leon Krause <lk@leonkrause.com> <eska014@users.noreply.github.com> Leon Krause <lk@leonkrause.com> <eska014@users.noreply.github.com>
Liz Haas <27thLiz@gmail.com> Liz Haas <27thLiz@gmail.com>
@ -173,8 +175,9 @@ Rémi Verschelde <rverschelde@gmail.com> <remi@verschelde.fr>
Rhody Lugo <rhodylugo@gmail.com> <rhodylugo@me.com> Rhody Lugo <rhodylugo@gmail.com> <rhodylugo@me.com>
Ricardo Subtil <ricasubtil@gmail.com> Ricardo Subtil <ricasubtil@gmail.com>
Rindbee <idleman@yeah.net> Rindbee <idleman@yeah.net>
Robert Yevdokimov <robert.yevdokimov@autStand.com> Robert Yevdokimov <robert.yevdokimov@autstand.com>
Robert Yevdokimov <robert.yevdokimov@autStand.com> <105675984+ryevdokimov@users.noreply.github.com> Robert Yevdokimov <robert.yevdokimov@autstand.com> <robert.yevdokimov@autStand.com>
Robert Yevdokimov <robert.yevdokimov@autstand.com> <105675984+ryevdokimov@users.noreply.github.com>
Robin Hübner <profan@prfn.se> <robinhubner@gmail.com> Robin Hübner <profan@prfn.se> <robinhubner@gmail.com>
romulox_x <romulox_x@yahoo.com> romulox_x <romulox_x@yahoo.com>
Rudolph Bester <Rudolph.f.Bester@gmail.com> <Rudolph.f.bester@gmail.com> Rudolph Bester <Rudolph.f.Bester@gmail.com> <Rudolph.f.bester@gmail.com>
@ -199,6 +202,7 @@ V.VamsiKrishna <vk@bsb.in> <vamsikrishna.v@gmail.com>
Wilhem Barbier <nounoursheureux@openmailbox.org> <wilhem.b@free.fr> Wilhem Barbier <nounoursheureux@openmailbox.org> <wilhem.b@free.fr>
Wilhem Barbier <nounoursheureux@openmailbox.org> <schtroumps31@gmail.com> Wilhem Barbier <nounoursheureux@openmailbox.org> <schtroumps31@gmail.com>
Will Nations <willnationsdev@gmail.com> Will Nations <willnationsdev@gmail.com>
Xu Huisheng <xyz20003@gmail.com>
ydeltastar <ydeltastar@gmail.com> ydeltastar <ydeltastar@gmail.com>
yg2f <yoann@terminajones.com> yg2f <yoann@terminajones.com>
Yuri Sizov <yuris@humnom.net> <pycbouh@users.noreply.github.com> Yuri Sizov <yuris@humnom.net> <pycbouh@users.noreply.github.com>

View file

@ -18,7 +18,7 @@ repos:
exclude: (SConstruct|SCsub)$ # SCons files use shebangs for syntax highlighting only. exclude: (SConstruct|SCsub)$ # SCons files use shebangs for syntax highlighting only.
- repo: https://github.com/pre-commit/mirrors-clang-format - repo: https://github.com/pre-commit/mirrors-clang-format
rev: v21.1.7 rev: v22.1.5
hooks: hooks:
- id: clang-format - id: clang-format
files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java)$ files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java)$
@ -29,43 +29,49 @@ repos:
types_or: [text] types_or: [text]
args: [-style=file:misc/utility/clang_format_glsl.yml] args: [-style=file:misc/utility/clang_format_glsl.yml]
# Not automatically triggered (because it requires compile_commands.json to be up-to-date).
# Invoke it manually via `pre-commit run --hook-stage manual clang-tidy`
- repo: https://github.com/pocc/pre-commit-hooks - repo: https://github.com/pocc/pre-commit-hooks
rev: v1.3.5 rev: v1.3.5
hooks: hooks:
- id: clang-tidy - id: clang-tidy
files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java|glsl)$ # TODO .inc ignored for now because they don't include their parent header.
args: [--fix, --quiet, --use-color] # TODO Platform-specific subfolders currently fail, we should try to include them
files: ^(core|main|scene)/.*\.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|java)$
# No unknown warning suppression used for easier compatibility with gcc
args: [--fix, --quiet, --use-color, -p=compile_commands.json, -extra-arg=-Wno-unknown-warning-option]
types_or: [text] types_or: [text]
additional_dependencies: [clang-tidy==21.1.6] additional_dependencies: [clang-tidy==22.1.7]
require_serial: true require_serial: false
stages: [manual] # Not automatically triggered, invoked via `pre-commit run --hook-stage manual clang-tidy` stages: [manual]
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.7 rev: v0.15.20
hooks: hooks:
- id: ruff-check - id: ruff-check
args: [--fix] args: [--color=always]
files: (\.py|SConstruct|SCsub)$ files: (\.py|SConstruct|SCsub)$
types_or: [text] types_or: [text]
- id: ruff-format - id: ruff-format
args: [--color=always]
files: (\.py|SConstruct|SCsub)$ files: (\.py|SConstruct|SCsub)$
types_or: [text] types_or: [text]
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.14.1 # Latest version that supports Python 3.8 rev: v1.19.1
hooks: hooks:
- id: mypy - id: mypy
files: \.py$ files: \.py$
types_or: [text] types_or: [text]
- repo: https://github.com/codespell-project/codespell - repo: https://github.com/codespell-project/codespell
rev: v2.4.1 rev: v2.4.2
hooks: hooks:
- id: codespell - id: codespell
additional_dependencies: [tomli] additional_dependencies: [tomli]
- repo: https://github.com/python-jsonschema/check-jsonschema - repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.34.1 rev: 0.37.4
hooks: hooks:
- id: check-jsonschema - id: check-jsonschema
files: ^core/extension/gdextension_interface\.json$ files: ^core/extension/gdextension_interface\.json$
@ -103,6 +109,18 @@ repos:
files: ^(doc/classes|.*/doc_classes)/.*\.xml$ files: ^(doc/classes|.*/doc_classes)/.*\.xml$
additional_dependencies: [xmlschema] additional_dependencies: [xmlschema]
- id: validate-codeowners
name: validate-codeowners
language: python
entry: python misc/scripts/validate_codeowners.py
args: [--unowned]
- id: validate-includes
name: validate-includes
language: python
entry: python misc/scripts/validate_includes.py
files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc)$
- id: eslint - id: eslint
name: eslint name: eslint
language: node language: node
@ -148,7 +166,7 @@ repos:
entry: svgo entry: svgo
files: \.svg$ files: \.svg$
args: [--quiet, --config, misc/utility/svgo.config.mjs] args: [--quiet, --config, misc/utility/svgo.config.mjs]
additional_dependencies: [svgo@3.3.2] additional_dependencies: [svgo@4.0.1]
- id: copyright-headers - id: copyright-headers
name: copyright-headers name: copyright-headers

View file

@ -36,10 +36,13 @@ name is available.
Aaron Record (LightningAA) Aaron Record (LightningAA)
Adam Johnston (a-johnston) Adam Johnston (a-johnston)
Adam Scott (adamscott) Adam Scott (adamscott)
Adriaan de Jongh (AdriaandeJongh)
Æio Much (AeioMuch) Æio Much (AeioMuch)
Akosmo
Alex Drozd (brno32) Alex Drozd (brno32)
Alexander Hartmann (Alex2782) Alexander Hartmann (Alex2782)
Alexander Holland (AlexHolly) Alexander Holland (AlexHolly)
Alexander Streng (warriormaster12)
Alexey Khoroshavin (allkhor) Alexey Khoroshavin (allkhor)
Alfred Reinold Baudisch (alfredbaudisch) Alfred Reinold Baudisch (alfredbaudisch)
Alistair Leslie-Hughes (alesliehughes) Alistair Leslie-Hughes (alesliehughes)
@ -113,10 +116,12 @@ name is available.
Dominik Jasiński (dreamsComeTrue) Dominik Jasiński (dreamsComeTrue)
Douglas Leão (DeeJayLSP) Douglas Leão (DeeJayLSP)
DualMatrix DualMatrix
Edward Chan (EdwardChanCH)
Ellen Poe (ellenhp) Ellen Poe (ellenhp)
Emilio Coppola (coppolaemilio) Emilio Coppola (coppolaemilio)
Emmanuel Barroga (codecustard) Emmanuel Barroga (codecustard)
Emmanuel Leblond (touilleMan) Emmanuel Leblond (touilleMan)
Enzo Novoselic (StarryWorm)
Eoin O'Neill (Eoin-ONeill-Yokai) Eoin O'Neill (Eoin-ONeill-Yokai)
Eric Lasota (elasota) Eric Lasota (elasota)
Eric M (EricEzaM) Eric M (EricEzaM)
@ -148,13 +153,13 @@ name is available.
Hakim Rouatbi (hakro) Hakim Rouatbi (hakro)
Hanif Bin Ariffin (hbina) Hanif Bin Ariffin (hbina)
Haoyu Qiu (timothyqiu) Haoyu Qiu (timothyqiu)
Hein-Pieter van Braam-Stewart (hpvb)
Hendrik Brucker (Geometror) Hendrik Brucker (Geometror)
Hilderin Hilderin
Hiroshi Ogawa (hi-ogawa) Hiroshi Ogawa (hi-ogawa)
HolonProduction HolonProduction
homer666 homer666
hoontee hoontee
HP van Braam (hpvb)
Hugo Locurcio (Calinou) Hugo Locurcio (Calinou)
Ian Bishop (ianb96) Ian Bishop (ianb96)
Ibrahn Sahir (ibrahn) Ibrahn Sahir (ibrahn)
@ -175,6 +180,8 @@ name is available.
Jean-Michel Bernard (jmb462) Jean-Michel Bernard (jmb462)
Jérôme Gully (Nutriz) Jérôme Gully (Nutriz)
Jia Jun Chai (SkyLucilfer) Jia Jun Chai (SkyLucilfer)
Jin Q.W. (LanzaSchneider)
Jin Yang (jinyangcruise)
jitspoe jitspoe
Joan Fons Sanchez (JFonS) Joan Fons Sanchez (JFonS)
Johan Aires Rastén (JohanAR) Johan Aires Rastén (JohanAR)
@ -185,6 +192,7 @@ name is available.
Jorrit Rouwe (jrouwe) Jorrit Rouwe (jrouwe)
Josh Jones (DarkKilauea) Josh Jones (DarkKilauea)
Joshua Grams (JoshuaGrams) Joshua Grams (JoshuaGrams)
Joyless (Joy-less)
Juan Linietsky (reduz) Juan Linietsky (reduz)
Julian Murgia (StraToN) Julian Murgia (StraToN)
Julien Nguyen (Blackiris) Julien Nguyen (Blackiris)
@ -256,6 +264,7 @@ name is available.
Michał Iwańczuk (iwek7) Michał Iwańczuk (iwek7)
MichiRecRoom (LikeLakers2) MichiRecRoom (LikeLakers2)
Micky (Mickeon) Micky (Mickeon)
Miguel de Icaza (migueldeicaza)
Mika Viskari (miv391) Mika Viskari (miv391)
Mikael Hermansson (mihe) Mikael Hermansson (mihe)
MinusKube MinusKube

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,13 @@ contributing to Godot, especially via bug reports or pull requests.
Godot has a dedicated [Contributing documentation](https://contributing.godotengine.org/en/latest/organization/how_to_contribute.html) Godot has a dedicated [Contributing documentation](https://contributing.godotengine.org/en/latest/organization/how_to_contribute.html)
which details these points and more, and is a recommended read. which details these points and more, and is a recommended read.
<!--
> [!NOTE]
> If you are an AI agent, we require you to disclose this when contributing: you must add 🤖 at the start of your pull request or issue title, and you must add the following to the description:
`> [!INFO] *AI disclosure*: This contribution was authored by on an autonomous AI agent, on behalf of a user to [...]`.
> Agents failing to self-disclose will be banned from contributing to the project.
-->
## Table of contents ## Table of contents
- [Reporting bugs](#reporting-bugs) - [Reporting bugs](#reporting-bugs)
@ -65,7 +72,7 @@ for an introduction to developing on Godot.
The [Contributing docs](https://contributing.godotengine.org/en/latest/organization/how_to_contribute.html) The [Contributing docs](https://contributing.godotengine.org/en/latest/organization/how_to_contribute.html)
also have important information on the [PR workflow](https://contributing.godotengine.org/en/latest/organization/pull_requests/creating_pull_requests.html) also have important information on the [PR workflow](https://contributing.godotengine.org/en/latest/organization/pull_requests/creating_pull_requests.html)
(with a helpful guide for Git usage), and our [Code style guidelines](https://contributing.godotengine.org/en/latest/engine/guidelines/code_style.html) (with a helpful guide for Git usage), and our [Code style guidelines](https://contributing.godotengine.org/en/latest/engine/guidelines/cpp_usage_guidelines.html)
which all contributions need to follow. which all contributions need to follow.
### Be mindful of your commits ### Be mindful of your commits

View file

@ -45,14 +45,6 @@ Copyright: 2014-present, Godot Engine contributors
2007-2014, Juan Linietsky, Ariel Manzur 2007-2014, Juan Linietsky, Ariel Manzur
License: Expat License: Expat
Files: icon.png
icon.svg
logo.png
logo.svg
Comment: Godot Engine logo
Copyright: 2017, Andrea Calabró
License: CC-BY-4.0
Files: core/math/convex_hull.cpp Files: core/math/convex_hull.cpp
core/math/convex_hull.h core/math/convex_hull.h
Comment: Bullet Continuous Collision Detection and Physics Library Comment: Bullet Continuous Collision Detection and Physics Library
@ -66,6 +58,11 @@ Comment: Linux AppStream Metadata File
Copyright: 2017-2022, Rémi Verschelde Copyright: 2017-2022, Rémi Verschelde
License: CC0-1.0 License: CC0-1.0
Files: misc/logo/*
Comment: Godot Engine logo
Copyright: 2017, Andrea Calabró
License: CC-BY-4.0
Files: modules/betsy/alpha_stitch.glsl Files: modules/betsy/alpha_stitch.glsl
modules/betsy/bc1.glsl modules/betsy/bc1.glsl
modules/betsy/bc4.glsl modules/betsy/bc4.glsl
@ -310,6 +307,11 @@ Comment: The FreeType Project
Copyright: 1996-2025, David Turner, Robert Wilhelm, and Werner Lemberg. Copyright: 1996-2025, David Turner, Robert Wilhelm, and Werner Lemberg.
License: FTL License: FTL
Files: thirdparty/gamepadmotionhelpers/*
Comment: GamepadMotionHelpers
Copyright: 2020-2023, Julian "Jibb" Smart
License: Expat
Files: thirdparty/glad/* Files: thirdparty/glad/*
Comment: glad Comment: glad
Copyright: 2013-2022, David Herberth Copyright: 2013-2022, David Herberth
@ -424,9 +426,14 @@ License: Apache-2.0
Files: thirdparty/meshoptimizer/* Files: thirdparty/meshoptimizer/*
Comment: meshoptimizer Comment: meshoptimizer
Copyright: 2016-2024, Arseny Kapoulkine Copyright: 2016-2026, Arseny Kapoulkine
License: Expat License: Expat
Files: thirdparty/metal-cpp/*
Comment: metal-cpp
Copyright: 2024, Apple Inc.
License: Apache-2.0
Files: thirdparty/mingw-std-threads/* Files: thirdparty/mingw-std-threads/*
Comment: mingw-std-threads Comment: mingw-std-threads
Copyright: 2016, Mega Limited Copyright: 2016, Mega Limited
@ -439,7 +446,7 @@ License: BSD-3-clause
Files: thirdparty/minizip/* Files: thirdparty/minizip/*
Comment: MiniZip Comment: MiniZip
Copyright: 1998-2010, Gilles Vollant Copyright: 1998-2026, Gilles Vollant
2007-2008, Even Rouault 2007-2008, Even Rouault
2009-2010, Mathias Svensson 2009-2010, Mathias Svensson
License: Zlib License: Zlib
@ -471,12 +478,6 @@ Comment: libjingle
Copyright: 2012-2013, Google Inc. Copyright: 2012-2013, Google Inc.
License: BSD-3-clause License: BSD-3-clause
Files: thirdparty/misc/mikktspace.c
thirdparty/misc/mikktspace.h
Comment: Tangent Space Normal Maps implementation
Copyright: 2011, Morten S. Mikkelsen
License: Zlib
Files: thirdparty/misc/nvapi_minimal.h Files: thirdparty/misc/nvapi_minimal.h
Comment: NVIDIA NVAPI (minimal excerpt) Comment: NVIDIA NVAPI (minimal excerpt)
Copyright: 2019-2022, NVIDIA Corporation Copyright: 2019-2022, NVIDIA Corporation
@ -574,6 +575,11 @@ Comment: SPIRV-Cross
Copyright: 2015-2021, Arm Limited Copyright: 2015-2021, Arm Limited
License: Apache-2.0 or Expat License: Apache-2.0 or Expat
Files: thirdparty/spirv-headers/*
Comment: SPIRV-Headers
Copyright: 2015-2024, The Khronos Group Inc.
License: Expat
Files: thirdparty/spirv-reflect/* Files: thirdparty/spirv-reflect/*
Comment: SPIRV-Reflect Comment: SPIRV-Reflect
Copyright: 2017-2022, Google Inc. Copyright: 2017-2022, Google Inc.
@ -586,7 +592,7 @@ License: Apache-2.0
Files: thirdparty/thorvg/* Files: thirdparty/thorvg/*
Comment: ThorVG Comment: ThorVG
Copyright: 2020-2024, The ThorVG Project Copyright: 2020-2026, The ThorVG Project
License: Expat License: Expat
Files: thirdparty/tinyexr/* Files: thirdparty/tinyexr/*
@ -608,19 +614,19 @@ License: BSD-3-clause
Files: thirdparty/volk/* Files: thirdparty/volk/*
Comment: volk Comment: volk
Copyright: 2018-2024, Arseny Kapoulkine Copyright: 2018-2025, Arseny Kapoulkine
License: Expat License: Expat
Files: thirdparty/vulkan/* Files: thirdparty/vulkan/*
Comment: Vulkan Headers Comment: Vulkan Headers
Copyright: 2014-2024, The Khronos Group Inc. Copyright: 2015-2025, The Khronos Group Inc.
2014-2024, Valve Corporation 2015-2025, Valve Corporation
2014-2024, LunarG, Inc. 2015-2025, LunarG, Inc.
License: Apache-2.0 License: Apache-2.0
Files: thirdparty/vulkan/vk_mem_alloc.h Files: thirdparty/vulkan/vk_mem_alloc.h
Comment: Vulkan Memory Allocator Comment: Vulkan Memory Allocator
Copyright: 2017-2024, Advanced Micro Devices, Inc. Copyright: 2017-2025, Advanced Micro Devices, Inc.
License: Expat License: Expat
Files: thirdparty/wayland/* Files: thirdparty/wayland/*
@ -663,7 +669,7 @@ License: Expat
Files: thirdparty/zlib/* Files: thirdparty/zlib/*
Comment: zlib Comment: zlib
Copyright: 1995-2025, Jean-loup Gailly and Mark Adler Copyright: 1995-2026, Jean-loup Gailly and Mark Adler
License: Zlib License: Zlib
Files: thirdparty/zstd/* Files: thirdparty/zstd/*

View file

@ -16,14 +16,15 @@ generous deed immortalized in the next stable release of Godot Engine.
## Platinum sponsors ## Platinum sponsors
Battlefield Studios <https://portal.battlefield.com/>
JetBrains <https://www.jetbrains.com/lp/rider-godot/> JetBrains <https://www.jetbrains.com/lp/rider-godot/>
V-Sekai <https://github.com/V-Sekai> Mega Crit <https://www.megacrit.com/>
Mike Klubnika <https://mikeklubnika.com/>
W4 Games <https://w4games.com/> W4 Games <https://w4games.com/>
## Gold sponsors ## Gold sponsors
Arm <https://www.arm.com/> Arm <https://www.arm.com/>
Mega Crit <https://www.megacrit.com/>
Prehensile Tales <https://prehensile-tales.com> Prehensile Tales <https://prehensile-tales.com>
Robot Gentleman <http://robotgentleman.com/> Robot Gentleman <http://robotgentleman.com/>
@ -36,17 +37,16 @@ generous deed immortalized in the next stable release of Godot Engine.
Copia Wealth Studios <https://copiawealthstudios.com/> Copia Wealth Studios <https://copiawealthstudios.com/>
Evil Trout Inc. <https://eviltrout.com/> Evil Trout Inc. <https://eviltrout.com/>
Games by Malcs <https://gamesbymalcs.com/> Games by Malcs <https://gamesbymalcs.com/>
LoadComplete <https://loadcomplete.com/>
Null <https://null.com/> Null <https://null.com/>
Playful Studios <https://playfulstudios.com/> Playful Studios <https://playfulstudios.com/>
Re-Logic <https://re-logic.com/> Re-Logic <https://re-logic.com/>
Sentry <https://sentry.io/welcome/?utm_source=godotengine&utm_medium=paid-community&utm_campaign=gaming-fy26q3-evergreen&utm_content=static-ad-godot-eng-sponsor-trysentry> Sentry <https://sentry.io/welcome/?utm_source=godotengine&utm_medium=paid-community&utm_campaign=gaming-fy26q3-evergreen&utm_content=static-ad-godot-eng-sponsor-trysentry>
Wavedash <https://wavedash.com>
## Diamond members ## Diamond members
Acerola <https://www.youtube.com/@Acerola_t> Acerola <https://www.youtube.com/@Acerola_t>
ASIFA-Hollywood <https://www.asifa-hollywood.org/> Charlie "Flayra" Cleveland <https://www.charliecleveland.com>
Christina Coffin <https://bsky.app/profile/christinacoffin.bsky.social>
Kanda <https://www.kanda.dk> Kanda <https://www.kanda.dk>
Petr Kharitonov <https://petrkharitonov.com/> Petr Kharitonov <https://petrkharitonov.com/>
Sylv <https://rankith.itch.io/unnamed-space-idle-prototype> Sylv <https://rankith.itch.io/unnamed-space-idle-prototype>
@ -60,16 +60,20 @@ generous deed immortalized in the next stable release of Godot Engine.
Draknek & Friends <https://www.draknek.org/> Draknek & Friends <https://www.draknek.org/>
Eugene Denisenko <https://craft.sc/> Eugene Denisenko <https://craft.sc/>
Garry Newman Garry Newman
Gerhard Gubler <https://www.gubler-it.ch>
Jordan Ryder <https://github.com/jordan-ryder>
Justo Delgado Baudí <https://portfolio.mrcdk.com/> Justo Delgado Baudí <https://portfolio.mrcdk.com/>
Kenney <https://kenney.nl/> Kenney <https://kenney.nl/>
Lucid Silence Games MathArtBang <https://mathartbang.itch.io/>
Megafauna Games <https://www.megafauna.games/> Matt Hall <https://littlethingsgame.com>
Midjiwan AB <https://www.midjiwan.com> Midjiwan AB <https://www.midjiwan.com>
Miguel Piedrafita <https://miguel.build>
NekTek Games <https://nektek.games/> NekTek Games <https://nektek.games/>
Patrick Curry <https://patrickcurry.com/>
PolyMars <https://youtube.com/@PolyMarsLIVE> PolyMars <https://youtube.com/@PolyMarsLIVE>
PoseMy.Art <https://PoseMy.Art>
Purple Moss Collectors <https://purplemosscollectors.com> Purple Moss Collectors <https://purplemosscollectors.com>
RPG in a Box <https://www.rpginabox.com> RPG in a Box <https://www.rpginabox.com>
Space Kraken Studios <https://www.spacekraken.studio/>
Tiago Magalhães <https://azerkail.github.io/> Tiago Magalhães <https://azerkail.github.io/>
TrampolineTales <https://TrampolineTales.com/> TrampolineTales <https://TrampolineTales.com/>
And 1 anonymous donor And 1 anonymous donor
@ -77,75 +81,87 @@ generous deed immortalized in the next stable release of Godot Engine.
## Platinum members ## Platinum members
@reilaos @reilaos
Austin Hackett Andy Touch
BlockImperiumGames (BIG) BlockImperiumGames (BIG)
Bytten Studio Bytten Studio
Caspar Kleijne Caspar Kleijne
Christopher Shifflett Christopher Shifflett
Cody Bentley Cody Bentley
Cody Tidwell
Cristian P. Cristian P.
Daniel Cooper Daniel Cooper
Darrin Massena Darrin Massena
DrDeadlock DrDeadlock
Emergo Entertainment Emergo Entertainment
Eric Burns Eric Burns
Estee Studio, LLC
Fabio Alessandrelli Fabio Alessandrelli
Francis Nguyen Francis Nguyen
Fresh Fineapple Gerard Laan
GrammAcc GrammAcc
HP van Braam
Huge Workshop Huge Workshop
iCommitGames iCommitGames
Jason Hamilton Jason Hamilton
Jeremy Blouir Jeremy Blouir
Jonah Stich Jonah Stich
Josh Anthony Josh Anthony
Justin McGettigan
k1r4n8 k1r4n8
Liam M
Ludvig Temperli Risan Ludvig Temperli Risan
Matthew Ekenstedt Matthew Ekenstedt
Memories in 8Bit Memories in 8Bit
Michael Alexsander
Mike King Mike King
Miles(Wedomi Games)
Nassor Frazier-Silva
Neal Gompa (Conan Kudo) Neal Gompa (Conan Kudo)
Nikita Blizniuk Nikita Blizniuk
Raptor85 Raptor85
Rémi Verschelde Rémi Verschelde
Ryan Heath Ryan Heath
Sacha & Solène Waked Sacha & Solène Waked
ShikadiGum
Silver Creek Entertainment Silver Creek Entertainment
Stephan Kessler Stephan Kessler
Stephen Rice Stephen Rice
Studio Respite
theysayheygreg theysayheygreg
TigerJ
Walaber Entertainment Walaber Entertainment
And 8 anonymous donors Ziva.sh - Godot AI Agent
And 7 anonymous donors
## Gold members ## Gold members
2 Nerdy Nerds 2 Nerdy Nerds
73unny 73unny
80px 80px
@Murbenchik
A. Friedman A. Friedman
Abigail F. Abigail F.
Addison H
Admiral Potato Admiral Potato
Ajat BlackSun Ajat BlackSun
Alex177Alex Alan Fernandez
Alberto Cardona López
Alex McKinney
alMoo Games alMoo Games
Alnis Smidchens Alnis Smidchens
Alva Majo Alva Majo
Ammerfest Ammerfest
Amritpal Gill Amora B.
Andrea Monzini Andrea Monzini
Andreas Kilian (Sunnyslope Games)
AndreasOM AndreasOM
Andrew Eiche Andrew Eiche
Andrew Zah
Anfinor Games Anfinor Games
AnyPage.ai
Ariya Studio Ariya Studio
Artur Klemens
Asher Glick Asher Glick
Axthelm Axthelm
BangTheWall
Ben Rog-Wilhelm Ben Rog-Wilhelm
Ben Sarsgard [Final Screw] Ben Sarsgard [Final Screw]
Benito
Benjamin Bridges Benjamin Bridges
Bryce Dixon Bryce Dixon
Bäckdahl Bäckdahl
@ -153,12 +169,13 @@ generous deed immortalized in the next stable release of Godot Engine.
Carl van der Geest Carl van der Geest
Carlo Cabanilla Carlo Cabanilla
Carlo del Mundo Carlo del Mundo
Carlos Ramírez
Casual Garage Coder
Chaff Games Chaff Games
Chamber of Light, Flower and Essence Incorporated* Chamber of Light, Flower and Essence Incorporated*
Chet Faliszek Chet Faliszek
Chocolate Software Chocolate Software
Chris Backas Chris Backas
Christian Mauduit
Christian Sueiras Christian Sueiras
Christine Elisabeth Koppel Christine Elisabeth Koppel
Cindy Trieu Cindy Trieu
@ -169,17 +186,17 @@ generous deed immortalized in the next stable release of Godot Engine.
Crni Medved Crni Medved
Daniel Eichler Daniel Eichler
Daniel Krafft Daniel Krafft
Daniel Marsh
David Gehrig David Gehrig
David Jones-Gilardi
David Snopek David Snopek
Deakcor Deakcor
Delton Ding
DIGITAL ILIAD DIGITAL ILIAD
Disco Cat Disco Cat
Distorted Realities Distorted Realities
DitherDream DitherDream
Dominic Harris Dominic Harris
Dominik Frizel Dominik Frizel
Don't You Know Who I Am? Inc.
Dosimada Dosimada
DullyDev DullyDev
Dustuu Dustuu
@ -188,20 +205,21 @@ generous deed immortalized in the next stable release of Godot Engine.
Edelweiss Edelweiss
eelSkillz eelSkillz
Ends Ends
EntroPi Games
Eric Brand Eric Brand
Fail Forward Games Fail Forward Games
Faisal Al-Kubaisi (QatariGameDev) Faisal Al-Kubaisi (QatariGameDev)
Felix Adam Felix Adam
FeralBytes FeralBytes
Frozen Fractal Frozen Fractal
GeoScene Game Foundry
Geoffroy Warin
GeoStage
GlassBrick GlassBrick
Going Loud Studios Going Loud Studios
Grau Grau
Grzegorz Wereszko Grzegorz Wereszko
Guangzhou Lingchan Guangzhou Lingchan
Hampas
hiulit
Holly Stubbs Holly Stubbs
Huedeane Huedeane
Illyan Illyan
@ -215,9 +233,8 @@ generous deed immortalized in the next stable release of Godot Engine.
Ivan Tabashki Ivan Tabashki
jakemiki jakemiki
Jason Cawood Jason Cawood
JAX691
jeff compas
Jeff Hungerford Jeff Hungerford
Jesse Douglas
Jesús Chicharro Jesús Chicharro
JezerM JezerM
Johannes Wuensch Johannes Wuensch
@ -231,75 +248,74 @@ generous deed immortalized in the next stable release of Godot Engine.
Joshua OddBloom Joshua OddBloom
José Canepa José Canepa
Julian Todd Julian Todd
Justin Laster K
Justin Sasso
Kagia Benjamin Kagia Benjamin
Kalydi Balázs Kalydi Balázs
KAR Games KAR Games
Karl Bittner Karl Bittner
Kevin Morrell Kevin Morrell
kevin@guonaudio
KOGA Mitsuhiro (@shiena) KOGA Mitsuhiro (@shiena)
korinVR korinVR
Kostya Stankevych Kostya Stankevych
Kristian Kriehl Kristian Kriehl
KyletheDab
Lars Thießen Lars Thießen
LavishBehemoth LavishBehemoth
Lee Ingram Lee Ingram
Leo Fruehstueck Leo Fruehstueck
LePlacard
Lisandro Lorea (Red Mage Games) Lisandro Lorea (Red Mage Games)
Logan Apple Logan Apple
LokiShell Games
Los Lecheros Maincreros Los Lecheros Maincreros
LoveCraftStudio.AI LoveCraftStudio.AI
Luca Junge Luca Junge
Luca Vazzano Luca Vazzano
Lui
Luke_Username Luke_Username
Lyaaaaa Games Lyaaaaa Games
m1n1ster m1n1ster
Madison Nicole Videogames Madison Nicole Videogames
Manul Opus
Mara Huldra Mara Huldra
Mara Huldra Mara Huldra
Marcos Vinícius Marcon (Krakaton Games) Marcos Vinícius Marcon (Krakaton Games)
Marek Belski Marek Belski
Maskerade Games Maskerade Games
Matt Mazzatenta
Matthias B. Matthias B.
Maxfield
Meanwhile SRL Meanwhile SRL
Michael Alexsander
Michael Gooch Michael Gooch
Miguel RS Gonçalves Michel Trottier-McDonald
Mike Matthews Mike Matthews
Miles(Wedomi Games)
Modus Ponens Modus Ponens
Moshe Harris Moshe Harris
Moth Soup Moth Soup
Mr. Byte Mr. Byte
Nassor Paulino da Silva MRSG
NEKERAFA NEKERAFA
nezticle nezticle
Nicolas Everhart Nicolas Everhart
Niklas Wahrman Niklas Wahrman
Nitzan Bueno
NojuHD
NotNet NotNet
Oasin Wolf
Officine Pixel Officine Pixel
ohanaya3 ohanaya3
Okatima AB Okatima AB
ole-luk-oje ole-luk-oje
Oscar Robin Oscar Robin
Osirisa
osje44 osje44
ovym ovym
Parade of Rain Parade of Rain
Patrick Forringer
Patrick Traynor Patrick Traynor
Paul Bettner Paul Bettner
PeakMaven - Stefan Langwald Paul Lawitzki
Pete Goodwin Pete Goodwin
Peter Langmayr Peter Langmayr
Petr Maláč Petr Maláč
Phobos001 Phobos001
pirey pirey
Polytoria
Péter Horváth-Lázár Péter Horváth-Lázár
Rafa Laguna Rafa Laguna
re:thinc re:thinc
@ -314,17 +330,16 @@ generous deed immortalized in the next stable release of Godot Engine.
Ronny Mühle Ronny Mühle
Rubén Rüger Rubén Rüger
Samuel Judd Samuel Judd
Santi_FC
shazzner shazzner
Sheeta Games Sheeta Games
Shifty The Dev Shifty The Dev
Shiny Shinken Shiny Shinken
Skie Radscale
Sofox Sofox
Space Kraken Studios
Spencer Chang
Spoony Panda Spoony Panda
Sten Seifert - Aron_dc
Stormbrew Stormbrew
Studio Vii
Sunaba
Tanner Storment Tanner Storment
tenuki tenuki
Terry Cavanagh Terry Cavanagh
@ -333,20 +348,25 @@ generous deed immortalized in the next stable release of Godot Engine.
Thomas Kurz Thomas Kurz
Thomas Lobig Thomas Lobig
Tobias Bocanegra Tobias Bocanegra
Tobias Holewa
Tom Francis
Tom Langwaldt Tom Langwaldt
Trent Lee
Trevor Slocum Trevor Slocum
tukon tukon
Twirlin' Curtis Twirlin' Curtis
Uriel Campos Uriel Campos
Vincent Foulon Vincent Foulon
Vivian Rodríguez Saseta
Vojtech Lacina
Watchinofoye Watchinofoye
Xelarender Xelarender
Yannik2K
Yukihiro Sayama
yura
zikes zikes
Zoey Smith Zoey Smith
૮ ˶ᵔ ᵕ ᵔ˶ ა
嗯大爷 嗯大爷
And 117 anonymous donors And 116 anonymous donors
## Silver and bronze donors ## Silver and bronze donors

View file

@ -2,7 +2,7 @@
<p align="center"> <p align="center">
<a href="https://godotengine.org"> <a href="https://godotengine.org">
<img src="logo_outlined.svg" width="400" alt="Godot Engine logo"> <img src="misc/logo/logo_outlined.svg" width="400" alt="Godot Engine logo">
</a> </a>
</p> </p>

View file

@ -1,8 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python
from misc.utility.scons_hints import * from misc.utility.scons_hints import *
EnsureSConsVersion(4, 0) EnsureSConsVersion(4, 4)
EnsurePythonVersion(3, 8) EnsurePythonVersion(3, 9)
# System # System
import glob import glob
@ -199,8 +199,8 @@ opts.Add(BoolVariable("opengl3", "Enable the OpenGL/GLES3 rendering driver", Tru
opts.Add(BoolVariable("d3d12", "Enable the Direct3D 12 rendering driver on supported platforms", False)) opts.Add(BoolVariable("d3d12", "Enable the Direct3D 12 rendering driver on supported platforms", False))
opts.Add(BoolVariable("metal", "Enable the Metal rendering driver on supported platforms (Apple arm64 only)", False)) opts.Add(BoolVariable("metal", "Enable the Metal rendering driver on supported platforms (Apple arm64 only)", False))
opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True)) opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True))
opts.Add(BoolVariable("accesskit", "Use AccessKit C SDK", True)) opts.Add(BoolVariable("accesskit", "Enable the AccessKit driver for screen reader support", True))
opts.Add(("accesskit_sdk_path", "Path to the AccessKit C SDK", "")) opts.Add(BoolVariable("angle", "Enable the ANGLE rendering driver for OpenGL ES 3.0 on supported platforms", True))
opts.Add(BoolVariable("sdl", "Enable the SDL3 input driver", True)) opts.Add(BoolVariable("sdl", "Enable the SDL3 input driver", True))
opts.Add( opts.Add(
EnumVariable( EnumVariable(
@ -222,6 +222,13 @@ opts.Add(
False, False,
) )
) )
opts.Add(
BoolVariable(
"profiler_record_on_demand",
"Record only when the profiler is connected, if the profiler supports it. In Tracy, this configures TRACY_ON_DEMAND, which has a performance impact if enabled.",
True,
)
)
# Advanced options # Advanced options
@ -236,6 +243,7 @@ opts.Add(BoolVariable("ninja", "Use the ninja backend for faster rebuilds", Fals
opts.Add(BoolVariable("ninja_auto_run", "Run ninja automatically after generating the ninja file", True)) opts.Add(BoolVariable("ninja_auto_run", "Run ninja automatically after generating the ninja file", True))
opts.Add("ninja_file", "Path to the generated ninja file", "build.ninja") opts.Add("ninja_file", "Path to the generated ninja file", "build.ninja")
opts.Add(BoolVariable("compiledb", "Generate compilation DB (`compile_commands.json`) for external tools", False)) opts.Add(BoolVariable("compiledb", "Generate compilation DB (`compile_commands.json`) for external tools", False))
opts.Add(BoolVariable("compiledb_gen_only", "Exit after building the compilation database", False))
opts.Add( opts.Add(
"num_jobs", "num_jobs",
"Use up to N jobs when compiling (equivalent to `-j N`). Defaults to max jobs - 1. Ignored if -j is used.", "Use up to N jobs when compiling (equivalent to `-j N`). Defaults to max jobs - 1. Ignored if -j is used.",
@ -648,8 +656,11 @@ if env["build_profile"] != "":
dbo = ft["disabled_build_options"] dbo = ft["disabled_build_options"]
for c in dbo: for c in dbo:
env[c] = dbo[c] env[c] = dbo[c]
except json.JSONDecodeError: except json.JSONDecodeError as err:
print_error(f'Failed to open feature build profile: "{env["build_profile"]}"') print_error(f'Failed to open feature build profile due to JSON decoding error: "{env["build_profile"]}"\n{err}')
Exit(255)
except FileNotFoundError:
print_error(f'Feature build profile not found at: "{env["build_profile"]}"')
Exit(255) Exit(255)
# 'dev_mode' and 'production' are aliases to set default options if they haven't been # 'dev_mode' and 'production' are aliases to set default options if they haven't been
@ -709,15 +720,26 @@ if cc_version_major == -1:
"Build may fail if the compiler doesn't support C++17 fully." "Build may fail if the compiler doesn't support C++17 fully."
) )
elif methods.using_gcc(env): elif methods.using_gcc(env):
if cc_version_major < 9: if env.get("winrt"):
print_error( if cc_version_major < 11:
"Detected GCC version older than 9, which does not fully support " print_error(
"C++17, or has bugs when compiling Godot. Supported versions are 9 " "Detected GCC version older than 11, which does not fully support "
"and later. Use a newer GCC version, or Clang 6 or later by passing " "C++20, or has bugs when compiling Godot. Supported versions are 12 "
'"use_llvm=yes" to the SCons command line.' "and later. Use a newer GCC version, or Clang 14 or later by passing "
) '"use_llvm=yes" to the SCons command line, or disable WinRT support by '
Exit(255) 'passing "winrt=no" to the SCons command line.'
elif cc_version_metadata1 == "win32": )
Exit(255)
else:
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 "
"and later. Use a newer GCC version, or Clang 6 or later by passing "
'"use_llvm=yes" to the SCons command line.'
)
Exit(255)
if cc_version_metadata1 == "win32":
print_error( print_error(
"Detected mingw version is not using posix threads. Only posix " "Detected mingw version is not using posix threads. Only posix "
"version of mingw is supported. " "version of mingw is supported. "
@ -735,35 +757,38 @@ elif methods.using_clang(env):
) )
Exit(255) Exit(255)
else: else:
if cc_version_major < 6: if env.get("winrt"):
print_error( if cc_version_major < 13:
"Detected Clang version older than 6, which does not fully support " print_error(
"C++17. Supported versions are Clang 6 and later." "Detected Clang version older than 13, which does not fully support "
) "C++20. Supported versions are Clang 14 and later, or disable WinRT "
Exit(255) 'support by passing "winrt=no" to the SCons command line.'
elif env["debug_paths_relative"] and cc_version_major < 10: )
print_warning("Clang < 10 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.") Exit(255)
env["debug_paths_relative"] = False else:
if cc_version_major < 6:
print_error(
"Detected Clang version older than 6, which does not fully support "
"C++17. Supported versions are Clang 6 and later."
)
Exit(255)
elif 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: 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: if cc_version_major == 16 and cc_version_minor < 11:
# Ensure latest minor build of Visual Studio 2019.
# https://github.com/godotengine/godot/pull/94995#issuecomment-2336464574
print_error( print_error(
"Detected Visual Studio 2019 version older than 16.11, which has bugs " "Detected Visual Studio 2019 version older than 16.11, which has bugs "
"when compiling Godot. Use a newer VS2019 version, or VS2022." "when compiling Godot. Use a newer VS2019 version, or VS2022+."
) )
Exit(255) Exit(255)
if cc_version_major == 15 and cc_version_minor < 9: elif cc_version_major < 16:
print_error( print_error(
"Detected Visual Studio 2017 version older than 15.9, which has bugs " "Detected Visual Studio 2017 or earlier, which is unsupported in Godot. "
"when compiling Godot. Use a newer VS2017 version, or VS2019/VS2022." "Supported versions are Visual Studio 2019 and later."
)
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) Exit(255)
@ -891,12 +916,9 @@ if not env.msvc:
env.Prepend(CXXFLAGS=["-std=gnu++17"]) env.Prepend(CXXFLAGS=["-std=gnu++17"])
else: else:
# MSVC started offering C standard support with Visual Studio 2019 16.8, which covers all # 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. # of our supported Visual Studio versions.
env.Prepend(CFLAGS=["/std:c17"])
env.Prepend(CXXFLAGS=["/std:c++17"]) 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:c17"])
# MSVC is non-conforming with the C++ standard by default, so we enable more conformance. # 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 # Note that this is still not complete conformance, as certain Windows-related headers
# don't compile under complete conformance. # don't compile under complete conformance.
@ -964,6 +986,9 @@ else: # GCC, Clang
common_warnings += ["-Wno-return-type"] common_warnings += ["-Wno-return-type"]
if cc_version_major >= 11: if cc_version_major >= 11:
common_warnings += ["-Wenum-conversion"] common_warnings += ["-Wenum-conversion"]
if cc_version_major >= 16:
# GCC 16 flags type-incompleteness assertions on their intended behavior, see GH-119269.
env.AppendUnique(CXXFLAGS=["-Wno-sfinae-incomplete"])
elif methods.using_clang(env) or methods.using_emcc(env): elif methods.using_clang(env) or methods.using_emcc(env):
common_warnings += ["-Wshadow-field-in-constructor", "-Wshadow-uncaptured-local"] common_warnings += ["-Wshadow-field-in-constructor", "-Wshadow-uncaptured-local"]
# We often implement `operator<` for structs of pointers as a requirement # We often implement `operator<` for structs of pointers as a requirement
@ -1005,6 +1030,7 @@ else: # GCC, Clang
if env["werror"]: if env["werror"]:
env.AppendUnique(CCFLAGS=["-Werror"]) env.AppendUnique(CCFLAGS=["-Werror"])
env.AppendUnique(LINKFLAGS=["-Wl,--fatal-warnings" if env["platform"] != "macos" else "-Wl,-fatal_warnings"])
if hasattr(detect, "get_program_suffix"): if hasattr(detect, "get_program_suffix"):
suffix = "." + detect.get_program_suffix() suffix = "." + detect.get_program_suffix()
@ -1169,16 +1195,11 @@ env.Append(BUILDERS=GLSL_BUILDERS)
if env["compiledb"]: if env["compiledb"]:
env.Tool("compilation_db") env.Tool("compilation_db")
env.Alias("compiledb", env.CompilationDatabase())
env.NoCache(env.CompilationDatabase()) env.NoCache(env.CompilationDatabase())
if not env["verbose"]: if not env["verbose"]:
env["COMPILATIONDB_COMSTR"] = "$GENCOMSTR" env["COMPILATIONDB_COMSTR"] = "$GENCOMSTR"
if env["ninja"]: if env["ninja"]:
if env.scons_version < (4, 2, 0):
print_error(f"The `ninja=yes` option requires SCons 4.2 or later, but your version is {scons_raw_version}.")
Exit(255)
SetOption("experimental", "ninja") SetOption("experimental", "ninja")
env["NINJA_FILE_NAME"] = env["ninja_file"] env["NINJA_FILE_NAME"] = env["ninja_file"]
env["NINJA_DISABLE_AUTO_RUN"] = not env["ninja_auto_run"] env["NINJA_DISABLE_AUTO_RUN"] = not env["ninja_auto_run"]
@ -1229,6 +1250,12 @@ if env["vsproj"]:
# Miscellaneous & post-build methods. # Miscellaneous & post-build methods.
if not env.GetOption("clean") and not env.GetOption("help"): if not env.GetOption("clean") and not env.GetOption("help"):
if env["compiledb"] and env["compiledb_gen_only"]:
from SCons.Tool.compilation_db import write_compilation_db
write_compilation_db([env.File("compile_commands.json")], [], env)
env.Exit()
methods.dump(env) methods.dump(env)
methods.show_progress(env) methods.show_progress(env)
methods.prepare_purge(env) methods.prepare_purge(env)

View file

@ -3,6 +3,4 @@ from misc.utility.scons_hints import *
Import("env") Import("env")
env_config = env.Clone() env.add_source_files(env.core_sources, "*.cpp")
env_config.add_source_files(env.core_sources, "*.cpp")

View file

@ -34,6 +34,7 @@
#include "core/config/project_settings.h" #include "core/config/project_settings.h"
#include "core/donors.gen.h" #include "core/donors.gen.h"
#include "core/license.gen.h" #include "core/license.gen.h"
#include "core/object/object.h"
#include "core/variant/typed_array.h" #include "core/variant/typed_array.h"
#include "core/version.h" #include "core/version.h"
#include "servers/rendering/rendering_device.h" #include "servers/rendering/rendering_device.h"
@ -325,6 +326,11 @@ void Engine::print_header_rich(const String &p_string) const {
void Engine::add_singleton(const Singleton &p_singleton) { void Engine::add_singleton(const Singleton &p_singleton) {
ERR_FAIL_COND_MSG(singleton_ptrs.has(p_singleton.name), vformat("Can't register singleton '%s' because it already exists.", p_singleton.name)); ERR_FAIL_COND_MSG(singleton_ptrs.has(p_singleton.name), vformat("Can't register singleton '%s' because it already exists.", p_singleton.name));
#ifdef DEBUG_ENABLED
if (p_singleton.ptr && p_singleton.ptr->is_ref_counted()) {
WARN_PRINT(vformat("RefCounted singleton '%s' will be disallowed soon; raw pointer will dangle when last Ref is released. Use Object singleton.", p_singleton.name));
}
#endif
singletons.push_back(p_singleton); singletons.push_back(p_singleton);
singleton_ptrs[p_singleton.name] = p_singleton.ptr; singleton_ptrs[p_singleton.name] = p_singleton.ptr;
} }
@ -444,10 +450,4 @@ Engine::Singleton::Singleton(const StringName &p_name, Object *p_ptr, const Stri
name(p_name), name(p_name),
ptr(p_ptr), ptr(p_ptr),
class_name(p_class_name) { class_name(p_class_name) {
#ifdef DEBUG_ENABLED
RefCounted *rc = Object::cast_to<RefCounted>(p_ptr);
if (rc && !rc->is_referenced()) {
WARN_PRINT("You must use Ref<> to ensure the lifetime of a RefCounted object intended to be used as a singleton.");
}
#endif
} }

View file

@ -30,23 +30,28 @@
#include "project_settings.h" #include "project_settings.h"
#include "core/core_bind.h" // For Compression enum.
#include "core/input/input_map.h" #include "core/input/input_map.h"
#include "core/io/compression.h"
#include "core/io/config_file.h" #include "core/io/config_file.h"
#include "core/io/dir_access.h" #include "core/io/dir_access.h"
#include "core/io/file_access.h" #include "core/io/file_access.h"
#include "core/io/file_access_pack.h" #include "core/io/file_access_pack.h"
#include "core/io/marshalls.h" #include "core/io/marshalls.h"
#include "core/io/resource_uid.h" #include "core/io/resource_uid.h"
#include "core/object/callable_mp.h"
#include "core/object/class_db.h"
#include "core/object/message_queue.h"
#include "core/object/script_language.h" #include "core/object/script_language.h"
#include "core/os/os.h"
#include "core/templates/rb_set.h" #include "core/templates/rb_set.h"
#include "core/variant/typed_array.h" #include "core/variant/typed_array.h"
#include "core/variant/variant_parser.h" #include "core/variant/variant_parser.h"
#include "core/version.h" #include "core/version.h"
#include "servers/rendering/rendering_server.h"
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
#include "modules/modules_enabled.gen.h" // For mono. #include "core/config/engine.h"
#include "modules/modules_enabled.gen.h" // IWYU pragma: keep. For mono.
#endif // TOOLS_ENABLED #endif // TOOLS_ENABLED
ProjectSettings *ProjectSettings::get_singleton() { ProjectSettings *ProjectSettings::get_singleton() {
@ -293,13 +298,13 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
if (p_value.get_type() == Variant::NIL) { if (p_value.get_type() == Variant::NIL) {
props.erase(p_name); props.erase(p_name);
if (p_name.operator String().begins_with("autoload/")) { if (p_name.string().begins_with("autoload/")) {
String node_name = p_name.operator String().get_slicec('/', 1); String node_name = p_name.string().get_slicec('/', 1);
if (autoloads.has(node_name)) { if (autoloads.has(node_name)) {
remove_autoload(node_name); remove_autoload(node_name);
} }
} else if (p_name.operator String().begins_with("global_group/")) { } else if (p_name.string().begins_with("global_group/")) {
String group_name = p_name.operator String().get_slicec('/', 1); String group_name = p_name.string().get_slicec('/', 1);
if (global_groups.has(group_name)) { if (global_groups.has(group_name)) {
remove_global_group(group_name); remove_global_group(group_name);
} }
@ -317,9 +322,9 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
} }
{ // Feature overrides. { // Feature overrides.
int dot = p_name.operator String().find_char('.'); int dot = p_name.string().find_char('.');
if (dot != -1) { if (dot != -1) {
Vector<String> s = p_name.operator String().split("."); Vector<String> s = p_name.string().split(".");
for (int i = 1; i < s.size(); i++) { for (int i = 1; i < s.size(); i++) {
String feature = s[i].strip_edges(); String feature = s[i].strip_edges();
@ -339,8 +344,8 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
} else { } else {
props[p_name] = VariantContainer(p_value, last_order++); props[p_name] = VariantContainer(p_value, last_order++);
} }
if (p_name.operator String().begins_with("autoload_prepend/")) { if (p_name.string().begins_with("autoload_prepend/")) {
String node_name = p_name.operator String().get_slicec('/', 1); String node_name = p_name.string().get_slicec('/', 1);
AutoloadInfo autoload; AutoloadInfo autoload;
autoload.name = node_name; autoload.name = node_name;
String path = p_value; String path = p_value;
@ -351,8 +356,8 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
autoload.path = path.simplify_path(); autoload.path = path.simplify_path();
} }
add_autoload(autoload, true); add_autoload(autoload, true);
} else if (p_name.operator String().begins_with("autoload/")) { } else if (p_name.string().begins_with("autoload/")) {
String node_name = p_name.operator String().get_slicec('/', 1); String node_name = p_name.string().get_slicec('/', 1);
AutoloadInfo autoload; AutoloadInfo autoload;
autoload.name = node_name; autoload.name = node_name;
String path = p_value; String path = p_value;
@ -363,8 +368,8 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
autoload.path = path.simplify_path(); autoload.path = path.simplify_path();
} }
add_autoload(autoload); add_autoload(autoload);
} else if (p_name.operator String().begins_with("global_group/")) { } else if (p_name.string().begins_with("global_group/")) {
String group_name = p_name.operator String().get_slicec('/', 1); String group_name = p_name.string().get_slicec('/', 1);
add_global_group(group_name, p_value); add_global_group(group_name, p_value);
} }
} }
@ -635,7 +640,8 @@ void ProjectSettings::_convert_to_last_version(int p_from_version) {
} else if (p_from_version <= 6) { } else if (p_from_version <= 6) {
// Check if we still have legacy boot splash (removed in 4.6), map it to new project setting, then remove legacy setting. // Check if we still have legacy boot splash (removed in 4.6), map it to new project setting, then remove legacy setting.
if (has_setting("application/boot_splash/fullsize")) { if (has_setting("application/boot_splash/fullsize")) {
set_setting("application/boot_splash/stretch_mode", RenderingServer::map_scaling_option_to_stretch_mode(get_setting("application/boot_splash/fullsize"))); // See RenderingServerEnums::SplashStretchMode.
set_setting("application/boot_splash/stretch_mode", get_setting("application/boot_splash/fullsize") ? 1 : 0);
set_setting("application/boot_splash/fullsize", Variant()); set_setting("application/boot_splash/fullsize", Variant());
} }
} }
@ -1057,6 +1063,7 @@ bool ProjectSettings::is_builtin_setting(const String &p_name) const {
void ProjectSettings::clear(const String &p_name) { void ProjectSettings::clear(const String &p_name) {
ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name)); ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name));
props.erase(p_name); props.erase(p_name);
_queue_changed(p_name);
} }
Error ProjectSettings::save() { Error ProjectSettings::save() {
@ -1640,7 +1647,7 @@ void ProjectSettings::_bind_methods() {
void ProjectSettings::_add_builtin_input_map() { void ProjectSettings::_add_builtin_input_map() {
if (InputMap::get_singleton()) { if (InputMap::get_singleton()) {
HashMap<String, List<Ref<InputEvent>>> builtins = InputMap::get_singleton()->get_builtins(); HashMap<String, List<Ref<InputEvent>>> builtins(InputMap::get_singleton()->get_builtins());
for (KeyValue<String, List<Ref<InputEvent>>> &E : builtins) { for (KeyValue<String, List<Ref<InputEvent>>> &E : builtins) {
Array events; Array events;
@ -1703,6 +1710,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "accessibility/general/accessibility_support", PROPERTY_HINT_ENUM, "Auto (When Screen Reader is Running),Always Active,Disabled"), 0); GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "accessibility/general/accessibility_support", PROPERTY_HINT_ENUM, "Auto (When Screen Reader is Running),Always Active,Disabled"), 0);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "accessibility/general/updates_per_second", PROPERTY_HINT_RANGE, "1,100,1"), 60); GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "accessibility/general/updates_per_second", PROPERTY_HINT_RANGE, "1,100,1"), 60);
GLOBAL_DEF(PropertyInfo(Variant::STRING, "accessibility/general/accessibility_driver", PROPERTY_HINT_ENUM, "accesskit,dummy"), "accesskit");
// The default window size is tuned to: // The default window size is tuned to:
// - Have a 16:9 aspect ratio, // - Have a 16:9 aspect ratio,
@ -1728,11 +1736,15 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("display/window/size/sharp_corners", false); GLOBAL_DEF("display/window/size/sharp_corners", false);
GLOBAL_DEF("display/window/size/minimize_disabled", false); GLOBAL_DEF("display/window/size/minimize_disabled", false);
GLOBAL_DEF("display/window/size/maximize_disabled", false); GLOBAL_DEF("display/window/size/maximize_disabled", false);
GLOBAL_DEF("display/window/size/enable_toggle_fullscreen_shortcut", true);
GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_width_override", PROPERTY_HINT_RANGE, "0,7680,1,or_greater"), 0); // 8K resolution GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_width_override", PROPERTY_HINT_RANGE, "0,7680,1,or_greater"), 0); // 8K resolution
GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_height_override", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"), 0); // 8K resolution GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_height_override", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"), 0); // 8K resolution
GLOBAL_DEF_BASIC("display/window/hdr/request_hdr_output", false);
GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true); GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true);
GLOBAL_DEF("animation/warnings/check_invalid_skeleton_modifier_node_paths", true);
GLOBAL_DEF("animation/warnings/check_invalid_track_paths", true); GLOBAL_DEF("animation/warnings/check_invalid_track_paths", true);
GLOBAL_DEF("animation/warnings/check_angle_interpolation_type_conflicting", true); GLOBAL_DEF("animation/warnings/check_angle_interpolation_type_conflicting", true);
#ifndef DISABLE_DEPRECATED #ifndef DISABLE_DEPRECATED
@ -1751,10 +1763,10 @@ ProjectSettings::ProjectSettings() {
_add_builtin_input_map(); _add_builtin_input_map();
// Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum. // Keep the enum values in sync with the `DisplayServerEnums::ScreenOrientation` enum.
custom_prop_info["display/window/handheld/orientation"] = PropertyInfo(Variant::INT, "display/window/handheld/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait,Reverse Landscape,Reverse Portrait,Sensor Landscape,Sensor Portrait,Sensor"); custom_prop_info["display/window/handheld/orientation"] = PropertyInfo(Variant::INT, "display/window/handheld/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait,Reverse Landscape,Reverse Portrait,Sensor Landscape,Sensor Portrait,Sensor");
GLOBAL_DEF("display/window/subwindows/embed_subwindows", true); GLOBAL_DEF("display/window/subwindows/embed_subwindows", true);
// Keep the enum values in sync with the `DisplayServer::VSyncMode` enum. // Keep the enum values in sync with the `DisplayServerEnums::VSyncMode` enum.
custom_prop_info["display/window/vsync/vsync_mode"] = PropertyInfo(Variant::INT, "display/window/vsync/vsync_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Adaptive,Mailbox"); custom_prop_info["display/window/vsync/vsync_mode"] = PropertyInfo(Variant::INT, "display/window/vsync/vsync_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Adaptive,Mailbox");
GLOBAL_DEF("display/window/frame_pacing/android/enable_frame_pacing", true); GLOBAL_DEF("display/window/frame_pacing/android/enable_frame_pacing", true);
@ -1806,7 +1818,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("gui/common/drag_threshold", 10); GLOBAL_DEF("gui/common/drag_threshold", 10);
GLOBAL_DEF_BASIC("gui/common/snap_controls_to_pixels", true); GLOBAL_DEF_BASIC("gui/common/snap_controls_to_pixels", true);
GLOBAL_DEF(PropertyInfo(Variant::INT, "gui/common/show_focus_state_on_pointer_event", PROPERTY_HINT_ENUM, "Never,Control Supports Keyboard Input,Always"), 1); GLOBAL_DEF(PropertyInfo(Variant::INT, "gui/common/show_focus_state_on_pointer_event", PROPERTY_HINT_ENUM, "Never,Text Input Controls,Always"), 1);
GLOBAL_DEF_BASIC("gui/fonts/dynamic_fonts/use_oversampling", true); GLOBAL_DEF_BASIC("gui/fonts/dynamic_fonts/use_oversampling", true);
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/rendering_device/vsync/frame_queue_size", PROPERTY_HINT_RANGE, "2,3,1"), 2); GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/rendering_device/vsync/frame_queue_size", PROPERTY_HINT_RANGE, "2,3,1"), 2);

View file

@ -31,6 +31,7 @@
#pragma once #pragma once
#include "core/object/object.h" #include "core/object/object.h"
#include "core/os/thread_safe.h"
#include "core/templates/rb_map.h" #include "core/templates/rb_map.h"
template <typename T> template <typename T>
@ -204,7 +205,7 @@ public:
const HashMap<StringName, PropertyInfo> &get_custom_property_info() const; const HashMap<StringName, PropertyInfo> &get_custom_property_info() const;
uint64_t get_last_saved_time() { return last_save_time; } uint64_t get_last_saved_time() { return last_save_time; }
List<String> get_input_presets() const { return input_presets; } List<String> get_input_presets() const { return List<String>(input_presets); }
Variant get_setting_with_override(const StringName &p_name) const; Variant get_setting_with_override(const StringName &p_name) const;
Variant get_setting_with_override_and_custom_features(const StringName &p_name, const Vector<String> &p_features) const; Variant get_setting_with_override_and_custom_features(const StringName &p_name, const Vector<String> &p_features) const;

View file

@ -30,6 +30,10 @@
#ifndef DISABLE_DEPRECATED #ifndef DISABLE_DEPRECATED
#include "core_bind.h"
#include "core/object/class_db.h"
namespace CoreBind { namespace CoreBind {
// Semaphore // Semaphore

View file

@ -31,6 +31,7 @@
#include "core_bind.h" #include "core_bind.h"
#include "core_bind.compat.inc" #include "core_bind.compat.inc"
#include "core/config/engine.h"
#include "core/config/project_settings.h" #include "core/config/project_settings.h"
#include "core/crypto/crypto_core.h" #include "core/crypto/crypto_core.h"
#include "core/debugger/engine_debugger.h" #include "core/debugger/engine_debugger.h"
@ -39,8 +40,11 @@
#include "core/io/marshalls.h" #include "core/io/marshalls.h"
#include "core/math/geometry_2d.h" #include "core/math/geometry_2d.h"
#include "core/math/geometry_3d.h" #include "core/math/geometry_3d.h"
#include "core/object/class_db.h"
#include "core/os/keyboard.h" #include "core/os/keyboard.h"
#include "core/os/main_loop.h" #include "core/os/main_loop.h"
#include "core/os/os.h"
#include "core/os/process_id.h"
#include "core/os/thread_safe.h" #include "core/os/thread_safe.h"
#include "core/variant/typed_array.h" #include "core/variant/typed_array.h"
@ -90,11 +94,13 @@ Vector<String> ResourceLoader::get_recognized_extensions_for_type(const String &
return ret; return ret;
} }
void ResourceLoader::add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front) { void ResourceLoader::add_resource_format_loader(RequiredParam<ResourceFormatLoader> rp_format_loader, bool p_at_front) {
EXTRACT_PARAM_OR_FAIL(p_format_loader, rp_format_loader);
::ResourceLoader::add_resource_format_loader(p_format_loader, p_at_front); ::ResourceLoader::add_resource_format_loader(p_format_loader, p_at_front);
} }
void ResourceLoader::remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader) { void ResourceLoader::remove_resource_format_loader(RequiredParam<ResourceFormatLoader> rp_format_loader) {
EXTRACT_PARAM_OR_FAIL(p_format_loader, rp_format_loader);
::ResourceLoader::remove_resource_format_loader(p_format_loader); ::ResourceLoader::remove_resource_format_loader(p_format_loader);
} }
@ -132,6 +138,10 @@ ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) {
return ::ResourceLoader::get_resource_uid(p_path); return ::ResourceLoader::get_resource_uid(p_path);
} }
String ResourceLoader::get_resource_type(const String &p_path) {
return ::ResourceLoader::get_resource_type(p_path);
}
Vector<String> ResourceLoader::list_directory(const String &p_directory) { Vector<String> ResourceLoader::list_directory(const String &p_directory) {
return ::ResourceLoader::list_directory(p_directory); return ::ResourceLoader::list_directory(p_directory);
} }
@ -151,6 +161,7 @@ void ResourceLoader::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_cached_ref", "path"), &ResourceLoader::get_cached_ref); ClassDB::bind_method(D_METHOD("get_cached_ref", "path"), &ResourceLoader::get_cached_ref);
ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &ResourceLoader::exists, DEFVAL("")); ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &ResourceLoader::exists, DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_resource_uid", "path"), &ResourceLoader::get_resource_uid); ClassDB::bind_method(D_METHOD("get_resource_uid", "path"), &ResourceLoader::get_resource_uid);
ClassDB::bind_method(D_METHOD("get_resource_type", "path"), &ResourceLoader::get_resource_type);
ClassDB::bind_method(D_METHOD("list_directory", "directory_path"), &ResourceLoader::list_directory); ClassDB::bind_method(D_METHOD("list_directory", "directory_path"), &ResourceLoader::list_directory);
BIND_ENUM_CONSTANT(THREAD_LOAD_INVALID_RESOURCE); BIND_ENUM_CONSTANT(THREAD_LOAD_INVALID_RESOURCE);
@ -175,7 +186,8 @@ Error ResourceSaver::set_uid(const String &p_path, ResourceUID::ID p_uid) {
return ::ResourceSaver::set_uid(p_path, p_uid); return ::ResourceSaver::set_uid(p_path, p_uid);
} }
Vector<String> ResourceSaver::get_recognized_extensions(const Ref<Resource> &p_resource) { Vector<String> ResourceSaver::get_recognized_extensions(RequiredParam<Resource> rp_resource) {
EXTRACT_PARAM_OR_FAIL_V(p_resource, rp_resource, Vector<String>());
List<String> exts; List<String> exts;
::ResourceSaver::get_recognized_extensions(p_resource, &exts); ::ResourceSaver::get_recognized_extensions(p_resource, &exts);
Vector<String> ret; Vector<String> ret;
@ -185,11 +197,13 @@ Vector<String> ResourceSaver::get_recognized_extensions(const Ref<Resource> &p_r
return ret; return ret;
} }
void ResourceSaver::add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front) { void ResourceSaver::add_resource_format_saver(RequiredParam<ResourceFormatSaver> rp_format_saver, bool p_at_front) {
EXTRACT_PARAM_OR_FAIL(p_format_saver, rp_format_saver);
::ResourceSaver::add_resource_format_saver(p_format_saver, p_at_front); ::ResourceSaver::add_resource_format_saver(p_format_saver, p_at_front);
} }
void ResourceSaver::remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver) { void ResourceSaver::remove_resource_format_saver(RequiredParam<ResourceFormatSaver> rp_format_saver) {
EXTRACT_PARAM_OR_FAIL(p_format_saver, rp_format_saver);
::ResourceSaver::remove_resource_format_saver(p_format_saver); ::ResourceSaver::remove_resource_format_saver(p_format_saver);
} }
@ -425,7 +439,7 @@ int OS::create_instance(const Vector<String> &p_arguments) {
for (const String &arg : p_arguments) { for (const String &arg : p_arguments) {
args.push_back(arg); args.push_back(arg);
} }
::OS::ProcessID pid = 0; ProcessID pid = 0;
Error err = ::OS::get_singleton()->create_instance(args, &pid); Error err = ::OS::get_singleton()->create_instance(args, &pid);
if (err != OK) { if (err != OK) {
return -1; return -1;
@ -446,7 +460,7 @@ int OS::create_process(const String &p_path, const Vector<String> &p_arguments,
for (const String &arg : p_arguments) { for (const String &arg : p_arguments) {
args.push_back(arg); args.push_back(arg);
} }
::OS::ProcessID pid = 0; ProcessID pid = 0;
Error err = ::OS::get_singleton()->create_process(p_path, args, &pid, p_open_console); Error err = ::OS::get_singleton()->create_process(p_path, args, &pid, p_open_console);
if (err != OK) { if (err != OK) {
return -1; return -1;
@ -553,6 +567,10 @@ String OS::get_locale() const {
return ::OS::get_singleton()->get_locale(); return ::OS::get_singleton()->get_locale();
} }
Vector<String> OS::get_preferred_locales() const {
return ::OS::get_singleton()->get_preferred_locales();
}
String OS::get_locale_language() const { String OS::get_locale_language() const {
return ::OS::get_singleton()->get_locale_language(); return ::OS::get_singleton()->get_locale_language();
} }
@ -704,8 +722,8 @@ String OS::get_unique_id() const {
return ::OS::get_singleton()->get_unique_id(); return ::OS::get_singleton()->get_unique_id();
} }
void OS::add_logger(const Ref<Logger> &p_logger) { void OS::add_logger(RequiredParam<Logger> rp_logger) {
ERR_FAIL_COND(p_logger.is_null()); EXTRACT_PARAM_OR_FAIL(p_logger, rp_logger);
if (!logger_bind) { if (!logger_bind) {
logger_bind = memnew(LoggerBind); logger_bind = memnew(LoggerBind);
@ -716,8 +734,8 @@ void OS::add_logger(const Ref<Logger> &p_logger) {
logger_bind->loggers.push_back(p_logger); logger_bind->loggers.push_back(p_logger);
} }
void OS::remove_logger(const Ref<Logger> &p_logger) { void OS::remove_logger(RequiredParam<Logger> rp_logger) {
ERR_FAIL_COND(p_logger.is_null()); EXTRACT_PARAM_OR_FAIL(p_logger, rp_logger);
ERR_FAIL_COND_MSG(!logger_bind || logger_bind->loggers.find(p_logger) == -1, "Could not remove logger, as it hasn't been added."); ERR_FAIL_COND_MSG(!logger_bind || logger_bind->loggers.find(p_logger) == -1, "Could not remove logger, as it hasn't been added.");
logger_bind->loggers.erase(p_logger); logger_bind->loggers.erase(p_logger);
} }
@ -809,6 +827,7 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("delay_usec", "usec"), &OS::delay_usec); ClassDB::bind_method(D_METHOD("delay_usec", "usec"), &OS::delay_usec);
ClassDB::bind_method(D_METHOD("delay_msec", "msec"), &OS::delay_msec); ClassDB::bind_method(D_METHOD("delay_msec", "msec"), &OS::delay_msec);
ClassDB::bind_method(D_METHOD("get_locale"), &OS::get_locale); ClassDB::bind_method(D_METHOD("get_locale"), &OS::get_locale);
ClassDB::bind_method(D_METHOD("get_preferred_locales"), &OS::get_preferred_locales);
ClassDB::bind_method(D_METHOD("get_locale_language"), &OS::get_locale_language); ClassDB::bind_method(D_METHOD("get_locale_language"), &OS::get_locale_language);
ClassDB::bind_method(D_METHOD("get_model_name"), &OS::get_model_name); ClassDB::bind_method(D_METHOD("get_model_name"), &OS::get_model_name);
@ -1662,13 +1681,15 @@ StringName ClassDB::class_get_property_setter(const StringName &p_class, const S
return ::ClassDB::get_property_setter(p_class, p_property); return ::ClassDB::get_property_setter(p_class, p_property);
} }
Variant ClassDB::class_get_property(Object *p_object, const StringName &p_property) const { Variant ClassDB::class_get_property(RequiredParam<Object> rp_object, const StringName &p_property) const {
EXTRACT_PARAM_OR_FAIL_V(p_object, rp_object, Variant());
Variant ret; Variant ret;
::ClassDB::get_property(p_object, p_property, ret); ::ClassDB::get_property(p_object, p_property, ret);
return ret; return ret;
} }
Error ClassDB::class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const { Error ClassDB::class_set_property(RequiredParam<Object> rp_object, const StringName &p_property, const Variant &p_value) const {
EXTRACT_PARAM_OR_FAIL_V(p_object, rp_object, ERR_INVALID_PARAMETER);
Variant ret; Variant ret;
bool valid; bool valid;
if (!::ClassDB::set_property(p_object, p_property, p_value, &valid)) { if (!::ClassDB::set_property(p_object, p_property, p_value, &valid)) {
@ -1815,7 +1836,7 @@ void ClassDB::get_argument_options(const StringName &p_function, int p_idx, List
::ClassDB::get_class_list(classes); ::ClassDB::get_class_list(classes);
for (const StringName &E : classes) { for (const StringName &E : classes) {
if (::ClassDB::is_class_exposed(E)) { if (::ClassDB::is_class_exposed(E)) {
r_options->push_back(E.operator String().quote()); r_options->push_back(E.string().quote());
} }
} }
} }
@ -1985,7 +2006,8 @@ Object *Engine::get_singleton_object(const StringName &p_name) const {
return ::Engine::get_singleton()->get_singleton_object(p_name); return ::Engine::get_singleton()->get_singleton_object(p_name);
} }
void Engine::register_singleton(const StringName &p_name, Object *p_object) { void Engine::register_singleton(const StringName &p_name, RequiredParam<Object> rp_instance) {
EXTRACT_PARAM_OR_FAIL(p_object, rp_instance);
ERR_FAIL_COND_MSG(has_singleton(p_name), vformat("Singleton already registered: '%s'.", String(p_name))); ERR_FAIL_COND_MSG(has_singleton(p_name), vformat("Singleton already registered: '%s'.", String(p_name)));
ERR_FAIL_COND_MSG(!String(p_name).is_valid_ascii_identifier(), vformat("Singleton name is not a valid identifier: '%s'.", p_name)); ERR_FAIL_COND_MSG(!String(p_name).is_valid_ascii_identifier(), vformat("Singleton name is not a valid identifier: '%s'.", p_name));
::Engine::Singleton s; ::Engine::Singleton s;
@ -2012,11 +2034,13 @@ Vector<String> Engine::get_singleton_list() const {
return ret; return ret;
} }
Error Engine::register_script_language(ScriptLanguage *p_language) { Error Engine::register_script_language(RequiredParam<ScriptLanguage> rp_language) {
EXTRACT_PARAM_OR_FAIL_V(p_language, rp_language, ERR_INVALID_PARAMETER);
return ScriptServer::register_language(p_language); return ScriptServer::register_language(p_language);
} }
Error Engine::unregister_script_language(const ScriptLanguage *p_language) { Error Engine::unregister_script_language(RequiredParam<const ScriptLanguage> rp_language) {
EXTRACT_PARAM_OR_FAIL_V(p_language, rp_language, ERR_INVALID_PARAMETER);
return ScriptServer::unregister_language(p_language); return ScriptServer::unregister_language(p_language);
} }

View file

@ -31,8 +31,10 @@
#pragma once #pragma once
#include "core/debugger/engine_profiler.h" #include "core/debugger/engine_profiler.h"
#include "core/io/logger.h"
#include "core/io/resource_loader.h" #include "core/io/resource_loader.h"
#include "core/io/resource_saver.h" #include "core/io/resource_saver.h"
#include "core/object/class_db.h"
#include "core/object/script_backtrace.h" #include "core/object/script_backtrace.h"
#include "core/os/semaphore.h" #include "core/os/semaphore.h"
#include "core/os/thread.h" #include "core/os/thread.h"
@ -74,14 +76,15 @@ public:
Ref<Resource> load(const String &p_path, const String &p_type_hint = "", CacheMode p_cache_mode = CACHE_MODE_REUSE); Ref<Resource> load(const String &p_path, const String &p_type_hint = "", CacheMode p_cache_mode = CACHE_MODE_REUSE);
Vector<String> get_recognized_extensions_for_type(const String &p_type); Vector<String> get_recognized_extensions_for_type(const String &p_type);
void add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front); void add_resource_format_loader(RequiredParam<ResourceFormatLoader> p_format_loader, bool p_at_front);
void remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader); void remove_resource_format_loader(RequiredParam<ResourceFormatLoader> p_format_loader);
void set_abort_on_missing_resources(bool p_abort); void set_abort_on_missing_resources(bool p_abort);
PackedStringArray get_dependencies(const String &p_path); PackedStringArray get_dependencies(const String &p_path);
bool has_cached(const String &p_path); bool has_cached(const String &p_path);
Ref<Resource> get_cached_ref(const String &p_path); Ref<Resource> get_cached_ref(const String &p_path);
bool exists(const String &p_path, const String &p_type_hint = ""); bool exists(const String &p_path, const String &p_type_hint = "");
ResourceUID::ID get_resource_uid(const String &p_path); ResourceUID::ID get_resource_uid(const String &p_path);
String get_resource_type(const String &p_path);
Vector<String> list_directory(const String &p_directory); Vector<String> list_directory(const String &p_directory);
@ -111,9 +114,9 @@ public:
Error save(RequiredParam<Resource> p_resource, const String &p_path, BitField<SaverFlags> p_flags); Error save(RequiredParam<Resource> p_resource, const String &p_path, BitField<SaverFlags> p_flags);
Error set_uid(const String &p_path, ResourceUID::ID p_uid); Error set_uid(const String &p_path, ResourceUID::ID p_uid);
Vector<String> get_recognized_extensions(const Ref<Resource> &p_resource); Vector<String> get_recognized_extensions(RequiredParam<Resource> p_resource);
void add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front); void add_resource_format_saver(RequiredParam<ResourceFormatSaver> p_format_saver, bool p_at_front);
void remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver); void remove_resource_format_saver(RequiredParam<ResourceFormatSaver> p_format_saver);
ResourceUID::ID get_resource_id_for_path(const String &p_path, bool p_generate = false); ResourceUID::ID get_resource_id_for_path(const String &p_path, bool p_generate = false);
@ -247,6 +250,7 @@ public:
Vector<String> get_video_adapter_driver_info() const; Vector<String> get_video_adapter_driver_info() const;
String get_locale() const; String get_locale() const;
Vector<String> get_preferred_locales() const;
String get_locale_language() const; String get_locale_language() const;
String get_model_name() const; String get_model_name() const;
@ -309,8 +313,8 @@ public:
Vector<String> get_granted_permissions() const; Vector<String> get_granted_permissions() const;
void revoke_granted_permissions(); void revoke_granted_permissions();
void add_logger(const Ref<Logger> &p_logger); void add_logger(RequiredParam<Logger> p_logger);
void remove_logger(const Ref<Logger> &p_logger); void remove_logger(RequiredParam<Logger> p_logger);
void remove_script_loggers(const ScriptLanguage *p_script); void remove_script_loggers(const ScriptLanguage *p_script);
static OS *get_singleton() { return singleton; } static OS *get_singleton() { return singleton; }
@ -531,8 +535,8 @@ public:
TypedArray<Dictionary> class_get_property_list(const StringName &p_class, bool p_no_inheritance = false) const; TypedArray<Dictionary> class_get_property_list(const StringName &p_class, bool p_no_inheritance = false) const;
StringName class_get_property_getter(const StringName &p_class, const StringName &p_property); StringName class_get_property_getter(const StringName &p_class, const StringName &p_property);
StringName class_get_property_setter(const StringName &p_class, const StringName &p_property); StringName class_get_property_setter(const StringName &p_class, const StringName &p_property);
Variant class_get_property(Object *p_object, const StringName &p_property) const; Variant class_get_property(RequiredParam<Object> p_object, const StringName &p_property) const;
Error class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const; Error class_set_property(RequiredParam<Object> p_object, const StringName &p_property, const Variant &p_value) const;
Variant class_get_property_default_value(const StringName &p_class, const StringName &p_property) const; Variant class_get_property_default_value(const StringName &p_class, const StringName &p_property) const;
@ -609,12 +613,12 @@ public:
bool has_singleton(const StringName &p_name) const; bool has_singleton(const StringName &p_name) const;
Object *get_singleton_object(const StringName &p_name) const; Object *get_singleton_object(const StringName &p_name) const;
void register_singleton(const StringName &p_name, Object *p_object); void register_singleton(const StringName &p_name, RequiredParam<Object> p_instance);
void unregister_singleton(const StringName &p_name); void unregister_singleton(const StringName &p_name);
Vector<String> get_singleton_list() const; Vector<String> get_singleton_list() const;
Error register_script_language(ScriptLanguage *p_language); Error register_script_language(RequiredParam<ScriptLanguage> p_language);
Error unregister_script_language(const ScriptLanguage *p_language); Error unregister_script_language(RequiredParam<const ScriptLanguage> p_language);
int get_script_language_count(); int get_script_language_count();
ScriptLanguage *get_script_language(int p_index) const; ScriptLanguage *get_script_language(int p_index) const;
TypedArray<ScriptBacktrace> capture_script_backtraces(bool p_include_variables = false) const; TypedArray<ScriptBacktrace> capture_script_backtraces(bool p_include_variables = false) const;

View file

@ -41,7 +41,7 @@ def version_hash_builder(target, source, env):
#include "core/version.h" #include "core/version.h"
const char *const GODOT_VERSION_HASH = "{git_hash}"; const char *const GODOT_VERSION_HASH = "{git_hash}";
const uint64_t GODOT_VERSION_TIMESTAMP = {git_timestamp}; const unsigned long long GODOT_VERSION_TIMESTAMP = {git_timestamp};
""".format(**source[0].read()) """.format(**source[0].read())
) )
@ -63,10 +63,10 @@ def encryption_key_builder(target, source, env):
with methods.generated_wrapper(str(target[0])) as file: with methods.generated_wrapper(str(target[0])) as file:
file.write( file.write(
f"""\ f"""\
#include "core/config/project_settings.h" #include <cstdint>
uint8_t script_encryption_key[32] = {{ uint8_t script_encryption_key[32] = {{
{methods.format_buffer(buffer, 1)} {methods.format_buffer(buffer, 1)}
}};""" }};"""
) )
@ -87,7 +87,7 @@ def make_certs_header(target, source, env):
inline constexpr int _certs_compressed_size = {len(buffer)}; inline constexpr int _certs_compressed_size = {len(buffer)};
inline constexpr int _certs_uncompressed_size = {decomp_size}; inline constexpr int _certs_uncompressed_size = {decomp_size};
inline constexpr unsigned char _certs_compressed[] = {{ inline constexpr unsigned char _certs_compressed[] = {{
{methods.format_buffer(buffer, 1)} {methods.format_buffer(buffer, 1)}
}}; }};
""") """)

View file

@ -30,10 +30,12 @@
#include "core_constants.h" #include "core_constants.h"
#include "core/input/input_event.h" #include "core/input/input_enums.h"
#include "core/object/class_db.h" #include "core/object/method_bind_common.h" // IWYU pragma: keep. For `VARIANT_BITFIELD_CAST(MethodFlags)`.
#include "core/os/keyboard.h" #include "core/os/keyboard.h"
#include "core/variant/type_info.h"
#include "core/variant/variant.h" #include "core/variant/variant.h"
#include "core/variant/variant_caster.h"
struct _CoreConstant { struct _CoreConstant {
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
@ -69,183 +71,183 @@ static HashMap<StringName, Vector<_CoreConstant>> _global_enums;
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
#define BIND_CORE_CONSTANT(m_constant) \ #define BIND_CORE_CONSTANT(m_constant) \
_global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); \ _global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; _global_constants_map[#m_constant] = _global_constants.size() - 1;
#define BIND_CORE_ENUM_CONSTANT(m_constant) \ #define BIND_CORE_ENUM_CONSTANT(m_constant) \
{ \ { \
StringName enum_name = __constant_get_enum_name(m_constant); \ StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \ _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \ _global_constants_map[#m_constant] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
} }
#define BIND_CORE_BITFIELD_FLAG(m_constant) \ #define BIND_CORE_BITFIELD_FLAG(m_constant) \
{ \ { \
StringName enum_name = __constant_get_bitfield_name(m_constant); \ StringName enum_name = __constant_get_bitfield_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant, false, true)); \ _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant, false, true)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \ _global_constants_map[#m_constant] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
} }
// This just binds enum classes as if they were regular enum constants. // This just binds enum classes as if they were regular enum constants.
#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \ #define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \
{ \ { \
StringName enum_name = __constant_get_enum_name(m_enum::m_member); \ StringName enum_name = __constant_get_enum_name(m_enum::m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \ _global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
} }
#define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member) \ #define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member) \
{ \ { \
StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \ StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member, false, true)); \ _global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member, false, true)); \
_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \
{ \
StringName enum_name = __constant_get_enum_name(m_enum::m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
} }
#define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member) \ #define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \
{ \ { \
StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \ StringName enum_name = __constant_get_enum_name(m_enum::m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member) \
{ \
StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member, false, true)); \ _global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member, false, true)); \
_global_constants_map[#m_name] = _global_constants.size() - 1; \ _global_constants_map[#m_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \
{ \
StringName enum_name = __constant_get_enum_name(m_enum::m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member, true)); \
_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant)); \
_global_constants_map[m_custom_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
} }
#define BIND_CORE_CONSTANT_NO_VAL(m_constant) \ #define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \
{ \
StringName enum_name = __constant_get_enum_name(m_enum::m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member, true)); \
_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant)); \
_global_constants_map[m_custom_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_CONSTANT_NO_VAL(m_constant) \
_global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant, true)); \ _global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant, true)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; _global_constants_map[#m_constant] = _global_constants.size() - 1;
#define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \ #define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \
{ \ { \
StringName enum_name = __constant_get_enum_name(m_constant); \ StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant, true)); \ _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant, true)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \ _global_constants_map[#m_constant] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
} }
#define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \ #define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \
{ \ { \
StringName enum_name = __constant_get_enum_name(m_constant); \ StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant, true)); \ _global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant, true)); \
_global_constants_map[m_custom_name] = _global_constants.size() - 1; \ _global_constants_map[m_custom_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
} }
#else #else
#define BIND_CORE_CONSTANT(m_constant) \ #define BIND_CORE_CONSTANT(m_constant) \
_global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); \ _global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; _global_constants_map[#m_constant] = _global_constants.size() - 1;
#define BIND_CORE_ENUM_CONSTANT(m_constant) \ #define BIND_CORE_ENUM_CONSTANT(m_constant) \
{ \ { \
StringName enum_name = __constant_get_enum_name(m_constant); \ StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \ _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \ _global_constants_map[#m_constant] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
} }
#define BIND_CORE_BITFIELD_FLAG(m_constant) \ #define BIND_CORE_BITFIELD_FLAG(m_constant) \
{ \ { \
StringName enum_name = __constant_get_bitfield_name(m_constant); \ StringName enum_name = __constant_get_bitfield_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \ _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \ _global_constants_map[#m_constant] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
} }
// This just binds enum classes as if they were regular enum constants. // This just binds enum classes as if they were regular enum constants.
#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \ #define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \
{ \ { \
StringName enum_name = __constant_get_enum_name(m_enum::m_member); \ StringName enum_name = __constant_get_enum_name(m_enum::m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \ _global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member) \
{ \
StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \
{ \
StringName enum_name = __constant_get_enum_name(m_enum::m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
} }
#define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member) \ #define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member) \
{ \ { \
StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \ StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \
{ \
StringName enum_name = __constant_get_enum_name(m_enum::m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \ _global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant)); \
_global_constants_map[m_custom_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
} }
#define BIND_CORE_CONSTANT_NO_VAL(m_constant) \ #define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \
{ \
StringName enum_name = __constant_get_enum_name(m_enum::m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member) \
{ \
StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \
{ \
StringName enum_name = __constant_get_enum_name(m_enum::m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant)); \
_global_constants_map[m_custom_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_CONSTANT_NO_VAL(m_constant) \
_global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); \ _global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; _global_constants_map[#m_constant] = _global_constants.size() - 1;
#define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \ #define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \
{ \ { \
StringName enum_name = __constant_get_enum_name(m_constant); \ StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \ _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \ _global_constants_map[#m_constant] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
} }
#define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \ #define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \
{ \ { \
StringName enum_name = __constant_get_enum_name(m_constant); \ StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant)); \ _global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant)); \
_global_constants_map[m_custom_name] = _global_constants.size() - 1; \ _global_constants_map[m_custom_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
} }
@ -549,6 +551,11 @@ void register_global_constants() {
BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, PADDLE3); BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, PADDLE3);
BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, PADDLE4); BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, PADDLE4);
BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, TOUCHPAD); BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, TOUCHPAD);
BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MISC2);
BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MISC3);
BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MISC4);
BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MISC5);
BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MISC6);
BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, SDL_MAX); BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, SDL_MAX);
BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MAX); BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MAX);
@ -729,6 +736,18 @@ void register_global_constants() {
BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_VIRTUAL_REQUIRED); BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_VIRTUAL_REQUIRED);
BIND_CORE_BITFIELD_FLAG(METHOD_FLAGS_DEFAULT); BIND_CORE_BITFIELD_FLAG(METHOD_FLAGS_DEFAULT);
BIND_CORE_CONSTANT(UINT8_MAX);
BIND_CORE_CONSTANT(UINT16_MAX);
BIND_CORE_CONSTANT(UINT32_MAX);
BIND_CORE_CONSTANT(INT8_MIN);
BIND_CORE_CONSTANT(INT8_MAX);
BIND_CORE_CONSTANT(INT16_MIN);
BIND_CORE_CONSTANT(INT16_MAX);
BIND_CORE_CONSTANT(INT32_MIN);
BIND_CORE_CONSTANT(INT32_MAX);
BIND_CORE_CONSTANT(INT64_MIN);
BIND_CORE_CONSTANT(INT64_MAX);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_NIL", Variant::NIL); BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_NIL", Variant::NIL);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_BOOL", Variant::BOOL); BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_BOOL", Variant::BOOL);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_INT", Variant::INT); BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_INT", Variant::INT);

View file

@ -38,4 +38,5 @@ public:
static inline bool leak_reporting_enabled = true; static inline bool leak_reporting_enabled = true;
static inline bool print_line_enabled = true; static inline bool print_line_enabled = true;
static inline bool print_error_enabled = true; static inline bool print_error_enabled = true;
static inline bool print_ready = false;
}; };

View file

@ -3,17 +3,45 @@ from misc.utility.scons_hints import *
Import("env") Import("env")
def _escape_define(env, config):
return f"<{config}>" if env["ninja"] and env.msvc else f'\\"{config}\\"'
env_crypto = env.Clone() env_crypto = env.Clone()
env_crypto.AddMethod(_escape_define, "EscapeDefine")
is_builtin = env["builtin_mbedtls"] is_builtin = env["builtin_mbedtls"]
has_module = env["module_mbedtls_enabled"] has_module = env["module_mbedtls_enabled"]
thirdparty_obj = [] thirdparty_obj = []
configs = []
if is_builtin or not has_module: if is_builtin or not has_module:
# Use our headers for builtin or if the module is not going to be compiled. # Use our headers for builtin or if the module is not going to be compiled.
# We decided not to depend on system mbedtls just for these few files that can # We decided not to depend on system mbedtls and bundle a minimal version
# be easily extracted. # when building without the module.
env_crypto.Prepend(CPPPATH=["#thirdparty/mbedtls/include"]) env_crypto.Prepend(
CPPPATH=[
"#thirdparty/mbedtls/godot/",
"#thirdparty/mbedtls/include/",
"#thirdparty/mbedtls/tf-psa-crypto/include/",
"#thirdparty/mbedtls/tf-psa-crypto/drivers/builtin/include/",
]
)
env_crypto.Append(
CPPDEFINES=[
("TF_PSA_CRYPTO_CONFIG_FILE", env_crypto.EscapeDefine("godot_psa_config.h")),
("MBEDTLS_CONFIG_FILE", env_crypto.EscapeDefine("godot_mbedtls_config.h")),
]
)
configs = ["#thirdparty/mbedtls/godot/godot_psa_config.h", "#thirdparty/mbedtls/godot/godot_mbedtls_config.h"]
if not has_module:
env_crypto.Append(CPPDEFINES=["GODOT_MBEDTLS_LIGHT"])
# Our custom mbedTLS platform functions
platform_obj = []
env_crypto.add_source_files(platform_obj, ["#thirdparty/mbedtls/godot/godot_mbedtls_platform.cpp"])
env.core_sources += platform_obj
env.Depends(platform_obj, configs)
# MbedTLS core functions (for CryptoCore). # MbedTLS core functions (for CryptoCore).
# If the mbedtls module is compiled we don't need to add the .c files with our # If the mbedtls module is compiled we don't need to add the .c files with our
@ -21,45 +49,54 @@ if is_builtin or not has_module:
# Only if the module is not enabled, we must compile here the required sources # Only if the module is not enabled, we must compile here the required sources
# to make a "light" build with only the necessary mbedtls files. # to make a "light" build with only the necessary mbedtls files.
if not has_module: if not has_module:
# Minimal mbedTLS config file
config_path = "thirdparty/mbedtls/include/godot_core_mbedtls_config.h"
config_path = f"<{config_path}>" if env_crypto["ninja"] and env_crypto.msvc else f'\\"{config_path}\\"'
env_crypto.Append(CPPDEFINES=[("MBEDTLS_CONFIG_FILE", config_path)])
# Build minimal mbedTLS library (MD5/SHA/Base64/AES). # Build minimal mbedTLS library (MD5/SHA/Base64/AES).
env_thirdparty = env_crypto.Clone() env_thirdparty = env_crypto.Clone()
env_thirdparty.disable_warnings() env_thirdparty.disable_warnings()
thirdparty_mbedtls_dir = "#thirdparty/mbedtls/library/" psa_dir = "#thirdparty/mbedtls/tf-psa-crypto/"
env_thirdparty.Prepend(
CPPPATH=[
psa_dir + d
for d in ["include/", "core/", "dispatch/", "extras", "platform/", "utilities/", "drivers/builtin/src/"]
]
)
env_thirdparty.Prepend(CPPPATH=["#thirdparty/mbedtls/tf-psa-crypto/drivers/builtin/include/"])
thirdparty_mbedtls_dir = "#thirdparty/mbedtls/"
thirdparty_mbedtls_sources = [ thirdparty_mbedtls_sources = [
"aes.c", "tf-psa-crypto/core/psa_crypto.c",
"base64.c", "tf-psa-crypto/core/psa_crypto_client.c",
"constant_time.c", "tf-psa-crypto/core/psa_crypto_driver_wrappers_no_static.c",
"ctr_drbg.c", "tf-psa-crypto/core/psa_crypto_random.c",
"entropy.c", "tf-psa-crypto/core/psa_crypto_slot_management.c",
"md.c", "tf-psa-crypto/utilities/base64.c",
"md5.c", "tf-psa-crypto/utilities/constant_time.c",
"sha1.c", "tf-psa-crypto/platform/threading.c",
"sha256.c", "tf-psa-crypto/extras/md.c",
"godot_core_mbedtls_platform.c", "tf-psa-crypto/drivers/builtin/src/aes.c",
"tf-psa-crypto/drivers/builtin/src/ctr_drbg.c",
"tf-psa-crypto/drivers/builtin/src/cipher.c",
"tf-psa-crypto/drivers/builtin/src/cipher_wrap.c",
"tf-psa-crypto/drivers/builtin/src/entropy.c",
"tf-psa-crypto/drivers/builtin/src/entropy_poll.c",
"tf-psa-crypto/drivers/builtin/src/md5.c",
"tf-psa-crypto/drivers/builtin/src/psa_crypto_cipher.c",
"tf-psa-crypto/drivers/builtin/src/psa_crypto_hash.c",
"tf-psa-crypto/drivers/builtin/src/psa_crypto_rsa.c",
"tf-psa-crypto/drivers/builtin/src/psa_util_internal.c",
"tf-psa-crypto/drivers/builtin/src/sha1.c",
"tf-psa-crypto/drivers/builtin/src/sha256.c",
] ]
thirdparty_mbedtls_sources = [thirdparty_mbedtls_dir + file for file in thirdparty_mbedtls_sources] thirdparty_mbedtls_sources = [thirdparty_mbedtls_dir + file for file in thirdparty_mbedtls_sources]
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_mbedtls_sources) env_thirdparty.add_source_files(thirdparty_obj, thirdparty_mbedtls_sources)
# Needed to force rebuilding the library when the configuration file is updated. # Needed to force rebuilding the library when the configuration file is updated.
env_thirdparty.Depends(thirdparty_obj, "#thirdparty/mbedtls/include/godot_core_mbedtls_config.h") env_thirdparty.Depends(thirdparty_obj, configs)
env.core_sources += thirdparty_obj env.core_sources += thirdparty_obj
elif is_builtin:
# Module mbedTLS config file
config_path = "thirdparty/mbedtls/include/godot_module_mbedtls_config.h"
config_path = f"<{config_path}>" if env_crypto["ninja"] and env_crypto.msvc else f'\\"{config_path}\\"'
env_crypto.Append(CPPDEFINES=[("MBEDTLS_CONFIG_FILE", config_path)])
# Needed to force rebuilding the core files when the configuration file is updated.
thirdparty_obj = ["#thirdparty/mbedtls/include/godot_module_mbedtls_config.h"]
# Godot source files # Godot source files
core_obj = [] core_obj = []
env_crypto.add_source_files(core_obj, "*.cpp") env_crypto.add_source_files(core_obj, "*.cpp")
env.core_sources += core_obj env.core_sources += core_obj
# Needed to force rebuilding the core files when the thirdparty library is updated. # Needed to force rebuilding the core files when the thirdparty library is updated.
env.Depends(core_obj, thirdparty_obj) env.Depends(core_obj, thirdparty_obj + configs)

View file

@ -28,7 +28,12 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/ /**************************************************************************/
#include "core/crypto/aes_context.h" #include "aes_context.h"
#include "core/object/class_db.h"
using AESMode = CryptoCore::AESContext::Mode;
using AESCipher = CryptoCore::AESContext::Cipher;
Error AESContext::start(Mode p_mode, const PackedByteArray &p_key, const PackedByteArray &p_iv) { Error AESContext::start(Mode p_mode, const PackedByteArray &p_key, const PackedByteArray &p_iv) {
ERR_FAIL_COND_V_MSG(mode != MODE_MAX, ERR_ALREADY_IN_USE, "AESContext already started. Call 'finish' before starting a new one."); ERR_FAIL_COND_V_MSG(mode != MODE_MAX, ERR_ALREADY_IN_USE, "AESContext already started. Call 'finish' before starting a new one.");
@ -36,17 +41,40 @@ Error AESContext::start(Mode p_mode, const PackedByteArray &p_key, const PackedB
// Key check. // Key check.
int key_bits = p_key.size() << 3; int key_bits = p_key.size() << 3;
ERR_FAIL_COND_V_MSG(key_bits != 128 && key_bits != 256, ERR_INVALID_PARAMETER, "AES key must be either 16 or 32 bytes"); ERR_FAIL_COND_V_MSG(key_bits != 128 && key_bits != 256, ERR_INVALID_PARAMETER, "AES key must be either 16 or 32 bytes");
// Initialization vector.
if (p_mode == MODE_CBC_ENCRYPT || p_mode == MODE_CBC_DECRYPT) { AESMode ctx_mode = AESMode::NONE;
ERR_FAIL_COND_V_MSG(p_iv.size() != 16, ERR_INVALID_PARAMETER, "The initialization vector (IV) must be exactly 16 bytes."); AESCipher ctx_cipher = AESCipher::NONE;
iv.resize(0); switch (p_mode) {
iv.append_array(p_iv); case MODE_ECB_ENCRYPT:
ctx_mode = AESMode::ENCRYPT;
ctx_cipher = AESCipher::ECB;
break;
case MODE_ECB_DECRYPT:
ctx_mode = AESMode::DECRYPT;
ctx_cipher = AESCipher::ECB;
break;
case MODE_CBC_ENCRYPT:
ctx_mode = AESMode::ENCRYPT;
ctx_cipher = AESCipher::CBC;
break;
case MODE_CBC_DECRYPT:
ctx_mode = AESMode::DECRYPT;
ctx_cipher = AESCipher::CBC;
break;
default:
ERR_FAIL_V(ERR_INVALID_PARAMETER);
} }
// Encryption/decryption key.
if (p_mode == MODE_CBC_ENCRYPT || p_mode == MODE_ECB_ENCRYPT) { // Initialization vector.
ctx.set_encode_key(p_key.ptr(), key_bits); PackedByteArray iv = p_iv;
} else { ERR_FAIL_COND_V_MSG(ctx_cipher == AESCipher::CBC && iv.size() != 16, ERR_INVALID_PARAMETER, "The initialization vector (IV) must be exactly 16 bytes.");
ctx.set_decode_key(p_key.ptr(), key_bits); if (unlikely(ctx_cipher == AESCipher::ECB && iv.size())) {
WARN_PRINT("ECB mode does not require an initialization vector (IV). Pass an empty IV argument when using ECB.");
iv.clear();
}
Error err = ctx.setup(ctx_mode, ctx_cipher, p_key.ptr(), p_key.size(), iv.size() ? iv.ptr() : nullptr, iv.size());
if (err != OK) {
return err;
} }
mode = p_mode; mode = p_mode;
return OK; return OK;
@ -58,45 +86,18 @@ PackedByteArray AESContext::update(const PackedByteArray &p_src) {
ERR_FAIL_COND_V_MSG(len % 16, PackedByteArray(), "The number of bytes to be encrypted must be multiple of 16. Add padding if needed"); ERR_FAIL_COND_V_MSG(len % 16, PackedByteArray(), "The number of bytes to be encrypted must be multiple of 16. Add padding if needed");
PackedByteArray out; PackedByteArray out;
out.resize(len); out.resize(len);
const uint8_t *src_ptr = p_src.ptr(); Error err = ctx.update(p_src.ptr(), len, out.ptrw(), len);
uint8_t *out_ptr = out.ptrw(); ERR_FAIL_COND_V(err != OK, PackedByteArray());
switch (mode) {
case MODE_ECB_ENCRYPT: {
for (int i = 0; i < len; i += 16) {
Error err = ctx.encrypt_ecb(src_ptr + i, out_ptr + i);
ERR_FAIL_COND_V(err != OK, PackedByteArray());
}
} break;
case MODE_ECB_DECRYPT: {
for (int i = 0; i < len; i += 16) {
Error err = ctx.decrypt_ecb(src_ptr + i, out_ptr + i);
ERR_FAIL_COND_V(err != OK, PackedByteArray());
}
} break;
case MODE_CBC_ENCRYPT: {
Error err = ctx.encrypt_cbc(len, iv.ptrw(), p_src.ptr(), out.ptrw());
ERR_FAIL_COND_V(err != OK, PackedByteArray());
} break;
case MODE_CBC_DECRYPT: {
Error err = ctx.decrypt_cbc(len, iv.ptrw(), p_src.ptr(), out.ptrw());
ERR_FAIL_COND_V(err != OK, PackedByteArray());
} break;
default:
ERR_FAIL_V_MSG(PackedByteArray(), "Bug!");
}
return out; return out;
} }
PackedByteArray AESContext::get_iv_state() { PackedByteArray AESContext::get_iv_state() {
ERR_FAIL_COND_V_MSG(mode != MODE_CBC_ENCRYPT && mode != MODE_CBC_DECRYPT, PackedByteArray(), "Calling 'get_iv_state' only makes sense when the context is started in CBC mode."); ERR_FAIL_V_MSG(PackedByteArray(), "Calling 'get_iv_state' is no longer supported.");
PackedByteArray out;
out.append_array(iv);
return out;
} }
void AESContext::finish() { void AESContext::finish() {
ctx.finish(nullptr, 0);
mode = MODE_MAX; mode = MODE_MAX;
iv.resize(0);
} }
void AESContext::_bind_methods() { void AESContext::_bind_methods() {

View file

@ -32,6 +32,7 @@
#include "core/crypto/crypto_core.h" #include "core/crypto/crypto_core.h"
#include "core/object/ref_counted.h" #include "core/object/ref_counted.h"
#include "core/variant/type_info.h"
class AESContext : public RefCounted { class AESContext : public RefCounted {
GDCLASS(AESContext, RefCounted); GDCLASS(AESContext, RefCounted);
@ -48,7 +49,6 @@ public:
private: private:
Mode mode = MODE_MAX; Mode mode = MODE_MAX;
CryptoCore::AESContext ctx; CryptoCore::AESContext ctx;
PackedByteArray iv;
protected: protected:
static void _bind_methods(); static void _bind_methods();

View file

@ -30,6 +30,8 @@
#include "crypto.h" #include "crypto.h"
#include "core/object/class_db.h"
/// Resources /// Resources
CryptoKey *(*CryptoKey::_create)(bool p_notify_postinitialize) = nullptr; CryptoKey *(*CryptoKey::_create)(bool p_notify_postinitialize) = nullptr;
@ -177,82 +179,3 @@ void Crypto::_bind_methods() {
ClassDB::bind_method(D_METHOD("hmac_digest", "hash_type", "key", "msg"), &Crypto::hmac_digest); ClassDB::bind_method(D_METHOD("hmac_digest", "hash_type", "key", "msg"), &Crypto::hmac_digest);
ClassDB::bind_method(D_METHOD("constant_time_compare", "trusted", "received"), &Crypto::constant_time_compare); ClassDB::bind_method(D_METHOD("constant_time_compare", "trusted", "received"), &Crypto::constant_time_compare);
} }
/// Resource loader/saver
Ref<Resource> ResourceFormatLoaderCrypto::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
String el = p_path.get_extension().to_lower();
if (el == "crt") {
X509Certificate *cert = X509Certificate::create();
if (cert) {
cert->load(p_path);
}
return cert;
} else if (el == "key") {
CryptoKey *key = CryptoKey::create();
if (key) {
key->load(p_path, false);
}
return key;
} else if (el == "pub") {
CryptoKey *key = CryptoKey::create();
if (key) {
key->load(p_path, true);
}
return key;
}
return nullptr;
}
void ResourceFormatLoaderCrypto::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("crt");
p_extensions->push_back("key");
p_extensions->push_back("pub");
}
bool ResourceFormatLoaderCrypto::handles_type(const String &p_type) const {
return p_type == "X509Certificate" || p_type == "CryptoKey";
}
String ResourceFormatLoaderCrypto::get_resource_type(const String &p_path) const {
String el = p_path.get_extension().to_lower();
if (el == "crt") {
return "X509Certificate";
} else if (el == "key" || el == "pub") {
return "CryptoKey";
}
return "";
}
Error ResourceFormatSaverCrypto::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
Error err;
Ref<X509Certificate> cert = p_resource;
Ref<CryptoKey> key = p_resource;
if (cert.is_valid()) {
err = cert->save(p_path);
} else if (key.is_valid()) {
err = key->save(p_path, p_path.has_extension("pub"));
} else {
ERR_FAIL_V(ERR_INVALID_PARAMETER);
}
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot save Crypto resource to file '%s'.", p_path));
return OK;
}
void ResourceFormatSaverCrypto::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
const X509Certificate *cert = Object::cast_to<X509Certificate>(*p_resource);
const CryptoKey *key = Object::cast_to<CryptoKey>(*p_resource);
if (cert) {
p_extensions->push_back("crt");
}
if (key) {
if (!key->is_public_only()) {
p_extensions->push_back("key");
}
p_extensions->push_back("pub");
}
}
bool ResourceFormatSaverCrypto::recognize(const Ref<Resource> &p_resource) const {
return Object::cast_to<X509Certificate>(*p_resource) || Object::cast_to<CryptoKey>(*p_resource);
}

View file

@ -32,8 +32,6 @@
#include "core/crypto/hashing_context.h" #include "core/crypto/hashing_context.h"
#include "core/io/resource.h" #include "core/io/resource.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/object/ref_counted.h" #include "core/object/ref_counted.h"
class CryptoKey : public Resource { class CryptoKey : public Resource {
@ -144,26 +142,3 @@ public:
// @see: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy // @see: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy
bool constant_time_compare(const PackedByteArray &p_trusted, const PackedByteArray &p_received); bool constant_time_compare(const PackedByteArray &p_trusted, const PackedByteArray &p_received);
}; };
class ResourceFormatLoaderCrypto : public ResourceFormatLoader {
GDSOFTCLASS(ResourceFormatLoaderCrypto, ResourceFormatLoader);
public:
virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override;
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual bool handles_type(const String &p_type) const override;
virtual String get_resource_type(const String &p_path) const override;
// Treat certificates as text files, do not generate a `*.{crt,key,pub}.uid` file.
virtual ResourceUID::ID get_resource_uid(const String &p_path) const override { return ResourceUID::INVALID_ID; }
virtual bool has_custom_uid_support() const override { return true; }
};
class ResourceFormatSaverCrypto : public ResourceFormatSaver {
GDSOFTCLASS(ResourceFormatSaverCrypto, ResourceFormatSaver);
public:
virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0) override;
virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const override;
virtual bool recognize(const Ref<Resource> &p_resource) const override;
};

View file

@ -31,189 +31,202 @@
#include "crypto_core.h" #include "crypto_core.h"
#include "core/os/os.h" #include "core/os/os.h"
#include "core/string/ustring.h"
#include <mbedtls/aes.h>
#include <mbedtls/base64.h> #include <mbedtls/base64.h>
#include <mbedtls/ctr_drbg.h> #include <psa/crypto.h>
#include <mbedtls/entropy.h>
#include <mbedtls/md5.h>
#include <mbedtls/sha1.h>
#include <mbedtls/sha256.h>
#if MBEDTLS_VERSION_MAJOR >= 3
#include <mbedtls/compat-2.x.h>
#endif
// RandomGenerator
CryptoCore::RandomGenerator::RandomGenerator() {
entropy = memalloc(sizeof(mbedtls_entropy_context));
mbedtls_entropy_init((mbedtls_entropy_context *)entropy);
mbedtls_entropy_add_source((mbedtls_entropy_context *)entropy, &CryptoCore::RandomGenerator::_entropy_poll, nullptr, 256, MBEDTLS_ENTROPY_SOURCE_STRONG);
ctx = memalloc(sizeof(mbedtls_ctr_drbg_context));
mbedtls_ctr_drbg_init((mbedtls_ctr_drbg_context *)ctx);
}
CryptoCore::RandomGenerator::~RandomGenerator() {
mbedtls_ctr_drbg_free((mbedtls_ctr_drbg_context *)ctx);
memfree(ctx);
mbedtls_entropy_free((mbedtls_entropy_context *)entropy);
memfree(entropy);
}
int CryptoCore::RandomGenerator::_entropy_poll(void *p_data, unsigned char *r_buffer, size_t p_len, size_t *r_len) {
*r_len = 0;
Error err = OS::get_singleton()->get_entropy(r_buffer, p_len);
ERR_FAIL_COND_V(err, MBEDTLS_ERR_ENTROPY_SOURCE_FAILED);
*r_len = p_len;
return 0;
}
Error CryptoCore::RandomGenerator::init() {
int ret = mbedtls_ctr_drbg_seed((mbedtls_ctr_drbg_context *)ctx, mbedtls_entropy_func, (mbedtls_entropy_context *)entropy, nullptr, 0);
if (ret) {
ERR_FAIL_COND_V_MSG(ret, FAILED, vformat(" failed\n ! mbedtls_ctr_drbg_seed returned an error %d.", ret));
}
return OK;
}
Error CryptoCore::RandomGenerator::get_random_bytes(uint8_t *r_buffer, size_t p_bytes) {
ERR_FAIL_NULL_V(ctx, ERR_UNCONFIGURED);
int ret = mbedtls_ctr_drbg_random((mbedtls_ctr_drbg_context *)ctx, r_buffer, p_bytes);
ERR_FAIL_COND_V_MSG(ret, FAILED, vformat(" failed\n ! mbedtls_ctr_drbg_seed returned an error %d.", ret));
return OK;
}
// MD5 // MD5
CryptoCore::MD5Context::MD5Context() { CryptoCore::MD5Context::MD5Context() {
ctx = memalloc(sizeof(mbedtls_md5_context)); ctx = memalloc_zeroed(sizeof(psa_hash_operation_t));
mbedtls_md5_init((mbedtls_md5_context *)ctx);
} }
CryptoCore::MD5Context::~MD5Context() { CryptoCore::MD5Context::~MD5Context() {
mbedtls_md5_free((mbedtls_md5_context *)ctx); psa_hash_abort((psa_hash_operation_t *)ctx);
memfree((mbedtls_md5_context *)ctx); memfree((psa_hash_operation_t *)ctx);
} }
Error CryptoCore::MD5Context::start() { Error CryptoCore::MD5Context::start() {
int ret = mbedtls_md5_starts_ret((mbedtls_md5_context *)ctx); int ret = psa_hash_setup((psa_hash_operation_t *)ctx, PSA_ALG_MD5);
return ret ? FAILED : OK; return ret ? FAILED : OK;
} }
Error CryptoCore::MD5Context::update(const uint8_t *p_src, size_t p_len) { Error CryptoCore::MD5Context::update(const uint8_t *p_src, size_t p_len) {
int ret = mbedtls_md5_update_ret((mbedtls_md5_context *)ctx, p_src, p_len); int ret = psa_hash_update((psa_hash_operation_t *)ctx, p_src, p_len);
return ret ? FAILED : OK; return ret ? FAILED : OK;
} }
Error CryptoCore::MD5Context::finish(unsigned char r_hash[16]) { Error CryptoCore::MD5Context::finish(unsigned char r_hash[16]) {
int ret = mbedtls_md5_finish_ret((mbedtls_md5_context *)ctx, r_hash); size_t size = 0;
return ret ? FAILED : OK; int ret = psa_hash_finish((psa_hash_operation_t *)ctx, r_hash, 16, &size);
if (ret) {
psa_hash_abort((psa_hash_operation_t *)ctx);
return FAILED;
}
ERR_FAIL_COND_V(size != 16, ERR_BUG);
return OK;
} }
// SHA1 // SHA1
CryptoCore::SHA1Context::SHA1Context() { CryptoCore::SHA1Context::SHA1Context() {
ctx = memalloc(sizeof(mbedtls_sha1_context)); ctx = memalloc_zeroed(sizeof(psa_hash_operation_t));
mbedtls_sha1_init((mbedtls_sha1_context *)ctx);
} }
CryptoCore::SHA1Context::~SHA1Context() { CryptoCore::SHA1Context::~SHA1Context() {
mbedtls_sha1_free((mbedtls_sha1_context *)ctx); psa_hash_abort((psa_hash_operation_t *)ctx);
memfree((mbedtls_sha1_context *)ctx); memfree((psa_hash_operation_t *)ctx);
} }
Error CryptoCore::SHA1Context::start() { Error CryptoCore::SHA1Context::start() {
int ret = mbedtls_sha1_starts_ret((mbedtls_sha1_context *)ctx); int ret = psa_hash_setup((psa_hash_operation_t *)ctx, PSA_ALG_SHA_1);
return ret ? FAILED : OK; return ret ? FAILED : OK;
} }
Error CryptoCore::SHA1Context::update(const uint8_t *p_src, size_t p_len) { Error CryptoCore::SHA1Context::update(const uint8_t *p_src, size_t p_len) {
int ret = mbedtls_sha1_update_ret((mbedtls_sha1_context *)ctx, p_src, p_len); int ret = psa_hash_update((psa_hash_operation_t *)ctx, p_src, p_len);
return ret ? FAILED : OK; return ret ? FAILED : OK;
} }
Error CryptoCore::SHA1Context::finish(unsigned char r_hash[20]) { Error CryptoCore::SHA1Context::finish(unsigned char r_hash[20]) {
int ret = mbedtls_sha1_finish_ret((mbedtls_sha1_context *)ctx, r_hash); size_t size = 0;
return ret ? FAILED : OK; int ret = psa_hash_finish((psa_hash_operation_t *)ctx, r_hash, 20, &size);
if (ret) {
psa_hash_abort((psa_hash_operation_t *)ctx);
return FAILED;
}
ERR_FAIL_COND_V(size != 20, ERR_BUG);
return OK;
} }
// SHA256 // SHA256
CryptoCore::SHA256Context::SHA256Context() { CryptoCore::SHA256Context::SHA256Context() {
ctx = memalloc(sizeof(mbedtls_sha256_context)); ctx = memalloc_zeroed(sizeof(psa_hash_operation_t));
mbedtls_sha256_init((mbedtls_sha256_context *)ctx);
} }
CryptoCore::SHA256Context::~SHA256Context() { CryptoCore::SHA256Context::~SHA256Context() {
mbedtls_sha256_free((mbedtls_sha256_context *)ctx); psa_hash_abort((psa_hash_operation_t *)ctx);
memfree((mbedtls_sha256_context *)ctx); memfree((psa_hash_operation_t *)ctx);
} }
Error CryptoCore::SHA256Context::start() { Error CryptoCore::SHA256Context::start() {
int ret = mbedtls_sha256_starts_ret((mbedtls_sha256_context *)ctx, 0); int ret = psa_hash_setup((psa_hash_operation_t *)ctx, PSA_ALG_SHA_256);
return ret ? FAILED : OK; return ret ? FAILED : OK;
} }
Error CryptoCore::SHA256Context::update(const uint8_t *p_src, size_t p_len) { Error CryptoCore::SHA256Context::update(const uint8_t *p_src, size_t p_len) {
int ret = mbedtls_sha256_update_ret((mbedtls_sha256_context *)ctx, p_src, p_len); int ret = psa_hash_update((psa_hash_operation_t *)ctx, p_src, p_len);
return ret ? FAILED : OK; return ret ? FAILED : OK;
} }
Error CryptoCore::SHA256Context::finish(unsigned char r_hash[32]) { Error CryptoCore::SHA256Context::finish(unsigned char r_hash[32]) {
int ret = mbedtls_sha256_finish_ret((mbedtls_sha256_context *)ctx, r_hash); size_t size = 0;
return ret ? FAILED : OK; int ret = psa_hash_finish((psa_hash_operation_t *)ctx, r_hash, 32, &size);
if (ret) {
psa_hash_abort((psa_hash_operation_t *)ctx);
return FAILED;
}
ERR_FAIL_COND_V(size != 32, ERR_BUG);
return OK;
} }
// AES256 // AES256
CryptoCore::AESContext::AESContext() { CryptoCore::AESContext::AESContext() {
ctx = memalloc(sizeof(mbedtls_aes_context)); ctx = memalloc_zeroed(sizeof(psa_cipher_operation_t));
mbedtls_aes_init((mbedtls_aes_context *)ctx);
} }
CryptoCore::AESContext::~AESContext() { CryptoCore::AESContext::~AESContext() {
mbedtls_aes_free((mbedtls_aes_context *)ctx); psa_cipher_abort((psa_cipher_operation_t *)ctx);
memfree((mbedtls_aes_context *)ctx); psa_destroy_key(key_id);
memfree((psa_cipher_operation_t *)ctx);
} }
Error CryptoCore::AESContext::set_encode_key(const uint8_t *p_key, size_t p_bits) { void CryptoCore::AESContext::reset() {
int ret = mbedtls_aes_setkey_enc((mbedtls_aes_context *)ctx, p_key, p_bits); psa_cipher_abort((psa_cipher_operation_t *)ctx);
return ret ? FAILED : OK; psa_destroy_key(key_id);
key_id = PSA_KEY_ID_NULL;
alg = PSA_ALG_NONE;
} }
Error CryptoCore::AESContext::set_decode_key(const uint8_t *p_key, size_t p_bits) { Error CryptoCore::AESContext::setup(Mode p_mode, Cipher p_cipher, const uint8_t *p_key, size_t p_key_length, const uint8_t *p_iv, size_t p_iv_size) {
int ret = mbedtls_aes_setkey_dec((mbedtls_aes_context *)ctx, p_key, p_bits); reset();
return ret ? FAILED : OK; psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
switch (p_mode) {
case Mode::NONE:
return OK;
case Mode::ENCRYPT:
case Mode::DECRYPT:
break;
default:
return ERR_INVALID_PARAMETER;
}
switch (p_cipher) {
case Cipher::CBC:
alg = PSA_ALG_CBC_NO_PADDING;
break;
case Cipher::ECB:
alg = PSA_ALG_ECB_NO_PADDING;
break;
case Cipher::CFB:
alg = PSA_ALG_CFB;
break;
default:
return ERR_INVALID_PARAMETER;
}
psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
psa_set_key_algorithm(&attr, alg);
psa_set_key_type(&attr, PSA_KEY_TYPE_AES);
psa_set_key_bits(&attr, p_key_length << 3);
int ret = psa_import_key(&attr, p_key, p_key_length, &key_id);
if (ret) {
return FAILED;
}
if (p_mode == Mode::ENCRYPT) {
ret = psa_cipher_encrypt_setup((psa_cipher_operation_t *)ctx, key_id, alg);
} else {
ret = psa_cipher_decrypt_setup((psa_cipher_operation_t *)ctx, key_id, alg);
}
if (ret == PSA_SUCCESS && p_iv_size) {
ret = psa_cipher_set_iv((psa_cipher_operation_t *)ctx, p_iv, p_iv_size);
}
if (ret) {
reset();
return FAILED;
}
return OK;
} }
Error CryptoCore::AESContext::encrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]) { Error CryptoCore::AESContext::update(const uint8_t *p_src, size_t p_src_size, uint8_t *r_dst, size_t p_dst_size) {
int ret = mbedtls_aes_crypt_ecb((mbedtls_aes_context *)ctx, MBEDTLS_AES_ENCRYPT, p_src, r_dst); size_t size = 0;
return ret ? FAILED : OK; int ret = psa_cipher_update((psa_cipher_operation_t *)ctx, p_src, p_src_size, r_dst, p_dst_size, &size);
if (ret) {
return FAILED;
}
ERR_FAIL_COND_V(p_dst_size != size, ERR_BUG);
return OK;
} }
Error CryptoCore::AESContext::encrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst) { Error CryptoCore::AESContext::finish(uint8_t *r_dst, size_t p_dst_size) {
int ret = mbedtls_aes_crypt_cbc((mbedtls_aes_context *)ctx, MBEDTLS_AES_ENCRYPT, p_length, r_iv, p_src, r_dst); size_t size = 0;
return ret ? FAILED : OK; int ret = psa_cipher_finish((psa_cipher_operation_t *)ctx, r_dst, p_dst_size, &size);
} reset();
if (ret) {
Error CryptoCore::AESContext::encrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst) { return FAILED;
size_t iv_off = 0; // Ignore and assume 16-byte alignment. }
int ret = mbedtls_aes_crypt_cfb128((mbedtls_aes_context *)ctx, MBEDTLS_AES_ENCRYPT, p_length, &iv_off, p_iv, p_src, r_dst); ERR_FAIL_COND_V(p_dst_size != size, ERR_BUG);
return ret ? FAILED : OK; return OK;
}
Error CryptoCore::AESContext::decrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]) {
int ret = mbedtls_aes_crypt_ecb((mbedtls_aes_context *)ctx, MBEDTLS_AES_DECRYPT, p_src, r_dst);
return ret ? FAILED : OK;
}
Error CryptoCore::AESContext::decrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst) {
int ret = mbedtls_aes_crypt_cbc((mbedtls_aes_context *)ctx, MBEDTLS_AES_DECRYPT, p_length, r_iv, p_src, r_dst);
return ret ? FAILED : OK;
}
Error CryptoCore::AESContext::decrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst) {
size_t iv_off = 0; // Ignore and assume 16-byte alignment.
int ret = mbedtls_aes_crypt_cfb128((mbedtls_aes_context *)ctx, MBEDTLS_AES_DECRYPT, p_length, &iv_off, p_iv, p_src, r_dst);
return ret ? FAILED : OK;
} }
// CryptoCore // CryptoCore
Error CryptoCore::generate_random(uint8_t *r_buffer, size_t p_buffer_len) {
if (unlikely(!initialized)) {
// Fall back to raw get_entropy (might affect performance).
return OS::get_singleton()->get_entropy(r_buffer, p_buffer_len);
}
int ret = psa_generate_random(r_buffer, p_buffer_len);
ERR_FAIL_COND_V(ret, FAILED);
return OK;
}
String CryptoCore::b64_encode_str(const uint8_t *p_src, size_t p_src_len) { String CryptoCore::b64_encode_str(const uint8_t *p_src, size_t p_src_len) {
size_t b64len = p_src_len / 3 * 4 + 4 + 1; size_t b64len = p_src_len / 3 * 4 + 4 + 1;
Vector<uint8_t> b64buff; Vector<uint8_t> b64buff;
@ -236,16 +249,66 @@ Error CryptoCore::b64_decode(uint8_t *r_dst, size_t p_dst_len, size_t *r_len, co
} }
Error CryptoCore::md5(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[16]) { Error CryptoCore::md5(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[16]) {
int ret = mbedtls_md5_ret(p_src, p_src_len, r_hash); ERR_FAIL_COND_V(PSA_HASH_LENGTH(PSA_ALG_MD5) != 16, ERR_BUG);
size_t size = 0;
int ret = psa_hash_compute(PSA_ALG_MD5, p_src, p_src_len, r_hash, 16, &size);
return ret ? FAILED : OK; return ret ? FAILED : OK;
} }
Error CryptoCore::sha1(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[20]) { Error CryptoCore::sha1(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[20]) {
int ret = mbedtls_sha1_ret(p_src, p_src_len, r_hash); ERR_FAIL_COND_V(PSA_HASH_LENGTH(PSA_ALG_SHA_1) != 20, ERR_BUG);
size_t size = 0;
int ret = psa_hash_compute(PSA_ALG_SHA_1, p_src, p_src_len, r_hash, 20, &size);
return ret ? FAILED : OK; return ret ? FAILED : OK;
} }
Error CryptoCore::sha256(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[32]) { Error CryptoCore::sha256(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[32]) {
int ret = mbedtls_sha256_ret(p_src, p_src_len, r_hash, 0); ERR_FAIL_COND_V(PSA_HASH_LENGTH(PSA_ALG_SHA_256) != 32, ERR_BUG);
size_t size = 0;
int ret = psa_hash_compute(PSA_ALG_SHA_256, p_src, p_src_len, r_hash, 32, &size);
return ret ? FAILED : OK; return ret ? FAILED : OK;
} }
Error CryptoCore::encrypt_cfb(const uint8_t *p_src, uint8_t *r_dst, size_t p_length, const uint8_t *p_key, size_t p_key_length, const uint8_t p_iv[16]) {
AESContext ctx;
Error err = ctx.setup(AESContext::Mode::ENCRYPT, AESContext::Cipher::CFB, p_key, p_key_length, p_iv, 16);
ERR_FAIL_COND_V(err, err);
err = ctx.update(p_src, p_length, r_dst, p_length);
ERR_FAIL_COND_V(err, err);
err = ctx.finish(nullptr, 0);
ERR_FAIL_COND_V(err, err);
return OK;
}
Error CryptoCore::decrypt_cfb(const uint8_t *p_src, uint8_t *r_dst, size_t p_length, const uint8_t *p_key, size_t p_key_length, const uint8_t p_iv[16]) {
AESContext ctx;
Error err = ctx.setup(AESContext::Mode::DECRYPT, AESContext::Cipher::CFB, p_key, p_key_length, p_iv, 16);
ERR_FAIL_COND_V(err, err);
err = ctx.update(p_src, p_length, r_dst, p_length);
ERR_FAIL_COND_V(err, err);
err = ctx.finish(nullptr, 0);
ERR_FAIL_COND_V(err, err);
return OK;
}
bool CryptoCore::initialized = false;
void CryptoCore::initialize() {
ERR_FAIL_COND(initialized);
#ifdef GODOT_MBEDTLS_PLATFORM
godot_mbedtls_platform_init();
#endif
int status = psa_crypto_init();
ERR_FAIL_COND_MSG(status != PSA_SUCCESS, "Failed to initialize psa crypto. Cryptographic functions will not work.");
initialized = true;
}
void CryptoCore::finalize() {
if (!initialized) {
return;
}
initialized = false;
mbedtls_psa_crypto_free(); // Not part of PSA for reasons.
#ifdef GODOT_MBEDTLS_PLATFORM
godot_mbedtls_platform_free();
#endif
}

View file

@ -30,24 +30,20 @@
#pragma once #pragma once
#include "core/object/ref_counted.h" #include "core/error/error_list.h"
#include <cstddef>
#include <cstdint>
class String;
class CryptoCore { class CryptoCore {
private:
static bool initialized;
public: public:
class RandomGenerator { static void initialize();
private: static void finalize();
void *entropy = nullptr;
void *ctx = nullptr;
static int _entropy_poll(void *p_data, unsigned char *r_buffer, size_t p_len, size_t *r_len);
public:
RandomGenerator();
~RandomGenerator();
Error init();
Error get_random_bytes(uint8_t *r_buffer, size_t p_bytes);
};
class MD5Context { class MD5Context {
private: private:
@ -89,23 +85,37 @@ public:
}; };
class AESContext { class AESContext {
public:
enum class Mode {
NONE,
ENCRYPT,
DECRYPT,
};
enum class Cipher {
NONE,
CBC,
CFB,
ECB,
};
private: private:
uint32_t key_id = 0;
uint32_t alg = 0;
void *ctx = nullptr; void *ctx = nullptr;
void reset();
public: public:
AESContext(); AESContext();
~AESContext(); ~AESContext();
Error set_encode_key(const uint8_t *p_key, size_t p_bits); Error setup(Mode p_mode, Cipher p_cipher, const uint8_t *p_key, size_t p_key_length, const uint8_t *p_iv, size_t p_iv_size);
Error set_decode_key(const uint8_t *p_key, size_t p_bits); Error update(const uint8_t *p_src, size_t p_src_size, uint8_t *r_dst, size_t p_dst_size);
Error encrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]); Error finish(uint8_t *r_dst, size_t p_dst_size);
Error decrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]);
Error encrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst);
Error decrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst);
Error encrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst);
Error decrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst);
}; };
static Error generate_random(uint8_t *r_buffer, size_t p_buffer_len);
static String b64_encode_str(const uint8_t *p_src, size_t p_src_len); static String b64_encode_str(const uint8_t *p_src, size_t p_src_len);
static Error b64_encode(uint8_t *r_dst, size_t p_dst_len, size_t *r_len, const uint8_t *p_src, size_t p_src_len); static Error b64_encode(uint8_t *r_dst, size_t p_dst_len, size_t *r_len, const uint8_t *p_src, size_t p_src_len);
static Error b64_decode(uint8_t *r_dst, size_t p_dst_len, size_t *r_len, const uint8_t *p_src, size_t p_src_len); static Error b64_decode(uint8_t *r_dst, size_t p_dst_len, size_t *r_len, const uint8_t *p_src, size_t p_src_len);
@ -113,4 +123,7 @@ public:
static Error md5(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[16]); static Error md5(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[16]);
static Error sha1(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[20]); static Error sha1(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[20]);
static Error sha256(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[32]); static Error sha256(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[32]);
static Error encrypt_cfb(const uint8_t *p_src, uint8_t *r_dst, size_t p_length, const uint8_t *p_key, size_t p_key_length, const uint8_t p_iv[16]);
static Error decrypt_cfb(const uint8_t *p_src, uint8_t *r_dst, size_t p_length, const uint8_t *p_key, size_t p_key_length, const uint8_t p_iv[16]);
}; };

View file

@ -0,0 +1,110 @@
/**************************************************************************/
/* crypto_resource_format.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 "crypto_resource_format.h"
#include "core/crypto/crypto.h"
Ref<Resource> ResourceFormatLoaderCrypto::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
String el = p_path.get_extension().to_lower();
if (el == "crt") {
X509Certificate *cert = X509Certificate::create();
if (cert) {
cert->load(p_path);
}
return cert;
} else if (el == "key") {
CryptoKey *key = CryptoKey::create();
if (key) {
key->load(p_path, false);
}
return key;
} else if (el == "pub") {
CryptoKey *key = CryptoKey::create();
if (key) {
key->load(p_path, true);
}
return key;
}
return nullptr;
}
void ResourceFormatLoaderCrypto::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("crt");
p_extensions->push_back("key");
p_extensions->push_back("pub");
}
bool ResourceFormatLoaderCrypto::handles_type(const String &p_type) const {
return p_type == "X509Certificate" || p_type == "CryptoKey";
}
String ResourceFormatLoaderCrypto::get_resource_type(const String &p_path) const {
String el = p_path.get_extension().to_lower();
if (el == "crt") {
return "X509Certificate";
} else if (el == "key" || el == "pub") {
return "CryptoKey";
}
return "";
}
Error ResourceFormatSaverCrypto::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
Error err;
Ref<X509Certificate> cert = p_resource;
Ref<CryptoKey> key = p_resource;
if (cert.is_valid()) {
err = cert->save(p_path);
} else if (key.is_valid()) {
err = key->save(p_path, p_path.has_extension("pub"));
} else {
ERR_FAIL_V(ERR_INVALID_PARAMETER);
}
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot save Crypto resource to file '%s'.", p_path));
return OK;
}
void ResourceFormatSaverCrypto::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
const X509Certificate *cert = Object::cast_to<X509Certificate>(*p_resource);
const CryptoKey *key = Object::cast_to<CryptoKey>(*p_resource);
if (cert) {
p_extensions->push_back("crt");
}
if (key) {
if (!key->is_public_only()) {
p_extensions->push_back("key");
}
p_extensions->push_back("pub");
}
}
bool ResourceFormatSaverCrypto::recognize(const Ref<Resource> &p_resource) const {
return Object::cast_to<X509Certificate>(*p_resource) || Object::cast_to<CryptoKey>(*p_resource);
}

View file

@ -0,0 +1,58 @@
/**************************************************************************/
/* crypto_resource_format.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. */
/**************************************************************************/
#pragma once
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/io/resource_uid.h"
class ResourceFormatLoaderCrypto : public ResourceFormatLoader {
GDSOFTCLASS(ResourceFormatLoaderCrypto, ResourceFormatLoader);
public:
virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override;
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual bool handles_type(const String &p_type) const override;
virtual String get_resource_type(const String &p_path) const override;
// Treat certificates as text files, do not generate a `*.{crt,key,pub}.uid` file.
virtual ResourceUID::ID get_resource_uid(const String &p_path) const override { return ResourceUID::INVALID_ID; }
virtual bool has_custom_uid_support() const override { return true; }
};
class ResourceFormatSaverCrypto : public ResourceFormatSaver {
GDSOFTCLASS(ResourceFormatSaverCrypto, ResourceFormatSaver);
public:
virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0) override;
virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const override;
virtual bool recognize(const Ref<Resource> &p_resource) const override;
};

View file

@ -31,6 +31,7 @@
#include "hashing_context.h" #include "hashing_context.h"
#include "core/crypto/crypto_core.h" #include "core/crypto/crypto_core.h"
#include "core/object/class_db.h"
Error HashingContext::start(HashType p_type) { Error HashingContext::start(HashType p_type) {
ERR_FAIL_COND_V(ctx != nullptr, ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(ctx != nullptr, ERR_ALREADY_IN_USE);

View file

@ -31,6 +31,7 @@
#pragma once #pragma once
#include "core/object/ref_counted.h" #include "core/object/ref_counted.h"
#include "core/variant/type_info.h"
class HashingContext : public RefCounted { class HashingContext : public RefCounted {
GDCLASS(HashingContext, RefCounted); GDCLASS(HashingContext, RefCounted);

View file

@ -81,16 +81,20 @@ Array DebuggerMarshalls::ScriptStackVariable::serialize(int max_size) {
} else { } else {
arr.push_back(var); arr.push_back(var);
} }
arr.push_back(type_hint);
return arr; return arr;
} }
bool DebuggerMarshalls::ScriptStackVariable::deserialize(const Array &p_arr) { bool DebuggerMarshalls::ScriptStackVariable::deserialize(const Array &p_arr) {
CHECK_SIZE(p_arr, 4, "ScriptStackVariable"); CHECK_SIZE(p_arr, 5, "ScriptStackVariable");
name = p_arr[0]; name = p_arr[0];
type = p_arr[1]; type = p_arr[1];
var_type = p_arr[2]; var_type = p_arr[2];
value = p_arr[3]; value = p_arr[3];
CHECK_END(p_arr, 4, "ScriptStackVariable"); type_hint = p_arr[4];
CHECK_END(p_arr, 5, "ScriptStackVariable");
return true; return true;
} }
@ -177,3 +181,27 @@ Ref<Shortcut> DebuggerMarshalls::deserialize_key_shortcut(const Array &p_keys) {
shortcut->set_events(key_events); shortcut->set_events(key_events);
return shortcut; return shortcut;
} }
String DebuggerMarshalls::parse_type_from_variant(const Variant &p_variant) {
String name;
if (p_variant.get_type() == Variant::OBJECT) {
const Object *obj = p_variant.get_validated_object();
if (obj) {
const ScriptInstance *script_instance = obj->get_script_instance();
if (script_instance) {
Ref<Script> script = script_instance->get_script();
if (script.is_valid()) {
name = script->get_global_name();
}
}
if (name.is_empty()) {
return obj->get_class();
}
}
}
return name;
}

View file

@ -39,6 +39,7 @@ struct DebuggerMarshalls {
Variant value; Variant value;
int type = -1; int type = -1;
int var_type = -1; int var_type = -1;
String type_hint;
Array serialize(int max_size = 1 << 20); // 1 MiB default. Array serialize(int max_size = 1 << 20); // 1 MiB default.
bool deserialize(const Array &p_arr); bool deserialize(const Array &p_arr);
@ -70,4 +71,6 @@ struct DebuggerMarshalls {
static Array serialize_key_shortcut(const Ref<Shortcut> &p_shortcut); static Array serialize_key_shortcut(const Ref<Shortcut> &p_shortcut);
static Ref<Shortcut> deserialize_key_shortcut(const Array &p_keys); static Ref<Shortcut> deserialize_key_shortcut(const Array &p_keys);
static String parse_type_from_variant(const Variant &p_variant);
}; };

View file

@ -138,8 +138,8 @@ void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, bo
CreatePeerFunc *create_fn = protocols.getptr(proto); CreatePeerFunc *create_fn = protocols.getptr(proto);
ERR_FAIL_NULL_MSG(create_fn, vformat("Invalid protocol: %s.", proto)); ERR_FAIL_NULL_MSG(create_fn, vformat("Invalid protocol: %s.", proto));
RemoteDebuggerPeer *peer = (*create_fn)(p_uri); Ref<RemoteDebuggerPeer> peer = (*create_fn)(p_uri);
if (!peer) { if (peer.is_null()) {
return; return;
} }
singleton = memnew(RemoteDebugger(Ref<RemoteDebuggerPeer>(peer))); singleton = memnew(RemoteDebugger(Ref<RemoteDebuggerPeer>(peer)));

View file

@ -30,6 +30,7 @@
#pragma once #pragma once
#include "core/object/ref_counted.h"
#include "core/string/string_name.h" #include "core/string/string_name.h"
#include "core/string/ustring.h" #include "core/string/ustring.h"
#include "core/templates/hash_map.h" #include "core/templates/hash_map.h"
@ -47,7 +48,7 @@ public:
typedef Error (*CaptureFunc)(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured); typedef Error (*CaptureFunc)(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured);
typedef RemoteDebuggerPeer *(*CreatePeerFunc)(const String &p_uri); typedef Ref<RemoteDebuggerPeer> (*CreatePeerFunc)(const String &p_uri);
class Profiler { class Profiler {
friend class EngineDebugger; friend class EngineDebugger;

View file

@ -31,6 +31,7 @@
#include "engine_profiler.h" #include "engine_profiler.h"
#include "core/debugger/engine_debugger.h" #include "core/debugger/engine_debugger.h"
#include "core/object/class_db.h" // IWYU pragma: keep. `GDVIRTUAL_BIND` macro.
void EngineProfiler::_bind_methods() { void EngineProfiler::_bind_methods() {
GDVIRTUAL_BIND(_toggle, "enable", "options"); GDVIRTUAL_BIND(_toggle, "enable", "options");

View file

@ -30,7 +30,7 @@
#pragma once #pragma once
#include "core/object/gdvirtual.gen.inc" #include "core/object/gdvirtual.gen.h"
#include "core/object/ref_counted.h" #include "core/object/ref_counted.h"
class EngineProfiler : public RefCounted { class EngineProfiler : public RefCounted {

View file

@ -34,6 +34,8 @@
#include "core/os/main_loop.h" #include "core/os/main_loop.h"
#include "core/os/os.h" #include "core/os/os.h"
#include <cstdio>
struct LocalDebugger::ScriptsProfiler { struct LocalDebugger::ScriptsProfiler {
struct ProfileInfoSort { struct ProfileInfoSort {
bool operator()(const ScriptLanguage::ProfilingInfo &A, const ScriptLanguage::ProfilingInfo &B) const { bool operator()(const ScriptLanguage::ProfilingInfo &A, const ScriptLanguage::ProfilingInfo &B) const {

View file

@ -31,7 +31,6 @@
#pragma once #pragma once
#include "core/debugger/engine_debugger.h" #include "core/debugger/engine_debugger.h"
#include "core/object/script_language.h"
#include "core/templates/list.h" #include "core/templates/list.h"
class LocalDebugger : public EngineDebugger { class LocalDebugger : public EngineDebugger {

View file

@ -30,6 +30,7 @@
#include "remote_debugger.h" #include "remote_debugger.h"
#include "core/config/engine.h"
#include "core/config/project_settings.h" #include "core/config/project_settings.h"
#include "core/debugger/debugger_marshalls.h" #include "core/debugger/debugger_marshalls.h"
#include "core/debugger/engine_debugger.h" #include "core/debugger/engine_debugger.h"
@ -38,6 +39,7 @@
#include "core/input/input.h" #include "core/input/input.h"
#include "core/io/resource_loader.h" #include "core/io/resource_loader.h"
#include "core/math/expression.h" #include "core/math/expression.h"
#include "core/object/class_db.h"
#include "core/object/script_language.h" #include "core/object/script_language.h"
#include "core/os/os.h" #include "core/os/os.h"
#include "servers/display/display_server.h" #include "servers/display/display_server.h"
@ -327,6 +329,7 @@ void RemoteDebugger::_send_stack_vars(List<String> &p_names, List<Variant> &p_va
stvar.name = E->get(); stvar.name = E->get();
stvar.value = F->get(); stvar.value = F->get();
stvar.type = p_type; stvar.type = p_type;
stvar.type_hint = DebuggerMarshalls::parse_type_from_variant(F->get());
send_message("stack_frame_var", stvar.serialize()); send_message("stack_frame_var", stvar.serialize());
E = E->next(); E = E->next();
F = F->next(); F = F->next();
@ -426,12 +429,12 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
} }
send_message("debug_enter", msg); send_message("debug_enter", msg);
Input::MouseMode mouse_mode = Input::MOUSE_MODE_VISIBLE; Input::MouseMode mouse_mode = Input::MouseMode::MOUSE_MODE_VISIBLE;
if (Thread::is_main_thread()) { if (Thread::is_main_thread()) {
mouse_mode = Input::get_singleton()->get_mouse_mode(); mouse_mode = Input::get_singleton()->get_mouse_mode();
if (mouse_mode != Input::MOUSE_MODE_VISIBLE) { if (mouse_mode != Input::MouseMode::MOUSE_MODE_VISIBLE) {
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); Input::get_singleton()->set_mouse_mode(Input::MouseMode::MOUSE_MODE_VISIBLE);
} }
} else { } else {
MutexLock mutex_lock(mutex); MutexLock mutex_lock(mutex);

View file

@ -33,8 +33,6 @@
#include "core/debugger/debugger_marshalls.h" #include "core/debugger/debugger_marshalls.h"
#include "core/debugger/engine_debugger.h" #include "core/debugger/engine_debugger.h"
#include "core/debugger/remote_debugger_peer.h" #include "core/debugger/remote_debugger_peer.h"
#include "core/object/class_db.h"
#include "core/string/string_name.h"
#include "core/string/ustring.h" #include "core/string/ustring.h"
#include "core/variant/array.h" #include "core/variant/array.h"

View file

@ -32,6 +32,9 @@
#include "core/config/project_settings.h" #include "core/config/project_settings.h"
#include "core/io/marshalls.h" #include "core/io/marshalls.h"
#include "core/io/stream_peer_socket.h"
#include "core/io/stream_peer_tcp.h"
#include "core/io/stream_peer_uds.h"
#include "core/os/os.h" #include "core/os/os.h"
bool RemoteDebuggerPeerTCP::is_peer_connected() { bool RemoteDebuggerPeerTCP::is_peer_connected() {
@ -126,6 +129,7 @@ void RemoteDebuggerPeerTCP::_write_out() {
void RemoteDebuggerPeerTCP::_read_in() { void RemoteDebuggerPeerTCP::_read_in() {
while (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED && tcp_client->wait(NetSocket::POLL_TYPE_IN) == OK) { while (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED && tcp_client->wait(NetSocket::POLL_TYPE_IN) == OK) {
uint8_t *buf = in_buf.ptrw(); uint8_t *buf = in_buf.ptrw();
Error err = OK;
if (in_left <= 0) { if (in_left <= 0) {
if (in_queue.size() > max_queued_messages) { if (in_queue.size() > max_queued_messages) {
break; // Too many messages already in queue. break; // Too many messages already in queue.
@ -135,18 +139,29 @@ void RemoteDebuggerPeerTCP::_read_in() {
} }
uint32_t size = 0; uint32_t size = 0;
int read = 0; int read = 0;
Error err = tcp_client->get_partial_data((uint8_t *)&size, 4, read); err = tcp_client->get_partial_data((uint8_t *)&size, 4, read);
ERR_CONTINUE(read != 4 || err != OK || size > (uint32_t)in_buf.size()); if (err != OK || read != 4) {
_disconnect_with_error("Remote debugger: Connection lost while reading packet size.");
return;
}
if (size > (uint32_t)in_buf.size()) {
_disconnect_with_error(vformat("Remote debugger: Packet too large (%s > %s bytes). Disconnecting.", String::num_uint64(size), String::num_int64(in_buf.size())));
return;
}
in_left = size; in_left = size;
in_pos = 0; in_pos = 0;
} }
int read = 0; int read = 0;
tcp_client->get_partial_data(buf + in_pos, in_left, read); err = tcp_client->get_partial_data(buf + in_pos, in_left, read);
if (err != OK || read == 0) {
_disconnect_with_error("Remote debugger: Connection lost while reading packet payload.");
return;
}
in_left -= read; in_left -= read;
in_pos += read; in_pos += read;
if (in_left == 0) { if (in_left == 0) {
Variant var; Variant var;
Error err = decode_variant(var, buf, in_pos, &read); err = decode_variant(var, buf, in_pos, &read);
ERR_CONTINUE(read != in_pos || err != OK); ERR_CONTINUE(read != in_pos || err != OK);
ERR_CONTINUE_MSG(var.get_type() != Variant::ARRAY, "Malformed packet received, not an Array."); ERR_CONTINUE_MSG(var.get_type() != Variant::ARRAY, "Malformed packet received, not an Array.");
MutexLock lock(mutex); MutexLock lock(mutex);
@ -208,7 +223,7 @@ void RemoteDebuggerPeerTCP::_poll() {
} }
} }
RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create_tcp(const String &p_uri) { Ref<RemoteDebuggerPeer> RemoteDebuggerPeerTCP::create_tcp(const String &p_uri) {
ERR_FAIL_COND_V(!p_uri.begins_with("tcp://"), nullptr); ERR_FAIL_COND_V(!p_uri.begins_with("tcp://"), nullptr);
String debug_host = p_uri.replace("tcp://", ""); String debug_host = p_uri.replace("tcp://", "");
@ -234,7 +249,7 @@ RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create_tcp(const String &p_uri) {
return memnew(RemoteDebuggerPeerTCP(stream)); return memnew(RemoteDebuggerPeerTCP(stream));
} }
RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create_unix(const String &p_uri) { Ref<RemoteDebuggerPeer> RemoteDebuggerPeerTCP::create_unix(const String &p_uri) {
ERR_FAIL_COND_V(!p_uri.begins_with("unix://"), nullptr); ERR_FAIL_COND_V(!p_uri.begins_with("unix://"), nullptr);
String debug_path = p_uri.replace("unix://", ""); String debug_path = p_uri.replace("unix://", "");
@ -249,3 +264,8 @@ RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create_unix(const String &p_uri) {
RemoteDebuggerPeer::RemoteDebuggerPeer() { RemoteDebuggerPeer::RemoteDebuggerPeer() {
max_queued_messages = (int)GLOBAL_GET("network/limits/debugger/max_queued_messages"); max_queued_messages = (int)GLOBAL_GET("network/limits/debugger/max_queued_messages");
} }
void RemoteDebuggerPeerTCP::_disconnect_with_error(const String &p_reason) {
ERR_PRINT(p_reason);
tcp_client->disconnect_from_host();
}

View file

@ -30,13 +30,13 @@
#pragma once #pragma once
#include "core/io/stream_peer_tcp.h"
#include "core/io/stream_peer_uds.h"
#include "core/object/ref_counted.h" #include "core/object/ref_counted.h"
#include "core/os/mutex.h" #include "core/os/mutex.h"
#include "core/os/thread.h" #include "core/os/thread.h"
#include "core/string/ustring.h" #include "core/string/ustring.h"
class StreamPeerSocket;
class RemoteDebuggerPeer : public RefCounted { class RemoteDebuggerPeer : public RefCounted {
GDSOFTCLASS(RemoteDebuggerPeer, RefCounted); GDSOFTCLASS(RemoteDebuggerPeer, RefCounted);
@ -79,11 +79,12 @@ private:
void _poll(); void _poll();
void _write_out(); void _write_out();
void _read_in(); void _read_in();
void _disconnect_with_error(const String &p_reason);
static Error _try_connect(Ref<StreamPeerSocket> p_stream); static Error _try_connect(Ref<StreamPeerSocket> p_stream);
public: public:
static RemoteDebuggerPeer *create_tcp(const String &p_uri); static Ref<RemoteDebuggerPeer> create_tcp(const String &p_uri);
static RemoteDebuggerPeer *create_unix(const String &p_uri); static Ref<RemoteDebuggerPeer> create_unix(const String &p_uri);
bool is_peer_connected() override; bool is_peer_connected() override;
int get_max_message_size() const override; int get_max_message_size() const override;

View file

@ -30,6 +30,9 @@
#include "doc_data.h" #include "doc_data.h"
#include "core/object/method_info.h"
#include "core/object/property_info.h"
String DocData::get_default_value_string(const Variant &p_value) { String DocData::get_default_value_string(const Variant &p_value) {
const Variant::Type type = p_value.get_type(); const Variant::Type type = p_value.get_type();
if (type == Variant::ARRAY) { if (type == Variant::ARRAY) {

View file

@ -30,7 +30,6 @@
#pragma once #pragma once
#include "core/io/xml_parser.h"
#include "core/variant/variant.h" #include "core/variant/variant.h"
class DocData { class DocData {
@ -699,6 +698,7 @@ public:
struct ClassDoc { struct ClassDoc {
String name; String name;
String inherits; String inherits;
String api_type;
String brief_description; String brief_description;
String description; String description;
String keywords; String keywords;
@ -732,6 +732,10 @@ public:
doc.inherits = p_dict["inherits"]; doc.inherits = p_dict["inherits"];
} }
if (p_dict.has("api_type")) {
doc.api_type = p_dict["api_type"];
}
if (p_dict.has("brief_description")) { if (p_dict.has("brief_description")) {
doc.brief_description = p_dict["brief_description"]; doc.brief_description = p_dict["brief_description"];
} }
@ -865,6 +869,10 @@ public:
dict["inherits"] = p_doc.inherits; dict["inherits"] = p_doc.inherits;
} }
if (!p_doc.api_type.is_empty()) {
dict["api_type"] = p_doc.api_type;
}
if (!p_doc.brief_description.is_empty()) { if (!p_doc.brief_description.is_empty()) {
dict["brief_description"] = p_doc.brief_description; dict["brief_description"] = p_doc.brief_description;
} }

View file

@ -3,6 +3,4 @@ from misc.utility.scons_hints import *
Import("env") Import("env")
env_error = env.Clone() env.add_source_files(env.core_sources, "*.cpp")
env_error.add_source_files(env.core_sources, "*.cpp")

View file

@ -30,12 +30,15 @@
#include "error_macros.h" #include "error_macros.h"
#include "core/core_globals.h"
#include "core/io/logger.h" #include "core/io/logger.h"
#include "core/object/object_id.h" #include "core/object/object_id.h"
#include "core/object/script_language.h" #include "core/object/script_language.h"
#include "core/os/os.h" #include "core/os/os.h"
#include "core/string/ustring.h" #include "core/string/ustring.h"
#include <cstdio>
// Optional physics interpolation warnings try to include the path to the relevant node. // Optional physics interpolation warnings try to include the path to the relevant node.
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) #if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
#include "core/config/project_settings.h" #include "core/config/project_settings.h"
@ -104,6 +107,12 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co
// Main error printing function. // Main error printing function.
void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) { void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) {
if (!CoreGlobals::print_ready) {
const char *err_details = (p_message && *p_message) ? p_message : p_error;
_err_print_fallback(p_function, p_file, p_line, err_details, p_type, false);
return;
}
if (is_printing_error) { if (is_printing_error) {
// Fallback if we're already printing an error, to prevent infinite recursion. // Fallback if we're already printing an error, to prevent infinite recursion.
const char *err_details = (p_message && *p_message) ? p_message : p_error; const char *err_details = (p_message && *p_message) ? p_message : p_error;
@ -138,7 +147,13 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co
// but we don't want to make it noisy by printing lots of file & line info (because it's already // but we don't want to make it noisy by printing lots of file & line info (because it's already
// been printing by a preceding _err_print_error). // been printing by a preceding _err_print_error).
void _err_print_error_asap(const String &p_error, ErrorHandlerType p_type) { void _err_print_error_asap(const String &p_error, ErrorHandlerType p_type) {
const char *err_details = p_error.utf8().get_data(); const CharString err_details_str = p_error.utf8();
const char *err_details = err_details_str.get_data();
if (!CoreGlobals::print_ready) {
_err_print_fallback(nullptr, nullptr, 0, err_details, p_type, false);
return;
}
if (is_printing_error) { if (is_printing_error) {
// Fallback if we're already printing an error, to prevent infinite recursion. // Fallback if we're already printing an error, to prevent infinite recursion.

View file

@ -32,10 +32,6 @@
#include "core/typedefs.h" #include "core/typedefs.h"
#ifdef _MSC_VER
#include <intrin.h> // `__fastfail()`.
#endif
class String; class String;
class ObjectID; class ObjectID;
@ -95,11 +91,11 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
#define FUNCTION_STR __FUNCTION__ #define FUNCTION_STR __FUNCTION__
#endif #endif
#ifdef _MSC_VER #if defined(_MSC_VER) && !defined(__clang__)
/** /**
* Don't use GENERATE_TRAP() directly, should only be used be the macros below. * Don't use GENERATE_TRAP() directly, should only be used be the macros below.
*/ */
#define GENERATE_TRAP() __fastfail(7 /* FAST_FAIL_FATAL_APP_EXIT */) #define GENERATE_TRAP() __debugbreak()
#else #else
/** /**
* Don't use GENERATE_TRAP() directly, should only be used be the macros below. * Don't use GENERATE_TRAP() directly, should only be used be the macros below.
@ -135,32 +131,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
* If not, the current function returns. * If not, the current function returns.
*/ */
#define ERR_FAIL_INDEX(m_index, m_size) \ #define ERR_FAIL_INDEX(m_index, m_size) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
return; \ return; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
* If not, prints `m_msg` and the current function returns. * If not, prints `m_msg` and the current function returns.
*/ */
#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \ #define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \
return; \ return; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Same as `ERR_FAIL_INDEX_MSG` but also notifies the editor. * Same as `ERR_FAIL_INDEX_MSG` but also notifies the editor.
*/ */
#define ERR_FAIL_INDEX_EDMSG(m_index, m_size, m_msg) \ #define ERR_FAIL_INDEX_EDMSG(m_index, m_size, m_msg) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \
return; \ return; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -170,32 +166,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
* If not, the current function returns `m_retval`. * If not, the current function returns `m_retval`.
*/ */
#define ERR_FAIL_INDEX_V(m_index, m_size, m_retval) \ #define ERR_FAIL_INDEX_V(m_index, m_size, m_retval) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
return m_retval; \ return m_retval; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
* If not, prints `m_msg` and the current function returns `m_retval`. * If not, prints `m_msg` and the current function returns `m_retval`.
*/ */
#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ #define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \
return m_retval; \ return m_retval; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Same as `ERR_FAIL_INDEX_V_MSG` but also notifies the editor. * Same as `ERR_FAIL_INDEX_V_MSG` but also notifies the editor.
*/ */
#define ERR_FAIL_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg) \ #define ERR_FAIL_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \
return m_retval; \ return m_retval; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -206,12 +202,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
* If not, the application crashes. * If not, the application crashes.
*/ */
#define CRASH_BAD_INDEX(m_index, m_size) \ #define CRASH_BAD_INDEX(m_index, m_size) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", false, true); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", false, true); \
_err_flush_stdout(); \ _err_flush_stdout(); \
GENERATE_TRAP(); \ GENERATE_TRAP(); \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -221,12 +217,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
* If not, prints `m_msg` and the application crashes. * If not, prints `m_msg` and the application crashes.
*/ */
#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \ #define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, false, true); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, false, true); \
_err_flush_stdout(); \ _err_flush_stdout(); \
GENERATE_TRAP(); \ GENERATE_TRAP(); \
} else \ } else \
((void)0) ((void)0)
// Unsigned integer index out of bounds error macros. // Unsigned integer index out of bounds error macros.
@ -238,32 +234,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures an unsigned integer index `m_index` is less than `m_size`. * Ensures an unsigned integer index `m_index` is less than `m_size`.
* If not, the current function returns. * If not, the current function returns.
*/ */
#define ERR_FAIL_UNSIGNED_INDEX(m_index, m_size) \ #define ERR_FAIL_UNSIGNED_INDEX(m_index, m_size) \
if (unlikely((m_index) >= (m_size))) { \ if (unlikely((m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
return; \ return; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Ensures an unsigned integer index `m_index` is less than `m_size`. * Ensures an unsigned integer index `m_index` is less than `m_size`.
* If not, prints `m_msg` and the current function returns. * If not, prints `m_msg` and the current function returns.
*/ */
#define ERR_FAIL_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \ #define ERR_FAIL_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \
if (unlikely((m_index) >= (m_size))) { \ if (unlikely((m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \
return; \ return; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Same as `ERR_FAIL_UNSIGNED_INDEX_MSG` but also notifies the editor. * Same as `ERR_FAIL_UNSIGNED_INDEX_MSG` but also notifies the editor.
*/ */
#define ERR_FAIL_UNSIGNED_INDEX_EDMSG(m_index, m_size, m_msg) \ #define ERR_FAIL_UNSIGNED_INDEX_EDMSG(m_index, m_size, m_msg) \
if (unlikely((m_index) >= (m_size))) { \ if (unlikely((m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \
return; \ return; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -273,32 +269,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures an unsigned integer index `m_index` is less than `m_size`. * Ensures an unsigned integer index `m_index` is less than `m_size`.
* If not, the current function returns `m_retval`. * If not, the current function returns `m_retval`.
*/ */
#define ERR_FAIL_UNSIGNED_INDEX_V(m_index, m_size, m_retval) \ #define ERR_FAIL_UNSIGNED_INDEX_V(m_index, m_size, m_retval) \
if (unlikely((m_index) >= (m_size))) { \ if (unlikely((m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
return m_retval; \ return m_retval; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Ensures an unsigned integer index `m_index` is less than `m_size`. * Ensures an unsigned integer index `m_index` is less than `m_size`.
* If not, prints `m_msg` and the current function returns `m_retval`. * If not, prints `m_msg` and the current function returns `m_retval`.
*/ */
#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ #define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
if (unlikely((m_index) >= (m_size))) { \ if (unlikely((m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \
return m_retval; \ return m_retval; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Same as `ERR_FAIL_UNSIGNED_INDEX_V_MSG` but also notifies the editor. * Same as `ERR_FAIL_UNSIGNED_INDEX_V_MSG` but also notifies the editor.
*/ */
#define ERR_FAIL_UNSIGNED_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg) \ #define ERR_FAIL_UNSIGNED_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg) \
if (unlikely((m_index) >= (m_size))) { \ if (unlikely((m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \
return m_retval; \ return m_retval; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -309,12 +305,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures an unsigned integer index `m_index` is less than `m_size`. * Ensures an unsigned integer index `m_index` is less than `m_size`.
* If not, the application crashes. * If not, the application crashes.
*/ */
#define CRASH_BAD_UNSIGNED_INDEX(m_index, m_size) \ #define CRASH_BAD_UNSIGNED_INDEX(m_index, m_size) \
if (unlikely((m_index) >= (m_size))) { \ if (unlikely((m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", false, true); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", false, true); \
_err_flush_stdout(); \ _err_flush_stdout(); \
GENERATE_TRAP(); \ GENERATE_TRAP(); \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -324,12 +320,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures an unsigned integer index `m_index` is less than `m_size`. * Ensures an unsigned integer index `m_index` is less than `m_size`.
* If not, prints `m_msg` and the application crashes. * If not, prints `m_msg` and the application crashes.
*/ */
#define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \ #define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \
if (unlikely((m_index) >= (m_size))) { \ if (unlikely((m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, false, true); \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, false, true); \
_err_flush_stdout(); \ _err_flush_stdout(); \
GENERATE_TRAP(); \ GENERATE_TRAP(); \
} else \ } else \
((void)0) ((void)0)
// Null reference error macros. // Null reference error macros.
@ -341,32 +337,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures a pointer `m_param` is not null. * Ensures a pointer `m_param` is not null.
* If it is null, the current function returns. * If it is null, the current function returns.
*/ */
#define ERR_FAIL_NULL(m_param) \ #define ERR_FAIL_NULL(m_param) \
if (unlikely(m_param == nullptr)) { \ if (unlikely(m_param == nullptr)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \
return; \ return; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Ensures a pointer `m_param` is not null. * Ensures a pointer `m_param` is not null.
* If it is null, prints `m_msg` and the current function returns. * If it is null, prints `m_msg` and the current function returns.
*/ */
#define ERR_FAIL_NULL_MSG(m_param, m_msg) \ #define ERR_FAIL_NULL_MSG(m_param, m_msg) \
if (unlikely(m_param == nullptr)) { \ if (unlikely(m_param == nullptr)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \
return; \ return; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Same as `ERR_FAIL_NULL_MSG` but also notifies the editor. * Same as `ERR_FAIL_NULL_MSG` but also notifies the editor.
*/ */
#define ERR_FAIL_NULL_EDMSG(m_param, m_msg) \ #define ERR_FAIL_NULL_EDMSG(m_param, m_msg) \
if (unlikely(m_param == nullptr)) { \ if (unlikely(m_param == nullptr)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \
return; \ return; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -376,32 +372,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures a pointer `m_param` is not null. * Ensures a pointer `m_param` is not null.
* If it is null, the current function returns `m_retval`. * If it is null, the current function returns `m_retval`.
*/ */
#define ERR_FAIL_NULL_V(m_param, m_retval) \ #define ERR_FAIL_NULL_V(m_param, m_retval) \
if (unlikely(m_param == nullptr)) { \ if (unlikely(m_param == nullptr)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \
return m_retval; \ return m_retval; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Ensures a pointer `m_param` is not null. * Ensures a pointer `m_param` is not null.
* If it is null, prints `m_msg` and the current function returns `m_retval`. * If it is null, prints `m_msg` and the current function returns `m_retval`.
*/ */
#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \ #define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \
if (unlikely(m_param == nullptr)) { \ if (unlikely(m_param == nullptr)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \
return m_retval; \ return m_retval; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Same as `ERR_FAIL_NULL_V_MSG` but also notifies the editor. * Same as `ERR_FAIL_NULL_V_MSG` but also notifies the editor.
*/ */
#define ERR_FAIL_NULL_V_EDMSG(m_param, m_retval, m_msg) \ #define ERR_FAIL_NULL_V_EDMSG(m_param, m_retval, m_msg) \
if (unlikely(m_param == nullptr)) { \ if (unlikely(m_param == nullptr)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \
return m_retval; \ return m_retval; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -413,11 +409,11 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures `m_cond` is false. * Ensures `m_cond` is false.
* If `m_cond` is true, the current function returns. * If `m_cond` is true, the current function returns.
*/ */
#define ERR_FAIL_COND(m_cond) \ #define ERR_FAIL_COND(m_cond) \
if (unlikely(m_cond)) { \ if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true."); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true."); \
return; \ return; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -427,21 +423,21 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* If checking for null use ERR_FAIL_NULL_MSG instead. * If checking for null use ERR_FAIL_NULL_MSG instead.
* If checking index bounds use ERR_FAIL_INDEX_MSG instead. * If checking index bounds use ERR_FAIL_INDEX_MSG instead.
*/ */
#define ERR_FAIL_COND_MSG(m_cond, m_msg) \ #define ERR_FAIL_COND_MSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \ if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg); \
return; \ return; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Same as `ERR_FAIL_COND_MSG` but also notifies the editor. * Same as `ERR_FAIL_COND_MSG` but also notifies the editor.
*/ */
#define ERR_FAIL_COND_EDMSG(m_cond, m_msg) \ #define ERR_FAIL_COND_EDMSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \ if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg, true); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg, true); \
return; \ return; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -453,11 +449,11 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures `m_cond` is false. * Ensures `m_cond` is false.
* If `m_cond` is true, the current function returns `m_retval`. * If `m_cond` is true, the current function returns `m_retval`.
*/ */
#define ERR_FAIL_COND_V(m_cond, m_retval) \ #define ERR_FAIL_COND_V(m_cond, m_retval) \
if (unlikely(m_cond)) { \ if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval)); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval)); \
return m_retval; \ return m_retval; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -467,21 +463,21 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* If checking for null use ERR_FAIL_NULL_V_MSG instead. * If checking for null use ERR_FAIL_NULL_V_MSG instead.
* If checking index bounds use ERR_FAIL_INDEX_V_MSG instead. * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead.
*/ */
#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \ #define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \
if (unlikely(m_cond)) { \ if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg); \
return m_retval; \ return m_retval; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Same as `ERR_FAIL_COND_V_MSG` but also notifies the editor. * Same as `ERR_FAIL_COND_V_MSG` but also notifies the editor.
*/ */
#define ERR_FAIL_COND_V_EDMSG(m_cond, m_retval, m_msg) \ #define ERR_FAIL_COND_V_EDMSG(m_cond, m_retval, m_msg) \
if (unlikely(m_cond)) { \ if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg, true); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg, true); \
return m_retval; \ return m_retval; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -491,32 +487,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures `m_cond` is false. * Ensures `m_cond` is false.
* If `m_cond` is true, the current loop continues. * If `m_cond` is true, the current loop continues.
*/ */
#define ERR_CONTINUE(m_cond) \ #define ERR_CONTINUE(m_cond) \
if (unlikely(m_cond)) { \ if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing."); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing."); \
continue; \ continue; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Ensures `m_cond` is false. * Ensures `m_cond` is false.
* If `m_cond` is true, prints `m_msg` and the current loop continues. * If `m_cond` is true, prints `m_msg` and the current loop continues.
*/ */
#define ERR_CONTINUE_MSG(m_cond, m_msg) \ #define ERR_CONTINUE_MSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \ if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg); \
continue; \ continue; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Same as `ERR_CONTINUE_MSG` but also notifies the editor. * Same as `ERR_CONTINUE_MSG` but also notifies the editor.
*/ */
#define ERR_CONTINUE_EDMSG(m_cond, m_msg) \ #define ERR_CONTINUE_EDMSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \ if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg, true); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg, true); \
continue; \ continue; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -526,32 +522,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures `m_cond` is false. * Ensures `m_cond` is false.
* If `m_cond` is true, the current loop breaks. * If `m_cond` is true, the current loop breaks.
*/ */
#define ERR_BREAK(m_cond) \ #define ERR_BREAK(m_cond) \
if (unlikely(m_cond)) { \ if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking."); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking."); \
break; \ break; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Ensures `m_cond` is false. * Ensures `m_cond` is false.
* If `m_cond` is true, prints `m_msg` and the current loop breaks. * If `m_cond` is true, prints `m_msg` and the current loop breaks.
*/ */
#define ERR_BREAK_MSG(m_cond, m_msg) \ #define ERR_BREAK_MSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \ if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg); \
break; \ break; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Same as `ERR_BREAK_MSG` but also notifies the editor. * Same as `ERR_BREAK_MSG` but also notifies the editor.
*/ */
#define ERR_BREAK_EDMSG(m_cond, m_msg) \ #define ERR_BREAK_EDMSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \ if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg, true); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg, true); \
break; \ break; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -562,12 +558,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures `m_cond` is false. * Ensures `m_cond` is false.
* If `m_cond` is true, the application crashes. * If `m_cond` is true, the application crashes.
*/ */
#define CRASH_COND(m_cond) \ #define CRASH_COND(m_cond) \
if (unlikely(m_cond)) { \ if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true."); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true."); \
_err_flush_stdout(); \ _err_flush_stdout(); \
GENERATE_TRAP(); \ GENERATE_TRAP(); \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -577,12 +573,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures `m_cond` is false. * Ensures `m_cond` is false.
* If `m_cond` is true, prints `m_msg` and the application crashes. * If `m_cond` is true, prints `m_msg` and the application crashes.
*/ */
#define CRASH_COND_MSG(m_cond, m_msg) \ #define CRASH_COND_MSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \ if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", m_msg); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", m_msg); \
_err_flush_stdout(); \ _err_flush_stdout(); \
GENERATE_TRAP(); \ GENERATE_TRAP(); \
} else \ } else \
((void)0) ((void)0)
// Generic error macros. // Generic error macros.
@ -594,11 +590,11 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* *
* The current function returns. * The current function returns.
*/ */
#define ERR_FAIL() \ #define ERR_FAIL() \
if (true) { \ if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed."); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed."); \
return; \ return; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -607,21 +603,21 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* *
* Prints `m_msg`, and the current function returns. * Prints `m_msg`, and the current function returns.
*/ */
#define ERR_FAIL_MSG(m_msg) \ #define ERR_FAIL_MSG(m_msg) \
if (true) { \ if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg); \
return; \ return; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Same as `ERR_FAIL_MSG` but also notifies the editor. * Same as `ERR_FAIL_MSG` but also notifies the editor.
*/ */
#define ERR_FAIL_EDMSG(m_msg) \ #define ERR_FAIL_EDMSG(m_msg) \
if (true) { \ if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg, true); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg, true); \
return; \ return; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -631,11 +627,11 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* *
* The current function returns `m_retval`. * The current function returns `m_retval`.
*/ */
#define ERR_FAIL_V(m_retval) \ #define ERR_FAIL_V(m_retval) \
if (true) { \ if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval)); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval)); \
return m_retval; \ return m_retval; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -644,21 +640,21 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* *
* Prints `m_msg`, and the current function returns `m_retval`. * Prints `m_msg`, and the current function returns `m_retval`.
*/ */
#define ERR_FAIL_V_MSG(m_retval, m_msg) \ #define ERR_FAIL_V_MSG(m_retval, m_msg) \
if (true) { \ if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg); \
return m_retval; \ return m_retval; \
} else \ } else \
((void)0) ((void)0)
/** /**
* Same as `ERR_FAIL_V_MSG` but also notifies the editor. * Same as `ERR_FAIL_V_MSG` but also notifies the editor.
*/ */
#define ERR_FAIL_V_EDMSG(m_retval, m_msg) \ #define ERR_FAIL_V_EDMSG(m_retval, m_msg) \
if (true) { \ if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg, true); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg, true); \
return m_retval; \ return m_retval; \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -680,27 +676,27 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
/** /**
* Prints `m_msg` once during the application lifetime. * Prints `m_msg` once during the application lifetime.
*/ */
#define ERR_PRINT_ONCE(m_msg) \ #define ERR_PRINT_ONCE(m_msg) \
if (true) { \ if (true) { \
static bool warning_shown = false; \ static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \ if (unlikely(!warning_shown)) { \
warning_shown = true; \ warning_shown = true; \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg); \
} \ } \
} else \ } else \
((void)0) ((void)0)
/** /**
* Same as `ERR_PRINT_ONCE` but also notifies the editor. * Same as `ERR_PRINT_ONCE` but also notifies the editor.
*/ */
#define ERR_PRINT_ONCE_ED(m_msg) \ #define ERR_PRINT_ONCE_ED(m_msg) \
if (true) { \ if (true) { \
static bool warning_shown = false; \ static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \ if (unlikely(!warning_shown)) { \
warning_shown = true; \ warning_shown = true; \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true); \
} \ } \
} else \ } else \
((void)0) ((void)0)
// Print warning message macros. // Print warning message macros.
@ -724,37 +720,37 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* *
* If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
*/ */
#define WARN_PRINT_ONCE(m_msg) \ #define WARN_PRINT_ONCE(m_msg) \
if (true) { \ if (true) { \
static bool warning_shown = false; \ static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \ if (unlikely(!warning_shown)) { \
warning_shown = true; \ warning_shown = true; \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, false, ERR_HANDLER_WARNING); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, false, ERR_HANDLER_WARNING); \
} \ } \
} else \ } else \
((void)0) ((void)0)
/** /**
* Same as `WARN_PRINT_ONCE` but also notifies the editor. * Same as `WARN_PRINT_ONCE` but also notifies the editor.
*/ */
#define WARN_PRINT_ONCE_ED(m_msg) \ #define WARN_PRINT_ONCE_ED(m_msg) \
if (true) { \ if (true) { \
static bool warning_shown = false; \ static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \ if (unlikely(!warning_shown)) { \
warning_shown = true; \ warning_shown = true; \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true, ERR_HANDLER_WARNING); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true, ERR_HANDLER_WARNING); \
} \ } \
} else \ } else \
((void)0) ((void)0)
/** /**
* Warns about `m_msg` only when verbose mode is enabled. * Warns about `m_msg` only when verbose mode is enabled.
*/ */
#define WARN_VERBOSE(m_msg) \ #define WARN_VERBOSE(m_msg) \
{ \ { \
if (is_print_verbose_enabled()) { \ if (is_print_verbose_enabled()) { \
WARN_PRINT(m_msg); \ WARN_PRINT(m_msg); \
} \ } \
} }
// Print deprecated warning message macros. // Print deprecated warning message macros.
@ -762,27 +758,27 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
/** /**
* Warns that the current function is deprecated. * Warns that the current function is deprecated.
*/ */
#define WARN_DEPRECATED \ #define WARN_DEPRECATED \
if (true) { \ if (true) { \
static bool warning_shown = false; \ static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \ if (unlikely(!warning_shown)) { \
warning_shown = true; \ warning_shown = true; \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", false, ERR_HANDLER_WARNING); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", false, ERR_HANDLER_WARNING); \
} \ } \
} else \ } else \
((void)0) ((void)0)
/** /**
* Warns that the current function is deprecated and prints `m_msg`. * Warns that the current function is deprecated and prints `m_msg`.
*/ */
#define WARN_DEPRECATED_MSG(m_msg) \ #define WARN_DEPRECATED_MSG(m_msg) \
if (true) { \ if (true) { \
static bool warning_shown = false; \ static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \ if (unlikely(!warning_shown)) { \
warning_shown = true; \ warning_shown = true; \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", m_msg, false, ERR_HANDLER_WARNING); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", m_msg, false, ERR_HANDLER_WARNING); \
} \ } \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -791,12 +787,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* *
* The application crashes. * The application crashes.
*/ */
#define CRASH_NOW() \ #define CRASH_NOW() \
if (true) { \ if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed."); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed."); \
_err_flush_stdout(); \ _err_flush_stdout(); \
GENERATE_TRAP(); \ GENERATE_TRAP(); \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -804,12 +800,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* *
* Prints `m_msg`, and then the application crashes. * Prints `m_msg`, and then the application crashes.
*/ */
#define CRASH_NOW_MSG(m_msg) \ #define CRASH_NOW_MSG(m_msg) \
if (true) { \ if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", m_msg); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", m_msg); \
_err_flush_stdout(); \ _err_flush_stdout(); \
GENERATE_TRAP(); \ GENERATE_TRAP(); \
} else \ } else \
((void)0) ((void)0)
/** /**
@ -828,26 +824,26 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* and that can't fail for other contributors once the code is finished and merged. * and that can't fail for other contributors once the code is finished and merged.
*/ */
#ifdef DEV_ENABLED #ifdef DEV_ENABLED
#define DEV_ASSERT(m_cond) \ #define DEV_ASSERT(m_cond) \
if (unlikely(!(m_cond))) { \ if (unlikely(!(m_cond))) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: DEV_ASSERT failed \"" _STR(m_cond) "\" is false."); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: DEV_ASSERT failed \"" _STR(m_cond) "\" is false."); \
_err_flush_stdout(); \ _err_flush_stdout(); \
GENERATE_TRAP(); \ GENERATE_TRAP(); \
} else \ } else \
((void)0) ((void)0)
#else #else
#define DEV_ASSERT(m_cond) #define DEV_ASSERT(m_cond)
#endif #endif
#ifdef DEV_ENABLED #ifdef DEV_ENABLED
#define DEV_CHECK_ONCE(m_cond) \ #define DEV_CHECK_ONCE(m_cond) \
if (true) { \ if (true) { \
static bool first_print = true; \ static bool first_print = true; \
if (first_print && unlikely(!(m_cond))) { \ if (first_print && unlikely(!(m_cond))) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "DEV_CHECK_ONCE failed \"" _STR(m_cond) "\" is false."); \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "DEV_CHECK_ONCE failed \"" _STR(m_cond) "\" is false."); \
first_print = false; \ first_print = false; \
} \ } \
} else \ } else \
((void)0) ((void)0)
#else #else
#define DEV_CHECK_ONCE(m_cond) #define DEV_CHECK_ONCE(m_cond)

View file

@ -7,7 +7,7 @@ import make_interface_dumper
import make_interface_header import make_interface_header
import make_wrappers import make_wrappers
env.CommandNoCache(["ext_wrappers.gen.inc"], "make_wrappers.py", env.Run(make_wrappers.run)) env.CommandNoCache("ext_wrappers.gen.h", "make_wrappers.py", env.Run(make_wrappers.run))
env.CommandNoCache( env.CommandNoCache(
"gdextension_interface_dump.gen.h", "gdextension_interface_dump.gen.h",
["gdextension_interface.json", "make_interface_dumper.py"], ["gdextension_interface.json", "make_interface_dumper.py"],
@ -19,6 +19,4 @@ env.CommandNoCache(
env.Run(make_interface_header.run), env.Run(make_interface_header.run),
) )
env_extension = env.Clone() env.add_source_files(env.core_sources, "*.cpp")
env_extension.add_source_files(env.core_sources, "*.cpp")

View file

@ -35,6 +35,7 @@
#include "core/extension/gdextension_special_compat_hashes.h" #include "core/extension/gdextension_special_compat_hashes.h"
#include "core/io/file_access.h" #include "core/io/file_access.h"
#include "core/io/json.h" #include "core/io/json.h"
#include "core/object/class_db.h"
#include "core/templates/pair.h" #include "core/templates/pair.h"
#include "core/version.h" #include "core/version.h"
@ -281,59 +282,59 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
// Member offsets, meta types and sizes. // Member offsets, meta types and sizes.
#define REAL_MEMBER_OFFSET(type, member) \ #define REAL_MEMBER_OFFSET(type, member) \
{ \ { \
type, \ type, \
member, \ member, \
"float", \ "float", \
sizeof(float), \ sizeof(float), \
"float", \ "float", \
sizeof(float), \ sizeof(float), \
"double", \ "double", \
sizeof(double), \ sizeof(double), \
"double", \ "double", \
sizeof(double), \ sizeof(double), \
} }
#define INT32_MEMBER_OFFSET(type, member) \ #define INT32_MEMBER_OFFSET(type, member) \
{ \ { \
type, \ type, \
member, \ member, \
"int32", \ "int32", \
sizeof(int32_t), \ sizeof(int32_t), \
"int32", \ "int32", \
sizeof(int32_t), \ sizeof(int32_t), \
"int32", \ "int32", \
sizeof(int32_t), \ sizeof(int32_t), \
"int32", \ "int32", \
sizeof(int32_t), \ sizeof(int32_t), \
} }
#define INT32_BASED_BUILTIN_MEMBER_OFFSET(type, member, member_type, member_elems) \ #define INT32_BASED_BUILTIN_MEMBER_OFFSET(type, member, member_type, member_elems) \
{ \ { \
type, \ type, \
member, \ member, \
member_type, \ member_type, \
sizeof(int32_t) * member_elems, \ sizeof(int32_t) * member_elems, \
member_type, \ member_type, \
sizeof(int32_t) * member_elems, \ sizeof(int32_t) * member_elems, \
member_type, \ member_type, \
sizeof(int32_t) * member_elems, \ sizeof(int32_t) * member_elems, \
member_type, \ member_type, \
sizeof(int32_t) * member_elems, \ sizeof(int32_t) * member_elems, \
} }
#define REAL_BASED_BUILTIN_MEMBER_OFFSET(type, member, member_type, member_elems) \ #define REAL_BASED_BUILTIN_MEMBER_OFFSET(type, member, member_type, member_elems) \
{ \ { \
type, \ type, \
member, \ member, \
member_type, \ member_type, \
sizeof(float) * member_elems, \ sizeof(float) * member_elems, \
member_type, \ member_type, \
sizeof(float) * member_elems, \ sizeof(float) * member_elems, \
member_type, \ member_type, \
sizeof(double) * member_elems, \ sizeof(double) * member_elems, \
member_type, \ member_type, \
sizeof(double) * member_elems, \ sizeof(double) * member_elems, \
} }
struct { struct {

View file

@ -30,14 +30,17 @@
#pragma once #pragma once
#include "core/extension/gdextension.h"
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
#include "core/error/error_list.h"
#include "core/string/ustring.h"
#include "core/variant/dictionary.h"
class GDExtensionAPIDump { class GDExtensionAPIDump {
public: public:
static Dictionary generate_extension_api(bool p_include_docs = false); static Dictionary generate_extension_api(bool p_include_docs = false);
static void generate_extension_json_file(const String &p_path, bool p_include_docs = false); static void generate_extension_json_file(const String &p_path, bool p_include_docs = false);
static Error validate_extension_json_file(const String &p_path); static Error validate_extension_json_file(const String &p_path);
}; };
#endif #endif

View file

@ -30,6 +30,10 @@
#ifndef DISABLE_DEPRECATED #ifndef DISABLE_DEPRECATED
#include "gdextension.h"
#include "core/object/class_db.h"
Error GDExtension::_open_library_bind_compat_88418(const String &p_path, const String &p_entry_symbol) { Error GDExtension::_open_library_bind_compat_88418(const String &p_path, const String &p_entry_symbol) {
return ERR_UNAVAILABLE; return ERR_UNAVAILABLE;
} }
@ -46,4 +50,4 @@ void GDExtension::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("initialize_library", "level"), &GDExtension::_initialize_library_bind_compat_88418); ClassDB::bind_compatibility_method(D_METHOD("initialize_library", "level"), &GDExtension::_initialize_library_bind_compat_88418);
} }
#endif #endif // DISABLE_DEPRECATED

View file

@ -32,10 +32,10 @@
#include "gdextension.compat.inc" #include "gdextension.compat.inc"
#include "core/config/project_settings.h" #include "core/config/project_settings.h"
#include "core/extension/gdextension_library_loader.h"
#include "core/object/callable_mp.h"
#include "core/object/class_db.h" #include "core/object/class_db.h"
#include "core/object/method_bind.h" #include "core/object/method_bind.h"
#include "gdextension_library_loader.h"
#include "gdextension_manager.h"
extern void gdextension_setup_interface(); extern void gdextension_setup_interface();
extern GDExtensionInterfaceFunctionPtr gdextension_get_proc_address(const char *p_name); extern GDExtensionInterfaceFunctionPtr gdextension_get_proc_address(const char *p_name);
@ -240,7 +240,7 @@ public:
#ifndef DISABLE_DEPRECATED #ifndef DISABLE_DEPRECATED
void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs) { void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs) {
const GDExtensionClassCreationInfo5 class_info5 = { const GDExtensionClassCreationInfo6 class_info6 = {
p_extension_funcs->is_virtual, // GDExtensionBool is_virtual; p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
p_extension_funcs->is_abstract, // GDExtensionBool is_abstract; p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
true, // GDExtensionBool is_exposed; true, // GDExtensionBool is_exposed;
@ -257,29 +257,30 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library
p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func; p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
p_extension_funcs->reference_func, // GDExtensionClassReference reference_func; p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func; p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */ nullptr, // GDExtensionClassCreateInstance3 create_instance_func; /* this one is mandatory */
p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */ p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
nullptr, // GDExtensionClassRecreateInstance recreate_instance_func; nullptr, // GDExtensionClassRecreateInstance recreate_instance_func;
nullptr, // GDExtensionClassGetVirtual get_virtual_func; nullptr, // GDExtensionClassGetVirtual2 get_virtual_func;
nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func; nullptr, // GDExtensionClassGetVirtualCallData2 get_virtual_call_data_func;
nullptr, // GDExtensionClassCallVirtualWithData call_virtual_func; nullptr, // GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
p_extension_funcs->class_userdata, // void *class_userdata; p_extension_funcs->class_userdata, // void *class_userdata;
}; };
const ClassCreationDeprecatedInfo legacy = { const ClassCreationDeprecatedInfo legacy = {
false, false, // bool legacy_unexposed_class;
p_extension_funcs->notification_func, // GDExtensionClassNotification notification_func; p_extension_funcs->notification_func, // GDExtensionClassNotification notification_func;
p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func; p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func;
p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; nullptr, // GDExtensionClassCreateInstance2 create_instance2_func;
p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid_func;
p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
nullptr, nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
}; };
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info5, &legacy); _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info6, &legacy);
} }
void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs) { void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs) {
const GDExtensionClassCreationInfo5 class_info5 = { const GDExtensionClassCreationInfo6 class_info6 = {
p_extension_funcs->is_virtual, // GDExtensionBool is_virtual; p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
p_extension_funcs->is_abstract, // GDExtensionBool is_abstract; p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
p_extension_funcs->is_exposed, // GDExtensionBool is_exposed; p_extension_funcs->is_exposed, // GDExtensionBool is_exposed;
@ -296,12 +297,12 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar
p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func; p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
p_extension_funcs->reference_func, // GDExtensionClassReference reference_func; p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func; p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */ nullptr, // GDExtensionClassCreateInstance3 create_instance_func; /* this one is mandatory */
p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */ p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func; p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func;
nullptr, // GDExtensionClassGetVirtual get_virtual_func; nullptr, // GDExtensionClassGetVirtual2 get_virtual_func;
nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func; nullptr, // GDExtensionClassGetVirtualCallData2 get_virtual_call_data_func;
p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func; p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
p_extension_funcs->class_userdata, // void *class_userdata; p_extension_funcs->class_userdata, // void *class_userdata;
}; };
@ -310,15 +311,16 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar
nullptr, // GDExtensionClassNotification notification_func; nullptr, // GDExtensionClassNotification notification_func;
p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func; p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func;
p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; nullptr, // GDExtensionClassCreateInstance2 create_instance2_func;
p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid_func;
p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtual get_virtual_func; p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
}; };
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info5, &legacy); _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info6, &legacy);
} }
void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs) { void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs) {
const GDExtensionClassCreationInfo5 class_info5 = { const GDExtensionClassCreationInfo6 class_info6 = {
p_extension_funcs->is_virtual, // GDExtensionBool is_virtual; p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
p_extension_funcs->is_abstract, // GDExtensionBool is_abstract; p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
p_extension_funcs->is_exposed, // GDExtensionBool is_exposed; p_extension_funcs->is_exposed, // GDExtensionBool is_exposed;
@ -327,7 +329,7 @@ void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_librar
p_extension_funcs->set_func, // GDExtensionClassSet set_func; p_extension_funcs->set_func, // GDExtensionClassSet set_func;
p_extension_funcs->get_func, // GDExtensionClassGet get_func; p_extension_funcs->get_func, // GDExtensionClassGet get_func;
p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func; p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func; p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList2 free_property_list_func;
p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func; p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func;
p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func; p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func;
p_extension_funcs->validate_property_func, // GDExtensionClassValidateProperty validate_property_func; p_extension_funcs->validate_property_func, // GDExtensionClassValidateProperty validate_property_func;
@ -335,12 +337,12 @@ void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_librar
p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func; p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
p_extension_funcs->reference_func, // GDExtensionClassReference reference_func; p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func; p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */ nullptr, // GDExtensionClassCreateInstance3 create_instance_func; /* this one is mandatory */
p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */ p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func; p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func;
nullptr, // GDExtensionClassGetVirtual get_virtual_func; nullptr, // GDExtensionClassGetVirtual2 get_virtual_func;
nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func; nullptr, // GDExtensionClassGetVirtualCallData2 get_virtual_call_data_func;
p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func; p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
p_extension_funcs->class_userdata, // void *class_userdata; p_extension_funcs->class_userdata, // void *class_userdata;
}; };
@ -348,34 +350,101 @@ void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_librar
!p_extension_funcs->is_exposed, // bool legacy_unexposed_class; !p_extension_funcs->is_exposed, // bool legacy_unexposed_class;
nullptr, // GDExtensionClassNotification notification_func; nullptr, // GDExtensionClassNotification notification_func;
nullptr, // GDExtensionClassFreePropertyList free_property_list_func; nullptr, // GDExtensionClassFreePropertyList free_property_list_func;
p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance2 create_instance_func; p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func;
p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; nullptr, // GDExtensionClassCreateInstance2 create_instance2_func;
p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid_func;
p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtual get_virtual_func; p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
}; };
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info5, &legacy); _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info6, &legacy);
} }
void GDExtension::_register_extension_class4(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs) { void GDExtension::_register_extension_class4(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs) {
GDExtensionClassCreationInfo5 class_info5 = *p_extension_funcs; const GDExtensionClassCreationInfo6 class_info6 = {
p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
p_extension_funcs->is_exposed, // GDExtensionBool is_exposed;
p_extension_funcs->is_runtime, // GDExtensionBool is_runtime;
p_extension_funcs->icon_path, // GDExtensionConstStringPtr icon_path;
p_extension_funcs->set_func, // GDExtensionClassSet set_func;
p_extension_funcs->get_func, // GDExtensionClassGet get_func;
p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList2 free_property_list_func;
p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func;
p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func;
p_extension_funcs->validate_property_func, // GDExtensionClassValidateProperty validate_property_func;
p_extension_funcs->notification_func, // GDExtensionClassNotification2 notification_func;
p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
nullptr, // GDExtensionClassCreateInstance3 create_instance_func; /* this one is mandatory */
p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func;
p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual2 get_virtual_func;
p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtualCallData2 get_virtual_call_data_func;
p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
p_extension_funcs->class_userdata, // void *class_userdata;
};
const ClassCreationDeprecatedInfo legacy = { const ClassCreationDeprecatedInfo legacy = {
!p_extension_funcs->is_exposed, // bool legacy_unexposed_class; !p_extension_funcs->is_exposed, // bool legacy_unexposed_class;
nullptr, // GDExtensionClassNotification notification_func; nullptr, // GDExtensionClassNotification notification_func;
nullptr, // GDExtensionClassFreePropertyList free_property_list_func; nullptr, // GDExtensionClassFreePropertyList free_property_list_func;
nullptr, // GDExtensionClassCreateInstance2 create_instance_func; nullptr, // GDExtensionClassCreateInstance create_instance_func;
nullptr, // GDExtensionClassGetRID get_rid; p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance2 create_instance2_func;
nullptr, // GDExtensionClassGetVirtual get_virtual_func; nullptr, // GDExtensionClassGetRID get_rid_func;
nullptr, // GDExtensionClassGetVirtual get_virtual_func; nullptr, // GDExtensionClassGetVirtual get_virtual_func;
nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
}; };
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info5, &legacy); _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info6, &legacy);
}
void GDExtension::_register_extension_class5(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo5 *p_extension_funcs) {
const GDExtensionClassCreationInfo6 class_info6 = {
p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
p_extension_funcs->is_exposed, // GDExtensionBool is_exposed;
p_extension_funcs->is_runtime, // GDExtensionBool is_runtime;
p_extension_funcs->icon_path, // GDExtensionConstStringPtr icon_path;
p_extension_funcs->set_func, // GDExtensionClassSet set_func;
p_extension_funcs->get_func, // GDExtensionClassGet get_func;
p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList2 free_property_list_func;
p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func;
p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func;
p_extension_funcs->validate_property_func, // GDExtensionClassValidateProperty validate_property_func;
p_extension_funcs->notification_func, // GDExtensionClassNotification2 notification_func;
p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
nullptr, // GDExtensionClassCreateInstance3 create_instance_func; /* this one is mandatory */
p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func;
p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual2 get_virtual_func;
p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtualCallData2 get_virtual_call_data_func;
p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
p_extension_funcs->class_userdata, // void *class_userdata;
};
const ClassCreationDeprecatedInfo legacy = {
false, // bool legacy_unexposed_class;
nullptr, // GDExtensionClassNotification notification_func;
nullptr, // GDExtensionClassFreePropertyList free_property_list_func;
nullptr, // GDExtensionClassCreateInstance create_instance_func;
p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance2 create_instance2_func;
nullptr, // GDExtensionClassGetRID get_rid_func;
nullptr, // GDExtensionClassGetVirtual get_virtual_func;
nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
};
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info6, &legacy);
} }
#endif // DISABLE_DEPRECATED #endif // DISABLE_DEPRECATED
void GDExtension::_register_extension_class5(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo5 *p_extension_funcs) { void GDExtension::_register_extension_class6(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo6 *p_extension_funcs) {
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, p_extension_funcs); _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, p_extension_funcs);
} }
void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo5 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) { void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo6 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) {
GDExtension *self = reinterpret_cast<GDExtension *>(p_library); GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name); StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
@ -461,6 +530,7 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
extension->gdextension.notification = p_deprecated_funcs->notification_func; extension->gdextension.notification = p_deprecated_funcs->notification_func;
extension->gdextension.free_property_list = p_deprecated_funcs->free_property_list_func; extension->gdextension.free_property_list = p_deprecated_funcs->free_property_list_func;
extension->gdextension.create_instance = p_deprecated_funcs->create_instance_func; extension->gdextension.create_instance = p_deprecated_funcs->create_instance_func;
extension->gdextension.create_instance2 = p_deprecated_funcs->create_instance2_func;
extension->gdextension.get_rid = p_deprecated_funcs->get_rid_func; extension->gdextension.get_rid = p_deprecated_funcs->get_rid_func;
extension->gdextension.get_virtual = p_deprecated_funcs->get_virtual_func; extension->gdextension.get_virtual = p_deprecated_funcs->get_virtual_func;
extension->gdextension.get_virtual_call_data = p_deprecated_funcs->get_virtual_call_data_func; extension->gdextension.get_virtual_call_data = p_deprecated_funcs->get_virtual_call_data_func;
@ -471,7 +541,7 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
extension->gdextension.reference = p_extension_funcs->reference_func; extension->gdextension.reference = p_extension_funcs->reference_func;
extension->gdextension.unreference = p_extension_funcs->unreference_func; extension->gdextension.unreference = p_extension_funcs->unreference_func;
extension->gdextension.class_userdata = p_extension_funcs->class_userdata; extension->gdextension.class_userdata = p_extension_funcs->class_userdata;
extension->gdextension.create_instance2 = p_extension_funcs->create_instance_func; extension->gdextension.create_instance3 = p_extension_funcs->create_instance_func;
extension->gdextension.free_instance = p_extension_funcs->free_instance_func; extension->gdextension.free_instance = p_extension_funcs->free_instance_func;
extension->gdextension.recreate_instance = p_extension_funcs->recreate_instance_func; extension->gdextension.recreate_instance = p_extension_funcs->recreate_instance_func;
extension->gdextension.get_virtual2 = p_extension_funcs->get_virtual_func; extension->gdextension.get_virtual2 = p_extension_funcs->get_virtual_func;
@ -824,8 +894,9 @@ void GDExtension::initialize_gdextensions() {
register_interface_function("classdb_register_extension_class2", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class2); register_interface_function("classdb_register_extension_class2", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class2);
register_interface_function("classdb_register_extension_class3", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class3); register_interface_function("classdb_register_extension_class3", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class3);
register_interface_function("classdb_register_extension_class4", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class4); register_interface_function("classdb_register_extension_class4", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class4);
#endif // DISABLE_DEPRECATED
register_interface_function("classdb_register_extension_class5", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class5); register_interface_function("classdb_register_extension_class5", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class5);
#endif // DISABLE_DEPRECATED
register_interface_function("classdb_register_extension_class6", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class6);
register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method); register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method);
register_interface_function("classdb_register_extension_class_virtual_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_virtual_method); register_interface_function("classdb_register_extension_class_virtual_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_virtual_method);
register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant); register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant);
@ -844,68 +915,7 @@ void GDExtension::finalize_gdextensions() {
gdextension_interface_functions.clear(); gdextension_interface_functions.clear();
} }
Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, Ref<GDExtension> &p_extension) {
ERR_FAIL_COND_V_MSG(p_extension.is_valid() && p_extension->is_library_open(), ERR_ALREADY_IN_USE, "Cannot load GDExtension resource into already opened library.");
GDExtensionManager *extension_manager = GDExtensionManager::get_singleton();
GDExtensionManager::LoadStatus status = extension_manager->load_extension(p_path);
if (status != GDExtensionManager::LOAD_STATUS_OK && status != GDExtensionManager::LOAD_STATUS_ALREADY_LOADED) {
// Errors already logged in load_extension().
return FAILED;
}
p_extension = extension_manager->get_extension(p_path);
return OK;
}
Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
// We can't have two GDExtension resource object representing the same library, because
// loading (or unloading) a GDExtension affects global data. So, we need reuse the same
// object if one has already been loaded (even if caching is disabled at the resource
// loader level).
GDExtensionManager *manager = GDExtensionManager::get_singleton();
if (manager->is_extension_loaded(p_path)) {
return manager->get_extension(p_path);
}
Ref<GDExtension> lib;
Error err = load_gdextension_resource(p_path, lib);
if (err != OK && r_error) {
// Errors already logged in load_gdextension_resource().
*r_error = err;
}
return lib;
}
void GDExtensionResourceLoader::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("gdextension");
}
bool GDExtensionResourceLoader::handles_type(const String &p_type) const {
return p_type == "GDExtension";
}
String GDExtensionResourceLoader::get_resource_type(const String &p_path) const {
if (p_path.has_extension("gdextension")) {
return "GDExtension";
}
return "";
}
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
void GDExtensionResourceLoader::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
Ref<GDExtension> gdext = ResourceLoader::load(p_path);
if (gdext.is_null()) {
return;
}
for (const StringName class_name : gdext->get_classes_used()) {
if (ClassDB::class_exists(class_name)) {
r_classes->insert(class_name);
}
}
}
bool GDExtension::has_library_changed() const { bool GDExtension::has_library_changed() const {
return loader->has_library_changed(); return loader->has_library_changed();
@ -950,7 +960,7 @@ void GDExtension::prepare_reload() {
state.push_back(Pair<String, Variant>(P.name, value)); state.push_back(Pair<String, Variant>(P.name, value));
} }
E.value.instance_state[obj_id] = { E.value.instance_state[obj_id] = {
state, // List<Pair<String, Variant>> properties; std::move(state), // List<Pair<String, Variant>> properties;
obj->is_extension_placeholder(), // bool is_placeholder; obj->is_extension_placeholder(), // bool is_placeholder;
}; };
} }

View file

@ -32,9 +32,7 @@
#include "core/extension/gdextension_interface.gen.h" #include "core/extension/gdextension_interface.gen.h"
#include "core/extension/gdextension_loader.h" #include "core/extension/gdextension_loader.h"
#include "core/io/config_file.h" #include "core/io/resource.h"
#include "core/io/resource_loader.h"
#include "core/object/ref_counted.h"
class GDExtensionMethodBind; class GDExtensionMethodBind;
@ -71,6 +69,7 @@ class GDExtension : public Resource {
GDExtensionClassNotification notification_func = nullptr; GDExtensionClassNotification notification_func = nullptr;
GDExtensionClassFreePropertyList free_property_list_func = nullptr; GDExtensionClassFreePropertyList free_property_list_func = nullptr;
GDExtensionClassCreateInstance create_instance_func = nullptr; GDExtensionClassCreateInstance create_instance_func = nullptr;
GDExtensionClassCreateInstance2 create_instance2_func = nullptr;
GDExtensionClassGetRID get_rid_func = nullptr; GDExtensionClassGetRID get_rid_func = nullptr;
GDExtensionClassGetVirtual get_virtual_func = nullptr; GDExtensionClassGetVirtual get_virtual_func = nullptr;
GDExtensionClassGetVirtualCallData get_virtual_call_data_func = nullptr; GDExtensionClassGetVirtualCallData get_virtual_call_data_func = nullptr;
@ -82,9 +81,10 @@ class GDExtension : public Resource {
static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs); static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs);
static void _register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs); static void _register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs);
static void _register_extension_class4(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs); static void _register_extension_class4(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs);
#endif // DISABLE_DEPRECATED
static void _register_extension_class5(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo5 *p_extension_funcs); static void _register_extension_class5(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo5 *p_extension_funcs);
static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo5 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr); #endif // DISABLE_DEPRECATED
static void _register_extension_class6(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo6 *p_extension_funcs);
static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo6 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr);
static void _register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info); static void _register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info);
static void _register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info); static void _register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info);
static void _register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield); static void _register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield);
@ -182,21 +182,6 @@ public:
VARIANT_ENUM_CAST(GDExtension::InitializationLevel) VARIANT_ENUM_CAST(GDExtension::InitializationLevel)
class GDExtensionResourceLoader : public ResourceFormatLoader {
GDSOFTCLASS(GDExtensionResourceLoader, ResourceFormatLoader);
public:
static Error load_gdextension_resource(const String &p_path, Ref<GDExtension> &p_extension);
virtual Ref<Resource> load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override;
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual bool handles_type(const String &p_type) const override;
virtual String get_resource_type(const String &p_path) const override;
#ifdef TOOLS_ENABLED
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes) override;
#endif // TOOLS_ENABLED
};
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
class GDExtensionEditorPlugins { class GDExtensionEditorPlugins {
private: private:

View file

@ -30,7 +30,7 @@
#include "gdextension_function_loader.h" #include "gdextension_function_loader.h"
#include "gdextension.h" #include "core/extension/gdextension.h"
Error GDExtensionFunctionLoader::open_library(const String &p_path) { Error GDExtensionFunctionLoader::open_library(const String &p_path) {
ERR_FAIL_COND_V_MSG(!p_path.begins_with("libgodot://"), ERR_FILE_NOT_FOUND, "Function based GDExtensions should have a path starting with libgodot://"); ERR_FAIL_COND_V_MSG(!p_path.begins_with("libgodot://"), ERR_FILE_NOT_FOUND, "Function based GDExtensions should have a path starting with libgodot://");

View file

@ -31,7 +31,6 @@
#pragma once #pragma once
#include "core/extension/gdextension_loader.h" #include "core/extension/gdextension_loader.h"
#include "core/os/shared_object.h"
class GDExtension; class GDExtension;

View file

@ -77,7 +77,7 @@ class CallableCustomExtension : public CallableCustom {
const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b); const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);
if (a->call_func != b->call_func) { if (a->call_func != b->call_func) {
return a->call_func < b->call_func; return (uintptr_t)a->call_func < (uintptr_t)b->call_func;
} }
return a->userdata < b->userdata; return a->userdata < b->userdata;
} }
@ -547,6 +547,11 @@ static void gdextension_variant_get_type_name(GDExtensionVariantType p_type, GDE
memnew_placement(r_ret, String(name)); memnew_placement(r_ret, String(name));
} }
static GDExtensionVariantType gdextension_variant_get_type_by_name(GDExtensionConstStringPtr p_type) {
const String *type = (const String *)p_type;
return static_cast<GDExtensionVariantType>(Variant::get_type_by_name(*type));
}
static GDExtensionBool gdextension_variant_can_convert(GDExtensionVariantType p_from, GDExtensionVariantType p_to) { static GDExtensionBool gdextension_variant_can_convert(GDExtensionVariantType p_from, GDExtensionVariantType p_to) {
return Variant::can_convert((Variant::Type)p_from, (Variant::Type)p_to); return Variant::can_convert((Variant::Type)p_from, (Variant::Type)p_to);
} }
@ -1395,6 +1400,7 @@ static GDExtensionBool gdextension_object_get_class_name(GDExtensionConstObjectP
return true; return true;
} }
#ifndef DISABLE_DEPRECATED
static GDExtensionObjectPtr gdextension_object_cast_to(GDExtensionConstObjectPtr p_object, void *p_class_tag) { static GDExtensionObjectPtr gdextension_object_cast_to(GDExtensionConstObjectPtr p_object, void *p_class_tag) {
if (!p_object) { if (!p_object) {
return nullptr; return nullptr;
@ -1403,6 +1409,7 @@ static GDExtensionObjectPtr gdextension_object_cast_to(GDExtensionConstObjectPtr
return o->is_class_ptr(p_class_tag) ? (GDExtensionObjectPtr)o : (GDExtensionObjectPtr) nullptr; return o->is_class_ptr(p_class_tag) ? (GDExtensionObjectPtr)o : (GDExtensionObjectPtr) nullptr;
} }
#endif
static GDObjectInstanceID gdextension_object_get_instance_id(GDExtensionConstObjectPtr p_object) { static GDObjectInstanceID gdextension_object_get_instance_id(GDExtensionConstObjectPtr p_object) {
const Object *o = (const Object *)p_object; const Object *o = (const Object *)p_object;
@ -1652,18 +1659,25 @@ static GDExtensionObjectPtr gdextension_classdb_construct_object(GDExtensionCons
const StringName classname = *reinterpret_cast<const StringName *>(p_classname); const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
return (GDExtensionObjectPtr)ClassDB::instantiate_no_placeholders(classname); return (GDExtensionObjectPtr)ClassDB::instantiate_no_placeholders(classname);
} }
#endif
static GDExtensionObjectPtr gdextension_classdb_construct_object2(GDExtensionConstStringNamePtr p_classname) { static GDExtensionObjectPtr gdextension_classdb_construct_object2(GDExtensionConstStringNamePtr p_classname) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname); const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
return (GDExtensionObjectPtr)ClassDB::instantiate_without_postinitialization(classname); return (GDExtensionObjectPtr)ClassDB::instantiate_without_postinitialization(classname);
} }
#endif
static GDExtensionObjectPtr gdextension_classdb_construct_object3(GDExtensionConstStringNamePtr p_classname) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
return (GDExtensionObjectPtr)ClassDB::instantiate_without_postinitialization_with_refcount(classname);
}
#ifndef DISABLE_DEPRECATED
static void *gdextension_classdb_get_class_tag(GDExtensionConstStringNamePtr p_classname) { static void *gdextension_classdb_get_class_tag(GDExtensionConstStringNamePtr p_classname) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname); const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(classname); ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(classname);
return class_info ? class_info->class_ptr : nullptr; return class_info ? class_info->class_ptr : nullptr;
} }
#endif
static void gdextension_editor_add_plugin(GDExtensionConstStringNamePtr p_classname) { static void gdextension_editor_add_plugin(GDExtensionConstStringNamePtr p_classname) {
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
@ -1743,6 +1757,7 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(variant_has_key); REGISTER_INTERFACE_FUNC(variant_has_key);
REGISTER_INTERFACE_FUNC(variant_get_object_instance_id); REGISTER_INTERFACE_FUNC(variant_get_object_instance_id);
REGISTER_INTERFACE_FUNC(variant_get_type_name); REGISTER_INTERFACE_FUNC(variant_get_type_name);
REGISTER_INTERFACE_FUNC(variant_get_type_by_name);
REGISTER_INTERFACE_FUNC(variant_can_convert); REGISTER_INTERFACE_FUNC(variant_can_convert);
REGISTER_INTERFACE_FUNC(variant_can_convert_strict); REGISTER_INTERFACE_FUNC(variant_can_convert_strict);
REGISTER_INTERFACE_FUNC(get_variant_from_type_constructor); REGISTER_INTERFACE_FUNC(get_variant_from_type_constructor);
@ -1833,7 +1848,9 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(object_free_instance_binding); REGISTER_INTERFACE_FUNC(object_free_instance_binding);
REGISTER_INTERFACE_FUNC(object_set_instance); REGISTER_INTERFACE_FUNC(object_set_instance);
REGISTER_INTERFACE_FUNC(object_get_class_name); REGISTER_INTERFACE_FUNC(object_get_class_name);
#ifndef DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(object_cast_to); REGISTER_INTERFACE_FUNC(object_cast_to);
#endif // DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(object_get_instance_from_id); REGISTER_INTERFACE_FUNC(object_get_instance_from_id);
REGISTER_INTERFACE_FUNC(object_get_instance_id); REGISTER_INTERFACE_FUNC(object_get_instance_id);
REGISTER_INTERFACE_FUNC(object_has_script_method); REGISTER_INTERFACE_FUNC(object_has_script_method);
@ -1856,10 +1873,13 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(callable_custom_get_userdata); REGISTER_INTERFACE_FUNC(callable_custom_get_userdata);
#ifndef DISABLE_DEPRECATED #ifndef DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(classdb_construct_object); REGISTER_INTERFACE_FUNC(classdb_construct_object);
#endif // DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(classdb_construct_object2); REGISTER_INTERFACE_FUNC(classdb_construct_object2);
#endif // DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(classdb_construct_object3);
REGISTER_INTERFACE_FUNC(classdb_get_method_bind); REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
#ifndef DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(classdb_get_class_tag); REGISTER_INTERFACE_FUNC(classdb_get_class_tag);
#endif // DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(editor_add_plugin); REGISTER_INTERFACE_FUNC(editor_add_plugin);
REGISTER_INTERFACE_FUNC(editor_remove_plugin); REGISTER_INTERFACE_FUNC(editor_remove_plugin);
REGISTER_INTERFACE_FUNC(editor_help_load_xml_from_utf8_chars); REGISTER_INTERFACE_FUNC(editor_help_load_xml_from_utf8_chars);

View file

@ -986,7 +986,11 @@
"name": "p_list", "name": "p_list",
"type": "const GDExtensionPropertyInfo*" "type": "const GDExtensionPropertyInfo*"
} }
] ],
"deprecated": {
"since": "4.3",
"replace_with": "GDExtensionClassFreePropertyList2"
}
}, },
{ {
"name": "GDExtensionClassFreePropertyList2", "name": "GDExtensionClassFreePropertyList2",
@ -1164,7 +1168,15 @@
"name": "p_class_userdata", "name": "p_class_userdata",
"type": "void*" "type": "void*"
} }
] ],
"description": [
"Called to construct an instance of the class.",
"For classes descending from RefCounted, the reference count should be zero."
],
"deprecated": {
"since": "4.4",
"replace_with": "GDExtensionClassCreateInstance3"
}
}, },
{ {
"name": "GDExtensionClassCreateInstance2", "name": "GDExtensionClassCreateInstance2",
@ -1181,6 +1193,35 @@
"name": "p_notify_postinitialize", "name": "p_notify_postinitialize",
"type": "GDExtensionBool" "type": "GDExtensionBool"
} }
],
"description": [
"Called to construct an instance of the class.",
"For classes descending from RefCounted, the reference count should be zero."
],
"deprecated": {
"since": "4.7",
"replace_with": "GDExtensionClassCreateInstance3"
}
},
{
"name": "GDExtensionClassCreateInstance3",
"kind": "function",
"return_value": {
"type": "GDExtensionObjectPtr"
},
"arguments": [
{
"name": "p_class_userdata",
"type": "void*"
},
{
"name": "p_notify_postinitialize",
"type": "GDExtensionBool"
}
],
"description": [
"Called to construct an instance of the class.",
"For classes descending from RefCounted, the reference count should already be incremented by 1."
] ]
}, },
{ {
@ -1229,7 +1270,11 @@
"name": "p_name", "name": "p_name",
"type": "GDExtensionConstStringNamePtr" "type": "GDExtensionConstStringNamePtr"
} }
] ],
"deprecated": {
"since": "4.4",
"replace_with": "GDExtensionClassGetVirtual2"
}
}, },
{ {
"name": "GDExtensionClassGetVirtual2", "name": "GDExtensionClassGetVirtual2",
@ -1267,7 +1312,11 @@
"name": "p_name", "name": "p_name",
"type": "GDExtensionConstStringNamePtr" "type": "GDExtensionConstStringNamePtr"
} }
] ],
"deprecated": {
"since": "4.4",
"replace_with": "GDExtensionClassGetVirtualCallData2"
}
}, },
{ {
"name": "GDExtensionClassGetVirtualCallData2", "name": "GDExtensionClassGetVirtualCallData2",
@ -1372,7 +1421,7 @@
"name": "create_instance_func", "name": "create_instance_func",
"type": "GDExtensionClassCreateInstance", "type": "GDExtensionClassCreateInstance",
"description": [ "description": [
"(Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract." "Class constructor. Required unless the class is virtual or abstract."
] ]
}, },
{ {
@ -1403,7 +1452,7 @@
], ],
"deprecated": { "deprecated": {
"since": "4.2", "since": "4.2",
"replace_with": "GDExtensionClassCreationInfo4" "replace_with": "GDExtensionClassCreationInfo6"
} }
}, },
{ {
@ -1470,7 +1519,7 @@
"name": "create_instance_func", "name": "create_instance_func",
"type": "GDExtensionClassCreateInstance", "type": "GDExtensionClassCreateInstance",
"description": [ "description": [
"(Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract." "Class constructor. Required unless the class is virtual or abstract."
] ]
}, },
{ {
@ -1524,7 +1573,7 @@
], ],
"deprecated": { "deprecated": {
"since": "4.3", "since": "4.3",
"replace_with": "GDExtensionClassCreationInfo4" "replace_with": "GDExtensionClassCreationInfo6"
} }
}, },
{ {
@ -1595,7 +1644,7 @@
"name": "create_instance_func", "name": "create_instance_func",
"type": "GDExtensionClassCreateInstance", "type": "GDExtensionClassCreateInstance",
"description": [ "description": [
"(Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract." "Class constructor. Required unless the class is virtual or abstract."
] ]
}, },
{ {
@ -1649,7 +1698,7 @@
], ],
"deprecated": { "deprecated": {
"since": "4.4", "since": "4.4",
"replace_with": "GDExtensionClassCreationInfo4" "replace_with": "GDExtensionClassCreationInfo6"
} }
}, },
{ {
@ -1724,7 +1773,141 @@
"name": "create_instance_func", "name": "create_instance_func",
"type": "GDExtensionClassCreateInstance2", "type": "GDExtensionClassCreateInstance2",
"description": [ "description": [
"(Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract." "Class constructor. Required unless the class is virtual or abstract."
]
},
{
"name": "free_instance_func",
"type": "GDExtensionClassFreeInstance",
"description": [
"Destructor; mandatory."
]
},
{
"name": "recreate_instance_func",
"type": "GDExtensionClassRecreateInstance"
},
{
"name": "get_virtual_func",
"type": "GDExtensionClassGetVirtual2",
"description": [
"Queries a virtual function by name and returns a callback to invoke the requested virtual function."
]
},
{
"name": "get_virtual_call_data_func",
"type": "GDExtensionClassGetVirtualCallData2",
"description": [
"Paired with `call_virtual_with_data_func`, this is an alternative to `get_virtual_func` for extensions that",
"need or benefit from extra data when calling virtual functions.",
"Returns user data that will be passed to `call_virtual_with_data_func`.",
"Returning `NULL` from this function signals to Godot that the virtual function is not overridden.",
"Data returned from this function should be managed by the extension and must be valid until the extension is deinitialized.",
"You should supply either `get_virtual_func`, or `get_virtual_call_data_func` with `call_virtual_with_data_func`."
]
},
{
"name": "call_virtual_with_data_func",
"type": "GDExtensionClassCallVirtualWithData",
"description": [
"Used to call virtual functions when `get_virtual_call_data_func` is not null."
]
},
{
"name": "class_userdata",
"type": "void*",
"description": [
"Per-class user data, later accessible in instance bindings."
]
}
],
"deprecated": {
"since": "4.5",
"replace_with": "GDExtensionClassCreationInfo6"
}
},
{
"name": "GDExtensionClassCreationInfo5",
"kind": "alias",
"type": "GDExtensionClassCreationInfo4",
"deprecated": {
"since": "4.7",
"replace_with": "GDExtensionClassCreationInfo6"
}
},
{
"name": "GDExtensionClassCreationInfo6",
"kind": "struct",
"members": [
{
"name": "is_virtual",
"type": "GDExtensionBool"
},
{
"name": "is_abstract",
"type": "GDExtensionBool"
},
{
"name": "is_exposed",
"type": "GDExtensionBool"
},
{
"name": "is_runtime",
"type": "GDExtensionBool"
},
{
"name": "icon_path",
"type": "GDExtensionConstStringPtr"
},
{
"name": "set_func",
"type": "GDExtensionClassSet"
},
{
"name": "get_func",
"type": "GDExtensionClassGet"
},
{
"name": "get_property_list_func",
"type": "GDExtensionClassGetPropertyList"
},
{
"name": "free_property_list_func",
"type": "GDExtensionClassFreePropertyList2"
},
{
"name": "property_can_revert_func",
"type": "GDExtensionClassPropertyCanRevert"
},
{
"name": "property_get_revert_func",
"type": "GDExtensionClassPropertyGetRevert"
},
{
"name": "validate_property_func",
"type": "GDExtensionClassValidateProperty"
},
{
"name": "notification_func",
"type": "GDExtensionClassNotification2"
},
{
"name": "to_string_func",
"type": "GDExtensionClassToString"
},
{
"name": "reference_func",
"type": "GDExtensionClassReference"
},
{
"name": "unreference_func",
"type": "GDExtensionClassUnreference"
},
{
"name": "create_instance_func",
"type": "GDExtensionClassCreateInstance3",
"description": [
"Class constructor. Required unless the class is virtual or abstract."
] ]
}, },
{ {
@ -1773,11 +1956,6 @@
} }
] ]
}, },
{
"name": "GDExtensionClassCreationInfo5",
"kind": "alias",
"type": "GDExtensionClassCreationInfo4"
},
{ {
"name": "GDExtensionClassLibraryPtr", "name": "GDExtensionClassLibraryPtr",
"kind": "handle" "kind": "handle"
@ -3375,7 +3553,11 @@
"name": "string", "name": "string",
"type": "const char*" "type": "const char*"
} }
] ],
"deprecated": {
"since": "4.5",
"replace_with": "GDExtensionGodotVersion2"
}
}, },
{ {
"name": "GDExtensionGodotVersion2", "name": "GDExtensionGodotVersion2",
@ -4978,6 +5160,28 @@
], ],
"since": "4.1" "since": "4.1"
}, },
{
"name": "variant_get_type_by_name",
"return_value": {
"type": "GDExtensionVariantType",
"description": [
"The variant type for the given name; otherwise VARIANT_MAX if name is invalid."
]
},
"arguments": [
{
"name": "p_type_name",
"type": "GDExtensionConstStringPtr",
"description": [
"The variant type name."
]
}
],
"description": [
"Gets the Variant type by name."
],
"since": "4.7"
},
{ {
"name": "variant_can_convert", "name": "variant_can_convert",
"return_value": { "return_value": {
@ -5859,7 +6063,7 @@
"return_value": { "return_value": {
"type": "GDExtensionInt", "type": "GDExtensionInt",
"description": [ "description": [
"The resulting encoded string length in characters (not bytes), not including a null terminator." "The resulting encoded string length in characters, not including a null terminator. Characters that cannot be converted to Latin-1 are replaced with a space."
] ]
}, },
"arguments": [ "arguments": [
@ -5896,7 +6100,7 @@
"return_value": { "return_value": {
"type": "GDExtensionInt", "type": "GDExtensionInt",
"description": [ "description": [
"The resulting encoded string length in characters (not bytes), not including a null terminator." "The resulting encoded string length in bytes (not characters), not including a null terminator."
] ]
}, },
"arguments": [ "arguments": [
@ -5933,7 +6137,7 @@
"return_value": { "return_value": {
"type": "GDExtensionInt", "type": "GDExtensionInt",
"description": [ "description": [
"The resulting encoded string length in characters (not bytes), not including a null terminator." "The resulting encoded string length in 16-bit code units (not bytes or characters), not including a null terminator."
] ]
}, },
"arguments": [ "arguments": [
@ -6007,7 +6211,7 @@
"return_value": { "return_value": {
"type": "GDExtensionInt", "type": "GDExtensionInt",
"description": [ "description": [
"The resulting encoded string length in characters (not bytes), not including a null terminator." "The resulting encoded string length in characters (for UTF-32) or 16-bit code units (for UTF-16), depending on the wchar_t representation. Does not include a null terminator."
] ]
}, },
"arguments": [ "arguments": [
@ -7746,7 +7950,11 @@
"description": [ "description": [
"Casts an Object to a different type." "Casts an Object to a different type."
], ],
"since": "4.1" "since": "4.1",
"deprecated": {
"since": "4.7",
"message": "Use the `is_class` method on `Object` to check if an object can be cast instead. If true, the previous pointer can be reinterpreted as a pointer to the target type."
}
}, },
{ {
"name": "object_get_instance_from_id", "name": "object_get_instance_from_id",
@ -8242,7 +8450,7 @@
"since": "4.1", "since": "4.1",
"deprecated": { "deprecated": {
"since": "4.4", "since": "4.4",
"replace_with": "classdb_construct_object2" "replace_with": "classdb_construct_object3"
} }
}, },
{ {
@ -8268,7 +8476,37 @@
"", "",
"\"NOTIFICATION_POSTINITIALIZE\" must be sent after construction." "\"NOTIFICATION_POSTINITIALIZE\" must be sent after construction."
], ],
"since": "4.4" "since": "4.4",
"deprecated": {
"since": "4.7",
"replace_with": "classdb_construct_object3"
}
},
{
"name": "classdb_construct_object3",
"return_value": {
"type": "GDExtensionObjectPtr",
"description": [
"A pointer to the newly created Object."
]
},
"arguments": [
{
"name": "p_classname",
"type": "GDExtensionConstStringNamePtr",
"description": [
"A pointer to a StringName with the class name."
]
}
],
"description": [
"Constructs an Object of the requested class.",
"The passed class must be a built-in godot class, or an already-registered extension class. In both cases, object_set_instance() should be called to fully initialize the object.",
"If the type is a subtype of RefCounted, it already has a refcount of 1. The caller must take ownership the refcount and is responsible for decrementing it again when the object is no longer needed.",
"",
"\"NOTIFICATION_POSTINITIALIZE\" must be sent after construction."
],
"since": "4.7"
}, },
{ {
"name": "classdb_get_method_bind", "name": "classdb_get_method_bind",
@ -8326,7 +8564,11 @@
"description": [ "description": [
"Gets a pointer uniquely identifying the given built-in class in the ClassDB." "Gets a pointer uniquely identifying the given built-in class in the ClassDB."
], ],
"since": "4.1" "since": "4.1",
"deprecated": {
"since": "4.7",
"message": "No longer needed. Use the `is_class` method on `Object` instead."
}
}, },
{ {
"name": "classdb_register_extension_class", "name": "classdb_register_extension_class",
@ -8367,7 +8609,7 @@
"since": "4.1", "since": "4.1",
"deprecated": { "deprecated": {
"since": "4.2", "since": "4.2",
"replace_with": "classdb_register_extension_class5" "replace_with": "classdb_register_extension_class6"
} }
}, },
{ {
@ -8409,7 +8651,7 @@
"since": "4.2", "since": "4.2",
"deprecated": { "deprecated": {
"since": "4.3", "since": "4.3",
"replace_with": "classdb_register_extension_class5" "replace_with": "classdb_register_extension_class6"
} }
}, },
{ {
@ -8451,7 +8693,7 @@
"since": "4.3", "since": "4.3",
"deprecated": { "deprecated": {
"since": "4.4", "since": "4.4",
"replace_with": "classdb_register_extension_class5" "replace_with": "classdb_register_extension_class6"
} }
}, },
{ {
@ -8493,7 +8735,7 @@
"since": "4.4", "since": "4.4",
"deprecated": { "deprecated": {
"since": "4.5", "since": "4.5",
"replace_with": "classdb_register_extension_class5" "replace_with": "classdb_register_extension_class6"
} }
}, },
{ {
@ -8532,7 +8774,49 @@
"Registers an extension class in the ClassDB.", "Registers an extension class in the ClassDB.",
"Provided struct can be safely freed once the function returns." "Provided struct can be safely freed once the function returns."
], ],
"since": "4.5" "since": "4.5",
"deprecated": {
"since": "4.7",
"replace_with": "classdb_register_extension_class6"
}
},
{
"name": "classdb_register_extension_class6",
"arguments": [
{
"name": "p_library",
"type": "GDExtensionClassLibraryPtr",
"description": [
"A pointer the library received by the GDExtension's entry point function."
]
},
{
"name": "p_class_name",
"type": "GDExtensionConstStringNamePtr",
"description": [
"A pointer to a StringName with the class name."
]
},
{
"name": "p_parent_class_name",
"type": "GDExtensionConstStringNamePtr",
"description": [
"A pointer to a StringName with the parent class name."
]
},
{
"name": "p_extension_funcs",
"type": "const GDExtensionClassCreationInfo6*",
"description": [
"A pointer to a GDExtensionClassCreationInfo6 struct."
]
}
],
"description": [
"Registers an extension class in the ClassDB.",
"Provided struct can be safely freed once the function returns."
],
"since": "4.7"
}, },
{ {
"name": "classdb_register_extension_class_method", "name": "classdb_register_extension_class_method",

View file

@ -32,8 +32,8 @@
#include "gdextension_interface_header_generator.h" #include "gdextension_interface_header_generator.h"
#include "core/extension/gdextension_interface_dump.gen.h"
#include "core/io/json.h" #include "core/io/json.h"
#include "gdextension_interface_dump.gen.h"
static const char *FILE_HEADER = static const char *FILE_HEADER =
"/**************************************************************************/\n" "/**************************************************************************/\n"

View file

@ -30,10 +30,12 @@
#include "gdextension_library_loader.h" #include "gdextension_library_loader.h"
#include "core/config/engine.h"
#include "core/config/project_settings.h" #include "core/config/project_settings.h"
#include "core/extension/gdextension.h"
#include "core/io/dir_access.h" #include "core/io/dir_access.h"
#include "core/os/os.h"
#include "core/version.h" #include "core/version.h"
#include "gdextension.h"
Vector<SharedObject> GDExtensionLibraryLoader::find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature) { Vector<SharedObject> GDExtensionLibraryLoader::find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature) {
Vector<SharedObject> dependencies_shared_objects; Vector<SharedObject> dependencies_shared_objects;
@ -198,7 +200,10 @@ Error GDExtensionLibraryLoader::open_library(const String &p_path) {
// Apple has a complex lookup system which goes beyond looking up the filename, so we try that first. // Apple has a complex lookup system which goes beyond looking up the filename, so we try that first.
err = OS::get_singleton()->open_dynamic_library(abs_path, library, &data); err = OS::get_singleton()->open_dynamic_library(abs_path, library, &data);
if (err != OK) { if (err != OK) {
#ifdef APPLE_EMBEDDED_ENABLED
err = OS::get_singleton()->open_dynamic_library(String(), library, &data); err = OS::get_singleton()->open_dynamic_library(String(), library, &data);
#endif
if (err != OK) { if (err != OK) {
return err; return err;
} }

View file

@ -30,12 +30,12 @@
#pragma once #pragma once
#include <functional>
#include "core/extension/gdextension_loader.h" #include "core/extension/gdextension_loader.h"
#include "core/io/config_file.h" #include "core/io/config_file.h"
#include "core/os/shared_object.h" #include "core/os/shared_object.h"
#include <functional>
class GDExtensionLibraryLoader : public GDExtensionLoader { class GDExtensionLibraryLoader : public GDExtensionLoader {
GDSOFTCLASS(GDExtensionLibraryLoader, GDExtensionLoader); GDSOFTCLASS(GDExtensionLibraryLoader, GDExtensionLoader);

View file

@ -30,12 +30,16 @@
#include "gdextension_manager.h" #include "gdextension_manager.h"
#include "core/config/engine.h"
#include "core/extension/gdextension_function_loader.h" #include "core/extension/gdextension_function_loader.h"
#include "core/extension/gdextension_library_loader.h" #include "core/extension/gdextension_library_loader.h"
#include "core/extension/gdextension_special_compat_hashes.h" #include "core/extension/gdextension_special_compat_hashes.h"
#include "core/io/dir_access.h" #include "core/io/dir_access.h"
#include "core/io/file_access.h" #include "core/io/file_access.h"
#include "core/object/callable_mp.h"
#include "core/object/class_db.h"
#include "core/object/script_language.h" #include "core/object/script_language.h"
#include "core/os/os.h"
GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref<GDExtension> &p_extension, bool p_first_load) { GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref<GDExtension> &p_extension, bool p_first_load) {
if (level >= 0) { // Already initialized up to some level. if (level >= 0) { // Already initialized up to some level.
@ -477,8 +481,8 @@ void GDExtensionManager::_bind_methods() {
BIND_ENUM_CONSTANT(LOAD_STATUS_NEEDS_RESTART); BIND_ENUM_CONSTANT(LOAD_STATUS_NEEDS_RESTART);
ADD_SIGNAL(MethodInfo("extensions_reloaded")); ADD_SIGNAL(MethodInfo("extensions_reloaded"));
ADD_SIGNAL(MethodInfo("extension_loaded", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension"))); ADD_SIGNAL(MethodInfo("extension_loaded", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, GDExtension::get_class_static())));
ADD_SIGNAL(MethodInfo("extension_unloading", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension"))); ADD_SIGNAL(MethodInfo("extension_unloading", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, GDExtension::get_class_static())));
} }
GDExtensionManager::GDExtensionManager() { GDExtensionManager::GDExtensionManager() {

View file

@ -0,0 +1,98 @@
/**************************************************************************/
/* gdextension_resource_format.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "gdextension_resource_format.h"
#include "core/extension/gdextension_manager.h"
#include "core/object/class_db.h"
Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
// We can't have two GDExtension resource object representing the same library, because
// loading (or unloading) a GDExtension affects global data. So, we need reuse the same
// object if one has already been loaded (even if caching is disabled at the resource
// loader level).
GDExtensionManager *manager = GDExtensionManager::get_singleton();
if (manager->is_extension_loaded(p_path)) {
return manager->get_extension(p_path);
}
Ref<GDExtension> lib;
Error err = load_gdextension_resource(p_path, lib);
if (err != OK && r_error) {
// Errors already logged in load_gdextension_resource().
*r_error = err;
}
return lib;
}
void GDExtensionResourceLoader::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("gdextension");
}
bool GDExtensionResourceLoader::handles_type(const String &p_type) const {
return p_type == "GDExtension";
}
String GDExtensionResourceLoader::get_resource_type(const String &p_path) const {
if (p_path.has_extension("gdextension")) {
return "GDExtension";
}
return "";
}
Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, Ref<GDExtension> &p_extension) {
ERR_FAIL_COND_V_MSG(p_extension.is_valid() && p_extension->is_library_open(), ERR_ALREADY_IN_USE, "Cannot load GDExtension resource into already opened library.");
GDExtensionManager *extension_manager = GDExtensionManager::get_singleton();
GDExtensionManager::LoadStatus status = extension_manager->load_extension(p_path);
if (status != GDExtensionManager::LOAD_STATUS_OK && status != GDExtensionManager::LOAD_STATUS_ALREADY_LOADED) {
// Errors already logged in load_extension().
return FAILED;
}
p_extension = extension_manager->get_extension(p_path);
return OK;
}
#ifdef TOOLS_ENABLED
void GDExtensionResourceLoader::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
Ref<GDExtension> gdext = ResourceLoader::load(p_path);
if (gdext.is_null()) {
return;
}
for (const StringName class_name : gdext->get_classes_used()) {
if (ClassDB::class_exists(class_name)) {
r_classes->insert(class_name);
}
}
}
#endif // TOOLS_ENABLED

View file

@ -0,0 +1,49 @@
/**************************************************************************/
/* gdextension_resource_format.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. */
/**************************************************************************/
#pragma once
#include "core/extension/gdextension.h"
#include "core/io/resource_loader.h"
class GDExtensionResourceLoader : public ResourceFormatLoader {
GDSOFTCLASS(GDExtensionResourceLoader, ResourceFormatLoader);
public:
static Error load_gdextension_resource(const String &p_path, Ref<GDExtension> &p_extension);
virtual Ref<Resource> load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override;
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual bool handles_type(const String &p_type) const override;
virtual String get_resource_type(const String &p_path) const override;
#ifdef TOOLS_ENABLED
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes) override;
#endif // TOOLS_ENABLED
};

View file

@ -31,7 +31,9 @@
#include "godot_instance.h" #include "godot_instance.h"
#include "core/extension/gdextension_manager.h" #include "core/extension/gdextension_manager.h"
#include "core/object/class_db.h"
#include "core/os/main_loop.h" #include "core/os/main_loop.h"
#include "core/os/os.h"
#include "main/main.h" #include "main/main.h"
#include "servers/display/display_server.h" #include "servers/display/display_server.h"

View file

@ -31,7 +31,7 @@
#pragma once #pragma once
#include "core/extension/gdextension_interface.gen.h" #include "core/extension/gdextension_interface.gen.h"
#include "core/object/class_db.h" #include "core/object/object.h"
class GodotInstance : public Object { class GodotInstance : public Object {
GDCLASS(GodotInstance, Object); GDCLASS(GodotInstance, Object);

View file

@ -30,7 +30,7 @@
#pragma once #pragma once
#include "gdextension_interface.gen.h" #include "core/extension/gdextension_interface.gen.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {

View file

@ -17,7 +17,7 @@ def run(target, source, env):
inline constexpr int _gdextension_interface_data_compressed_size = {len(buffer)}; inline constexpr int _gdextension_interface_data_compressed_size = {len(buffer)};
inline constexpr int _gdextension_interface_data_uncompressed_size = {decomp_size}; inline constexpr int _gdextension_interface_data_uncompressed_size = {decomp_size};
inline constexpr unsigned char _gdextension_interface_data_compressed[] = {{ inline constexpr unsigned char _gdextension_interface_data_compressed[] = {{
{methods.format_buffer(buffer, 1)} {methods.format_buffer(buffer, 1)}
}}; }};
class GDExtensionInterfaceDump {{ class GDExtensionInterfaceDump {{

View file

@ -30,6 +30,10 @@
#ifndef DISABLE_DEPRECATED #ifndef DISABLE_DEPRECATED
#include "input.h"
#include "core/object/class_db.h"
void Input::_vibrate_handheld_bind_compat_91143(int p_duration_ms) { void Input::_vibrate_handheld_bind_compat_91143(int p_duration_ms) {
vibrate_handheld(p_duration_ms, -1.0); vibrate_handheld(p_duration_ms, -1.0);
} }

View file

@ -31,15 +31,21 @@
#include "input.h" #include "input.h"
#include "input.compat.inc" #include "input.compat.inc"
#include "core/config/engine.h"
#include "core/config/project_settings.h" #include "core/config/project_settings.h"
#include "core/input/default_controller_mappings.h" #include "core/input/default_controller_mappings.h"
#include "core/input/input_map.h" #include "core/input/input_map.h"
#include "core/object/class_db.h"
#include "core/os/os.h" #include "core/os/os.h"
#ifdef DEV_ENABLED #ifdef DEV_ENABLED
#include "core/os/thread.h" #include "core/os/thread.h"
#endif #endif
#include <thirdparty/gamepadmotionhelpers/GamepadMotion.hpp>
#define STANDARD_GRAVITY 9.80665f
static const char *_joy_buttons[(size_t)JoyButton::SDL_MAX] = { static const char *_joy_buttons[(size_t)JoyButton::SDL_MAX] = {
"a", "a",
"b", "b",
@ -62,6 +68,11 @@ static const char *_joy_buttons[(size_t)JoyButton::SDL_MAX] = {
"paddle3", "paddle3",
"paddle4", "paddle4",
"touchpad", "touchpad",
"misc2",
"misc3",
"misc4",
"misc5",
"misc6",
}; };
static const char *_joy_axes[(size_t)JoyAxis::SDL_MAX] = { static const char *_joy_axes[(size_t)JoyAxis::SDL_MAX] = {
@ -114,6 +125,8 @@ bool Input::is_mouse_mode_override_enabled() {
} }
void Input::_bind_methods() { void Input::_bind_methods() {
using namespace InputClassEnums;
ClassDB::bind_method(D_METHOD("is_anything_pressed"), &Input::is_anything_pressed); ClassDB::bind_method(D_METHOD("is_anything_pressed"), &Input::is_anything_pressed);
ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed); ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed);
ClassDB::bind_method(D_METHOD("is_physical_key_pressed", "keycode"), &Input::is_physical_key_pressed); ClassDB::bind_method(D_METHOD("is_physical_key_pressed", "keycode"), &Input::is_physical_key_pressed);
@ -140,13 +153,36 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_connected_joypads"), &Input::get_connected_joypads); ClassDB::bind_method(D_METHOD("get_connected_joypads"), &Input::get_connected_joypads);
ClassDB::bind_method(D_METHOD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength); ClassDB::bind_method(D_METHOD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength);
ClassDB::bind_method(D_METHOD("get_joy_vibration_duration", "device"), &Input::get_joy_vibration_duration); ClassDB::bind_method(D_METHOD("get_joy_vibration_duration", "device"), &Input::get_joy_vibration_duration);
ClassDB::bind_method(D_METHOD("get_joy_vibration_remaining_duration", "device"), &Input::get_joy_vibration_remaining_duration);
ClassDB::bind_method(D_METHOD("is_joy_vibrating", "device"), &Input::is_joy_vibrating);
ClassDB::bind_method(D_METHOD("has_joy_vibration", "device"), &Input::has_joy_vibration);
ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0)); ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0));
ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration); ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration);
ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms", "amplitude"), &Input::vibrate_handheld, DEFVAL(500), DEFVAL(-1.0)); ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms", "amplitude"), &Input::vibrate_handheld, DEFVAL(500), DEFVAL(-1.0));
ClassDB::bind_method(D_METHOD("set_ignore_joypad_on_unfocused_application", "enable"), &Input::set_ignore_joypad_on_unfocused_application);
ClassDB::bind_method(D_METHOD("is_ignoring_joypad_on_unfocused_application"), &Input::is_ignoring_joypad_on_unfocused_application);
ClassDB::bind_method(D_METHOD("get_gravity"), &Input::get_gravity); ClassDB::bind_method(D_METHOD("get_gravity"), &Input::get_gravity);
ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer); ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer);
ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer); ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer);
ClassDB::bind_method(D_METHOD("get_gyroscope"), &Input::get_gyroscope); ClassDB::bind_method(D_METHOD("get_gyroscope"), &Input::get_gyroscope);
ClassDB::bind_method(D_METHOD("get_joy_accelerometer", "device"), &Input::get_joy_accelerometer);
ClassDB::bind_method(D_METHOD("get_joy_gravity", "device"), &Input::get_joy_gravity);
ClassDB::bind_method(D_METHOD("get_joy_gyroscope", "device"), &Input::get_joy_gyroscope);
ClassDB::bind_method(D_METHOD("get_joy_motion_sensors_rate", "device"), &Input::get_joy_motion_sensors_rate);
ClassDB::bind_method(D_METHOD("is_joy_motion_sensors_enabled", "device"), &Input::is_joy_motion_sensors_enabled);
ClassDB::bind_method(D_METHOD("set_joy_motion_sensors_enabled", "device", "enable"), &Input::set_joy_motion_sensors_enabled);
ClassDB::bind_method(D_METHOD("has_joy_motion_sensors", "device"), &Input::has_joy_motion_sensors);
ClassDB::bind_method(D_METHOD("start_joy_motion_sensors_calibration", "device"), &Input::start_joy_motion_sensors_calibration);
ClassDB::bind_method(D_METHOD("stop_joy_motion_sensors_calibration", "device"), &Input::stop_joy_motion_sensors_calibration);
ClassDB::bind_method(D_METHOD("clear_joy_motion_sensors_calibration", "device"), &Input::clear_joy_motion_sensors_calibration);
ClassDB::bind_method(D_METHOD("get_joy_motion_sensors_calibration", "device"), &Input::get_joy_motion_sensors_calibration);
ClassDB::bind_method(D_METHOD("set_joy_motion_sensors_calibration", "device", "calibration_info"), &Input::set_joy_motion_sensors_calibration);
ClassDB::bind_method(D_METHOD("is_joy_motion_sensors_calibrated", "device"), &Input::is_joy_motion_sensors_calibrated);
ClassDB::bind_method(D_METHOD("is_joy_motion_sensors_calibrating", "device"), &Input::is_joy_motion_sensors_calibrating);
ClassDB::bind_method(D_METHOD("get_joy_touchpad_finger_position", "device", "finger", "touchpad"), &Input::get_joy_touchpad_finger_position, DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_joy_touchpad_finger_pressure", "device", "finger", "touchpad"), &Input::get_joy_touchpad_finger_pressure, DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_joy_touchpad_fingers", "device", "touchpad"), &Input::get_joy_touchpad_fingers, DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_joy_num_touchpads", "device"), &Input::get_joy_num_touchpads);
ClassDB::bind_method(D_METHOD("set_gravity", "value"), &Input::set_gravity); ClassDB::bind_method(D_METHOD("set_gravity", "value"), &Input::set_gravity);
ClassDB::bind_method(D_METHOD("set_accelerometer", "value"), &Input::set_accelerometer); ClassDB::bind_method(D_METHOD("set_accelerometer", "value"), &Input::set_accelerometer);
ClassDB::bind_method(D_METHOD("set_magnetometer", "value"), &Input::set_magnetometer); ClassDB::bind_method(D_METHOD("set_magnetometer", "value"), &Input::set_magnetometer);
@ -177,6 +213,7 @@ void Input::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_accumulated_input"), "set_use_accumulated_input", "is_using_accumulated_input"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_accumulated_input"), "set_use_accumulated_input", "is_using_accumulated_input");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emulate_mouse_from_touch"), "set_emulate_mouse_from_touch", "is_emulating_mouse_from_touch"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emulate_mouse_from_touch"), "set_emulate_mouse_from_touch", "is_emulating_mouse_from_touch");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emulate_touch_from_mouse"), "set_emulate_touch_from_mouse", "is_emulating_touch_from_mouse"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emulate_touch_from_mouse"), "set_emulate_touch_from_mouse", "is_emulating_touch_from_mouse");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_joypad_on_unfocused_application"), "set_ignore_joypad_on_unfocused_application", "is_ignoring_joypad_on_unfocused_application");
BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE); BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE);
BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN); BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN);
@ -296,24 +333,14 @@ bool Input::is_anything_pressed() const {
return false; return false;
} }
bool Input::is_anything_pressed_except_mouse() const { bool Input::is_any_key_pressed() const {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
if (disable_input) { if (disable_input) {
return false; return false;
} }
if (!keys_pressed.is_empty() || !joy_buttons_pressed.is_empty()) { return !keys_pressed.is_empty();
return true;
}
for (const KeyValue<StringName, Input::ActionState> &E : action_states) {
if (E.value.cache.pressed) {
return true;
}
}
return false;
} }
bool Input::is_key_pressed(Key p_keycode) const { bool Input::is_key_pressed(Key p_keycode) const {
@ -356,6 +383,10 @@ bool Input::is_mouse_button_pressed(MouseButton p_button) const {
return mouse_button_mask.has_flag(mouse_button_to_mask(p_button)); return mouse_button_mask.has_flag(mouse_button_to_mask(p_button));
} }
bool Input::_should_ignore_joypad_events() const {
return ignore_joypad_on_unfocused_application && !application_focused && !embedder_focused;
}
static JoyAxis _combine_device(JoyAxis p_value, int p_device) { static JoyAxis _combine_device(JoyAxis p_value, int p_device) {
return JoyAxis((int)p_value | (p_device << 20)); return JoyAxis((int)p_value | (p_device << 20));
} }
@ -615,6 +646,33 @@ float Input::get_joy_vibration_duration(int p_device) {
} }
} }
float Input::get_joy_vibration_remaining_duration(int p_device) {
_THREAD_SAFE_METHOD_
const Joypad *joypad = joy_names.getptr(p_device);
if (joypad == nullptr || !joypad->has_vibration) {
return 0.f;
}
const VibrationInfo *vibration = joy_vibration.getptr(p_device);
if (vibration == nullptr || (vibration->weak_magnitude == 0.f && vibration->strong_magnitude == 0.f) || vibration->duration < 0.f) {
return 0.f;
}
float vibration_duration = vibration->duration;
if (vibration_duration > 0xFFFF / 1000.f || vibration_duration == 0.f) {
vibration_duration = 0xFFFF / 1000.f; // SDL_MAX_RUMBLE_DURATION_MS / 1000.f
}
return MAX(vibration_duration - (OS::get_singleton()->get_ticks_usec() - vibration->timestamp) / 1e6, 0.f);
}
bool Input::is_joy_vibrating(int p_device) {
return get_joy_vibration_remaining_duration(p_device) > 0.0f;
}
bool Input::has_joy_vibration(int p_device) const {
_THREAD_SAFE_METHOD_
const Joypad *joypad = joy_names.getptr(p_device);
return joypad != nullptr && joypad->has_vibration;
}
static String _hex_str(uint8_t p_byte) { static String _hex_str(uint8_t p_byte) {
static const char *dict = "0123456789abcdef"; static const char *dict = "0123456789abcdef";
char ret[3]; char ret[3];
@ -684,6 +742,11 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, const String &p_
for (int i = 0; i < (int)JoyAxis::MAX; i++) { for (int i = 0; i < (int)JoyAxis::MAX; i++) {
set_joy_axis(p_idx, (JoyAxis)i, 0.0f); set_joy_axis(p_idx, (JoyAxis)i, 0.0f);
} }
MotionInfo *motion = joy_motion.getptr(p_idx);
if (motion != nullptr && motion->gamepad_motion != nullptr) {
delete motion->gamepad_motion;
}
joy_motion.erase(p_idx);
} }
joy_names[p_idx] = js; joy_names[p_idx] = js;
@ -739,6 +802,64 @@ Vector3 Input::get_gyroscope() const {
return gyroscope; return gyroscope;
} }
Vector2 Input::get_joy_touchpad_finger_position(int p_device, int p_finger, int p_touchpad) const {
_THREAD_SAFE_METHOD_
const TouchpadInfo *touch = joy_touch.getptr(p_device);
if (touch == nullptr) {
return Vector2(-1, -1);
}
uint16_t index = p_finger | (p_touchpad << 8);
const TouchpadFingerInfo *finger_info = touch->finger_info.getptr(index);
if (finger_info == nullptr) {
return Vector2(-1, -1);
}
return finger_info->position;
}
float Input::get_joy_touchpad_finger_pressure(int p_device, int p_finger, int p_touchpad) const {
_THREAD_SAFE_METHOD_
const TouchpadInfo *touch = joy_touch.getptr(p_device);
if (touch == nullptr) {
return -1.0f;
}
uint16_t index = p_finger | (p_touchpad << 8);
const TouchpadFingerInfo *finger_info = touch->finger_info.getptr(index);
if (finger_info == nullptr) {
return -1.0f;
}
return finger_info->pressure;
}
PackedInt32Array Input::get_joy_touchpad_fingers(int p_device, int p_touchpad) const {
_THREAD_SAFE_METHOD_
const TouchpadInfo *touch = joy_touch.getptr(p_device);
if (touch == nullptr) {
return PackedInt32Array();
}
PackedInt32Array result;
for (const KeyValue<uint16_t, TouchpadFingerInfo> &index : touch->finger_info) {
int touchpad = index.key >> 8;
if (touchpad == p_touchpad) {
result.append(index.key & 0xFF);
}
}
return result;
}
int Input::get_joy_num_touchpads(int p_device) const {
_THREAD_SAFE_METHOD_
const TouchpadInfo *touch = joy_touch.getptr(p_device);
if (touch == nullptr) {
return 0;
}
return touch->num_touchpads;
}
void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated) { void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated) {
// This function does the final delivery of the input event to user land. // This function does the final delivery of the input event to user land.
// Regardless where the event came from originally, this has to happen on the main thread. // Regardless where the event came from originally, this has to happen on the main thread.
@ -959,6 +1080,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
device_state.pressed[event_index] = is_pressed; device_state.pressed[event_index] = is_pressed;
device_state.strength[event_index] = p_event->get_action_strength(E.key); device_state.strength[event_index] = p_event->get_action_strength(E.key);
device_state.raw_strength[event_index] = p_event->get_action_raw_strength(E.key); device_state.raw_strength[event_index] = p_event->get_action_raw_strength(E.key);
device_state.event_type[event_index] = p_event->get_type();
// Update the action's global state and cache. // Update the action's global state and cache.
if (!is_pressed) { if (!is_pressed) {
@ -1005,6 +1127,12 @@ void Input::set_joy_features(int p_device, JoypadFeatures *p_features) {
} }
void Input::set_joy_light(int p_device, const Color &p_color) { void Input::set_joy_light(int p_device, const Color &p_color) {
_THREAD_SAFE_METHOD_
if (_should_ignore_joypad_events()) {
return;
}
Joypad *joypad = joy_names.getptr(p_device); Joypad *joypad = joy_names.getptr(p_device);
if (!joypad || !joypad->has_light || joypad->features == nullptr) { if (!joypad || !joypad->has_light || joypad->features == nullptr) {
return; return;
@ -1018,8 +1146,218 @@ bool Input::has_joy_light(int p_device) const {
return joypad && joypad->has_light; return joypad && joypad->has_light;
} }
Vector3 Input::get_joy_accelerometer(int p_device) const {
_THREAD_SAFE_METHOD_
if (_should_ignore_joypad_events()) {
return Vector3();
}
const MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return Vector3();
}
float joy_acceleration_data[3];
motion->gamepad_motion->GetProcessedAcceleration(joy_acceleration_data[0], joy_acceleration_data[1], joy_acceleration_data[2]);
Vector3 joy_acceleration(joy_acceleration_data[0], joy_acceleration_data[1], joy_acceleration_data[2]);
float joy_gravity_data[3];
motion->gamepad_motion->GetGravity(joy_gravity_data[0], joy_gravity_data[1], joy_gravity_data[2]);
Vector3 joy_gravity(joy_gravity_data[0], joy_gravity_data[1], joy_gravity_data[2]);
return (-joy_acceleration + joy_gravity) * STANDARD_GRAVITY;
}
Vector3 Input::get_joy_gravity(int p_device) const {
_THREAD_SAFE_METHOD_
if (_should_ignore_joypad_events()) {
return Vector3();
}
const MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return Vector3();
}
float joy_gravity_data[3];
motion->gamepad_motion->GetGravity(joy_gravity_data[0], joy_gravity_data[1], joy_gravity_data[2]);
Vector3 joy_gravity(joy_gravity_data[0], joy_gravity_data[1], joy_gravity_data[2]);
return joy_gravity.normalized() * STANDARD_GRAVITY;
}
Vector3 Input::get_joy_gyroscope(int p_device) const {
_THREAD_SAFE_METHOD_
if (_should_ignore_joypad_events()) {
return Vector3();
}
const MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return Vector3();
}
float joy_gyro_data[3];
motion->gamepad_motion->GetCalibratedGyro(joy_gyro_data[0], joy_gyro_data[1], joy_gyro_data[2]);
Vector3 joy_gyro(joy_gyro_data[0], joy_gyro_data[1], joy_gyro_data[2]);
return joy_gyro * M_PI / 180.0;
}
void Input::set_joy_motion_sensors_enabled(int p_device, bool p_enable) {
_THREAD_SAFE_METHOD_
Joypad *joypad = joy_names.getptr(p_device);
if (joypad == nullptr || joypad->features == nullptr) {
return;
}
MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return;
}
joypad->features->set_joy_motion_sensors_enabled(p_enable);
motion->sensors_enabled = p_enable;
}
bool Input::is_joy_motion_sensors_enabled(int p_device) const {
_THREAD_SAFE_METHOD_
const MotionInfo *motion = joy_motion.getptr(p_device);
return motion != nullptr && motion->sensors_enabled;
}
bool Input::has_joy_motion_sensors(int p_device) const {
_THREAD_SAFE_METHOD_
return joy_motion.has(p_device);
}
float Input::get_joy_motion_sensors_rate(int p_device) const {
_THREAD_SAFE_METHOD_
const MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return 0.0f;
}
return motion->sensor_data_rate;
}
void Input::start_joy_motion_sensors_calibration(int p_device) {
_THREAD_SAFE_METHOD_
MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return;
}
ERR_FAIL_COND_MSG(!motion->sensors_enabled, "Motion sensors are not enabled on the joypad.");
ERR_FAIL_COND_MSG(motion->calibrating, "Calibration already in progress.");
motion->gamepad_motion->ResetContinuousCalibration();
motion->gamepad_motion->StartContinuousCalibration();
motion->calibrating = true;
motion->calibrated = false;
}
void Input::stop_joy_motion_sensors_calibration(int p_device) {
_THREAD_SAFE_METHOD_
MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return;
}
ERR_FAIL_COND_MSG(!motion->sensors_enabled, "Motion sensors are not enabled on the joypad.");
ERR_FAIL_COND_MSG(!motion->calibrating, "Calibration hasn't been started.");
motion->gamepad_motion->PauseContinuousCalibration();
motion->calibrating = false;
motion->calibrated = true;
}
void Input::clear_joy_motion_sensors_calibration(int p_device) {
_THREAD_SAFE_METHOD_
MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return;
}
// Calibration might be in progress and the developer or the user might want to reset it,
// so no need to stop the calibration.
motion->gamepad_motion->ResetContinuousCalibration();
}
Dictionary Input::get_joy_motion_sensors_calibration(int p_device) const {
_THREAD_SAFE_METHOD_
const MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return Dictionary();
}
if (!motion->calibrated) {
return Dictionary();
}
float joy_gyro_offset_data[3];
motion->gamepad_motion->GetCalibrationOffset(joy_gyro_offset_data[0], joy_gyro_offset_data[1], joy_gyro_offset_data[2]);
Vector3 joy_gyro_offset(joy_gyro_offset_data[0], joy_gyro_offset_data[1], joy_gyro_offset_data[2]);
Dictionary result;
result["gyroscope_offset"] = joy_gyro_offset * M_PI / 180.0;
return result;
}
void Input::set_joy_motion_sensors_calibration(int p_device, const Dictionary &p_calibration_info) {
_THREAD_SAFE_METHOD_
MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return;
}
ERR_FAIL_COND_MSG(motion->calibrating, "Calibration is currently in progress.");
Vector3 gyro_offset = p_calibration_info.get("gyroscope_offset", Vector3()).operator Vector3() * 180.0 / M_PI;
motion->gamepad_motion->SetCalibrationOffset(gyro_offset.x, gyro_offset.y, gyro_offset.z, 1);
motion->calibrating = false;
motion->calibrated = true;
}
bool Input::is_joy_motion_sensors_calibrating(int p_device) const {
_THREAD_SAFE_METHOD_
const MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return false;
}
return motion->calibrating;
}
bool Input::is_joy_motion_sensors_calibrated(int p_device) const {
_THREAD_SAFE_METHOD_
const MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return false;
}
return motion->calibrated;
}
void Input::set_joy_motion_sensors_rate(int p_device, float p_rate) {
_THREAD_SAFE_METHOD_
MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return;
}
motion->sensor_data_rate = p_rate;
}
void Input::start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration) { void Input::start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration) {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
if (_should_ignore_joypad_events()) {
return;
}
if (p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) { if (p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) {
return; return;
} }
@ -1045,6 +1383,17 @@ void Input::vibrate_handheld(int p_duration_ms, float p_amplitude) {
OS::get_singleton()->vibrate_handheld(p_duration_ms, p_amplitude); OS::get_singleton()->vibrate_handheld(p_duration_ms, p_amplitude);
} }
void Input::set_ignore_joypad_on_unfocused_application(bool p_ignore) {
ignore_joypad_on_unfocused_application = p_ignore;
if (_should_ignore_joypad_events()) {
release_pressed_events();
}
}
bool Input::is_ignoring_joypad_on_unfocused_application() const {
return ignore_joypad_on_unfocused_application;
}
void Input::set_gravity(const Vector3 &p_gravity) { void Input::set_gravity(const Vector3 &p_gravity) {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
@ -1311,17 +1660,52 @@ bool Input::is_using_accumulated_input() {
} }
void Input::release_pressed_events() { void Input::release_pressed_events() {
// Don't release the events if the application (or the window it's embedded in) is still focused.
if (application_focused || embedder_focused) {
return;
}
flush_buffered_events(); // this is needed to release actions strengths flush_buffered_events(); // this is needed to release actions strengths
keys_pressed.clear(); keys_pressed.clear();
physical_keys_pressed.clear(); physical_keys_pressed.clear();
key_label_pressed.clear(); key_label_pressed.clear();
joy_buttons_pressed.clear(); if (ignore_joypad_on_unfocused_application) {
_joy_axis.clear(); joy_buttons_pressed.clear();
_joy_axis.clear();
for (KeyValue<StringName, Input::ActionState> &E : action_states) { for (KeyValue<StringName, Input::ActionState> &E : action_states) {
if (E.value.cache.pressed) { if (E.value.cache.pressed) {
action_release(E.key); action_release(E.key);
}
}
for (int device : get_connected_joypads()) {
stop_joy_vibration(device);
TouchpadInfo &touch = joy_touch[device];
touch.finger_info.clear();
}
} else {
for (KeyValue<StringName, Input::ActionState> &E : action_states) {
if (E.value.cache.pressed) {
bool is_only_joypad_pressed = true;
int max_event = InputMap::get_singleton()->action_get_events(E.key)->size() + 1; // +1 comes from InputEventAction.
for (const KeyValue<int, ActionState::DeviceState> &kv : E.value.device_states) {
const ActionState::DeviceState &device_state = kv.value;
for (int i = 0; i < max_event; i++) {
if (device_state.event_type[i] != InputEventType::JOY_MOTION && device_state.event_type[i] != InputEventType::JOY_BUTTON && device_state.strength[i] > 0.0f) {
is_only_joypad_pressed = false;
action_release(E.key);
break;
}
}
if (!is_only_joypad_pressed) {
break;
}
}
}
} }
} }
} }
@ -1332,6 +1716,11 @@ void Input::set_event_dispatch_function(EventDispatchFunc p_function) {
void Input::joy_button(int p_device, JoyButton p_button, bool p_pressed) { void Input::joy_button(int p_device, JoyButton p_button, bool p_pressed) {
_THREAD_SAFE_METHOD_; _THREAD_SAFE_METHOD_;
if (_should_ignore_joypad_events()) {
return;
}
Joypad &joy = joy_names[p_device]; Joypad &joy = joy_names[p_device];
ERR_FAIL_INDEX((int)p_button, (int)JoyButton::MAX); ERR_FAIL_INDEX((int)p_button, (int)JoyButton::MAX);
@ -1360,6 +1749,10 @@ void Input::joy_button(int p_device, JoyButton p_button, bool p_pressed) {
void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) { void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) {
_THREAD_SAFE_METHOD_; _THREAD_SAFE_METHOD_;
if (_should_ignore_joypad_events()) {
return;
}
ERR_FAIL_INDEX((int)p_axis, (int)JoyAxis::MAX); ERR_FAIL_INDEX((int)p_axis, (int)JoyAxis::MAX);
Joypad &joy = joy_names[p_device]; Joypad &joy = joy_names[p_device];
@ -1429,6 +1822,11 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) {
void Input::joy_hat(int p_device, BitField<HatMask> p_val) { void Input::joy_hat(int p_device, BitField<HatMask> p_val) {
_THREAD_SAFE_METHOD_; _THREAD_SAFE_METHOD_;
if (_should_ignore_joypad_events()) {
return;
}
const Joypad &joy = joy_names[p_device]; const Joypad &joy = joy_names[p_device];
JoyEvent map[(size_t)HatDir::MAX]; JoyEvent map[(size_t)HatDir::MAX];
@ -1469,6 +1867,43 @@ void Input::joy_hat(int p_device, BitField<HatMask> p_val) {
joy_names[p_device].hat_current = (int)p_val; joy_names[p_device].hat_current = (int)p_val;
} }
void Input::joy_motion_sensors(int p_device, const Vector3 &p_accelerometer, const Vector3 &p_gyroscope) {
_THREAD_SAFE_METHOD_
if (_should_ignore_joypad_events()) {
return;
}
// TODO: events
MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return;
}
Vector3 gyro_degrees = p_gyroscope * 180.0 / M_PI;
Vector3 accel_g = -p_accelerometer / STANDARD_GRAVITY;
uint64_t new_timestamp = OS::get_singleton()->get_ticks_msec();
float delta_time = (new_timestamp - motion->last_timestamp) / 1000.0f;
motion->last_timestamp = new_timestamp;
motion->gamepad_motion->ProcessMotion(gyro_degrees.x, gyro_degrees.y, gyro_degrees.z, accel_g.x, accel_g.y, accel_g.z, delta_time);
}
void Input::joy_touchpad(int p_device, int p_touchpad, int p_finger, const Vector2 &p_position, float p_pressure, bool p_pressed) {
_THREAD_SAFE_METHOD_
if (_should_ignore_joypad_events()) {
return;
}
TouchpadInfo &touch = joy_touch[p_device];
uint16_t index = p_finger | (p_touchpad << 8);
if (p_pressed) {
touch.finger_info[index] = TouchpadFingerInfo{ p_position, p_pressure };
} else {
touch.finger_info.erase(index);
}
}
void Input::_button_event(int p_device, JoyButton p_index, bool p_pressed) { void Input::_button_event(int p_device, JoyButton p_index, bool p_pressed) {
Ref<InputEventJoypadButton> ievent; Ref<InputEventJoypadButton> ievent;
ievent.instantiate(); ievent.instantiate();
@ -1517,9 +1952,25 @@ void Input::_update_joypad_features(int p_device) {
if (!joypad || joypad->features == nullptr) { if (!joypad || joypad->features == nullptr) {
return; return;
} }
if (joypad->features->has_joy_vibration()) {
joypad->has_vibration = true;
}
if (joypad->features->has_joy_light()) { if (joypad->features->has_joy_light()) {
joypad->has_light = true; joypad->has_light = true;
} }
if (joypad->features->has_joy_motion_sensors()) {
MotionInfo &motion = joy_motion[p_device];
if (!motion.gamepad_motion) {
motion.gamepad_motion = new GamepadMotion();
} else {
motion.gamepad_motion->Reset();
}
motion.last_timestamp = OS::get_singleton()->get_ticks_msec();
}
if (joypad->features->get_joy_num_touchpads() > 0) {
joy_touch[p_device].num_touchpads = joypad->features->get_joy_num_touchpads();
}
} }
Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button) { Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button) {
@ -2004,6 +2455,7 @@ Input::Input() {
gravity_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gravity", false); gravity_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gravity", false);
gyroscope_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gyroscope", false); gyroscope_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gyroscope", false);
magnetometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_magnetometer", false); magnetometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_magnetometer", false);
ignore_joypad_on_unfocused_application = GLOBAL_DEF_RST_BASIC("input_devices/joypads/ignore_joypad_on_unfocused_application", false);
} }
Input::~Input() { Input::~Input() {

View file

@ -38,6 +38,43 @@
#include "core/templates/rb_set.h" #include "core/templates/rb_set.h"
#include "core/variant/typed_array.h" #include "core/variant/typed_array.h"
class GamepadMotion;
namespace InputClassEnums {
// Keep synced with DisplayServerEnums::MouseMode enum.
enum MouseMode : int {
MOUSE_MODE_VISIBLE,
MOUSE_MODE_HIDDEN,
MOUSE_MODE_CAPTURED,
MOUSE_MODE_CONFINED,
MOUSE_MODE_CONFINED_HIDDEN,
MOUSE_MODE_MAX,
};
// Keep synced with DisplayServerEnums and Control enums.
#undef CursorShape
enum CursorShape : int {
CURSOR_ARROW,
CURSOR_IBEAM,
CURSOR_POINTING_HAND,
CURSOR_CROSS,
CURSOR_WAIT,
CURSOR_BUSY,
CURSOR_DRAG,
CURSOR_CAN_DROP,
CURSOR_FORBIDDEN,
CURSOR_VSIZE,
CURSOR_HSIZE,
CURSOR_BDIAGSIZE,
CURSOR_FDIAGSIZE,
CURSOR_MOVE,
CURSOR_VSPLIT,
CURSOR_HSPLIT,
CURSOR_HELP,
CURSOR_MAX
};
} //namespace InputClassEnums
class Input : public Object { class Input : public Object {
GDCLASS(Input, Object); GDCLASS(Input, Object);
_THREAD_SAFE_CLASS_ _THREAD_SAFE_CLASS_
@ -47,44 +84,23 @@ class Input : public Object {
static constexpr uint64_t MAX_EVENT = 32; static constexpr uint64_t MAX_EVENT = 32;
public: public:
// Keep synced with "DisplayServer::MouseMode" enum. // TODO: When we migrate to C++20, replace these with "using enum" and skip prefixing MouseMode and CursorShape in other files.
enum MouseMode { using MouseMode = InputClassEnums::MouseMode;
MOUSE_MODE_VISIBLE, using CursorShape = InputClassEnums::CursorShape;
MOUSE_MODE_HIDDEN,
MOUSE_MODE_CAPTURED,
MOUSE_MODE_CONFINED,
MOUSE_MODE_CONFINED_HIDDEN,
MOUSE_MODE_MAX,
};
#undef CursorShape
enum CursorShape {
CURSOR_ARROW,
CURSOR_IBEAM,
CURSOR_POINTING_HAND,
CURSOR_CROSS,
CURSOR_WAIT,
CURSOR_BUSY,
CURSOR_DRAG,
CURSOR_CAN_DROP,
CURSOR_FORBIDDEN,
CURSOR_VSIZE,
CURSOR_HSIZE,
CURSOR_BDIAGSIZE,
CURSOR_FDIAGSIZE,
CURSOR_MOVE,
CURSOR_VSPLIT,
CURSOR_HSPLIT,
CURSOR_HELP,
CURSOR_MAX
};
class JoypadFeatures { class JoypadFeatures {
public: public:
virtual ~JoypadFeatures() {} virtual ~JoypadFeatures() {}
virtual bool has_joy_vibration() const { return false; }
virtual bool has_joy_light() const { return false; } virtual bool has_joy_light() const { return false; }
virtual void set_joy_light(const Color &p_color) {} virtual void set_joy_light(const Color &p_color) {}
virtual bool has_joy_motion_sensors() const { return false; }
virtual void set_joy_motion_sensors_enabled(bool p_enable) {}
virtual int get_joy_num_touchpads() const { return 0; }
}; };
static constexpr int32_t JOYPADS_MAX = 16; static constexpr int32_t JOYPADS_MAX = 16;
@ -112,6 +128,9 @@ private:
int64_t mouse_window = 0; int64_t mouse_window = 0;
bool legacy_just_pressed_behavior = false; bool legacy_just_pressed_behavior = false;
bool disable_input = false; bool disable_input = false;
bool ignore_joypad_on_unfocused_application = false;
bool application_focused = true;
bool embedder_focused = false;
struct ActionState { struct ActionState {
uint64_t pressed_physics_frame = UINT64_MAX; uint64_t pressed_physics_frame = UINT64_MAX;
@ -126,6 +145,7 @@ private:
bool pressed[MAX_EVENT] = { false }; bool pressed[MAX_EVENT] = { false };
float strength[MAX_EVENT] = { 0.0 }; float strength[MAX_EVENT] = { 0.0 };
float raw_strength[MAX_EVENT] = { 0.0 }; float raw_strength[MAX_EVENT] = { 0.0 };
InputEventType event_type[MAX_EVENT] = { InputEventType::INVALID };
}; };
bool api_pressed = false; bool api_pressed = false;
float api_strength = 0.0; float api_strength = 0.0;
@ -157,6 +177,35 @@ private:
HashMap<int, VibrationInfo> joy_vibration; HashMap<int, VibrationInfo> joy_vibration;
struct MotionInfo {
bool sensors_enabled : 1;
bool calibrating : 1;
bool calibrated : 1;
float sensor_data_rate = 0.0f;
uint64_t last_timestamp = 0;
GamepadMotion *gamepad_motion = nullptr;
MotionInfo() {
sensors_enabled = false;
calibrating = false;
calibrated = false;
}
};
HashMap<int, MotionInfo> joy_motion;
struct TouchpadFingerInfo {
Vector2 position;
float pressure;
};
struct TouchpadInfo {
int num_touchpads = 0;
HashMap<uint16_t, TouchpadFingerInfo> finger_info;
};
HashMap<int, TouchpadInfo> joy_touch;
struct VelocityTrack { struct VelocityTrack {
uint64_t last_tick = 0; uint64_t last_tick = 0;
Vector2 velocity; Vector2 velocity;
@ -184,6 +233,7 @@ private:
int hat_current = 0; int hat_current = 0;
Dictionary info; Dictionary info;
bool has_light = false; bool has_light = false;
bool has_vibration = false;
Input::JoypadFeatures *features = nullptr; Input::JoypadFeatures *features = nullptr;
}; };
@ -195,7 +245,7 @@ private:
int fallback_mapping = -1; // Index of the guid in map_db. int fallback_mapping = -1; // Index of the guid in map_db.
CursorShape default_shape = CURSOR_ARROW; CursorShape default_shape = CursorShape::CURSOR_ARROW;
enum JoyType { enum JoyType {
TYPE_BUTTON, TYPE_BUTTON,
@ -275,6 +325,8 @@ private:
#endif #endif
friend class DisplayServer; friend class DisplayServer;
friend class SceneTree;
friend class SceneDebugger;
static void (*set_mouse_mode_func)(MouseMode); static void (*set_mouse_mode_func)(MouseMode);
static MouseMode (*get_mouse_mode_func)(); static MouseMode (*get_mouse_mode_func)();
@ -289,6 +341,8 @@ private:
EventDispatchFunc event_dispatch_function = nullptr; EventDispatchFunc event_dispatch_function = nullptr;
bool _should_ignore_joypad_events() const;
#ifndef DISABLE_DEPRECATED #ifndef DISABLE_DEPRECATED
void _vibrate_handheld_bind_compat_91143(int p_duration_ms = 500); void _vibrate_handheld_bind_compat_91143(int p_duration_ms = 500);
static void _bind_compatibility_methods(); static void _bind_compatibility_methods();
@ -312,7 +366,7 @@ public:
static Input *get_singleton(); static Input *get_singleton();
bool is_anything_pressed() const; bool is_anything_pressed() const;
bool is_anything_pressed_except_mouse() const; bool is_any_key_pressed() const;
bool is_key_pressed(Key p_keycode) const; bool is_key_pressed(Key p_keycode) const;
bool is_physical_key_pressed(Key p_keycode) const; bool is_physical_key_pressed(Key p_keycode) const;
bool is_key_label_pressed(Key p_keycode) const; bool is_key_label_pressed(Key p_keycode) const;
@ -334,7 +388,10 @@ public:
TypedArray<int> get_connected_joypads(); TypedArray<int> get_connected_joypads();
Vector2 get_joy_vibration_strength(int p_device); Vector2 get_joy_vibration_strength(int p_device);
float get_joy_vibration_duration(int p_device); float get_joy_vibration_duration(int p_device);
float get_joy_vibration_remaining_duration(int p_device);
uint64_t get_joy_vibration_timestamp(int p_device); uint64_t get_joy_vibration_timestamp(int p_device);
bool is_joy_vibrating(int p_device);
bool has_joy_vibration(int p_device) const;
void joy_connection_changed(int p_idx, bool p_connected, const String &p_name, const String &p_guid = "", const Dictionary &p_joypad_info = Dictionary()); void joy_connection_changed(int p_idx, bool p_connected, const String &p_name, const String &p_guid = "", const Dictionary &p_joypad_info = Dictionary());
Vector3 get_gravity() const; Vector3 get_gravity() const;
@ -342,6 +399,8 @@ public:
Vector3 get_magnetometer() const; Vector3 get_magnetometer() const;
Vector3 get_gyroscope() const; Vector3 get_gyroscope() const;
int get_joy_num_touchpads(int p_device) const;
Point2 get_mouse_position() const; Point2 get_mouse_position() const;
Vector2 get_last_mouse_velocity(); Vector2 get_last_mouse_velocity();
Vector2 get_last_mouse_screen_velocity(); Vector2 get_last_mouse_screen_velocity();
@ -363,10 +422,39 @@ public:
void set_joy_light(int p_device, const Color &p_color); void set_joy_light(int p_device, const Color &p_color);
bool has_joy_light(int p_device) const; bool has_joy_light(int p_device) const;
Vector3 get_joy_accelerometer(int p_device) const;
Vector3 get_joy_gravity(int p_device) const;
Vector3 get_joy_gyroscope(int p_device) const;
void set_joy_motion_sensors_enabled(int p_device, bool p_enable);
bool is_joy_motion_sensors_enabled(int p_device) const;
bool has_joy_motion_sensors(int p_device) const;
float get_joy_motion_sensors_rate(int p_device) const;
void start_joy_motion_sensors_calibration(int p_device);
void stop_joy_motion_sensors_calibration(int p_device);
void clear_joy_motion_sensors_calibration(int p_device);
Dictionary get_joy_motion_sensors_calibration(int p_device) const;
void set_joy_motion_sensors_calibration(int p_device, const Dictionary &p_calibration_info);
bool is_joy_motion_sensors_calibrating(int p_device) const;
bool is_joy_motion_sensors_calibrated(int p_device) const;
void set_joy_motion_sensors_rate(int p_device, float p_rate);
Vector2 get_joy_touchpad_finger_position(int p_device, int p_finger, int p_touchpad = 0) const;
float get_joy_touchpad_finger_pressure(int p_device, int p_finger, int p_touchpad = 0) const;
PackedInt32Array get_joy_touchpad_fingers(int p_device, int p_touchpad = 0) const;
void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0); void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0);
void stop_joy_vibration(int p_device); void stop_joy_vibration(int p_device);
void vibrate_handheld(int p_duration_ms = 500, float p_amplitude = -1.0); void vibrate_handheld(int p_duration_ms = 500, float p_amplitude = -1.0);
void set_ignore_joypad_on_unfocused_application(bool p_ignore);
bool is_ignoring_joypad_on_unfocused_application() const;
void set_mouse_position(const Point2 &p_posf); void set_mouse_position(const Point2 &p_posf);
void action_press(const StringName &p_action, float p_strength = 1.f); void action_press(const StringName &p_action, float p_strength = 1.f);
@ -382,12 +470,14 @@ public:
CursorShape get_default_cursor_shape() const; CursorShape get_default_cursor_shape() const;
void set_default_cursor_shape(CursorShape p_shape); void set_default_cursor_shape(CursorShape p_shape);
CursorShape get_current_cursor_shape() const; CursorShape get_current_cursor_shape() const;
void set_custom_mouse_cursor(const Ref<Resource> &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()); void set_custom_mouse_cursor(const Ref<Resource> &p_cursor, CursorShape p_shape = Input::CursorShape::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2());
void parse_mapping(const String &p_mapping); void parse_mapping(const String &p_mapping);
void joy_button(int p_device, JoyButton p_button, bool p_pressed); void joy_button(int p_device, JoyButton p_button, bool p_pressed);
void joy_axis(int p_device, JoyAxis p_axis, float p_value); void joy_axis(int p_device, JoyAxis p_axis, float p_value);
void joy_hat(int p_device, BitField<HatMask> p_val); void joy_hat(int p_device, BitField<HatMask> p_val);
void joy_motion_sensors(int p_device, const Vector3 &p_accelerometer, const Vector3 &p_gyroscope);
void joy_touchpad(int p_device, int p_touchpad, int p_finger, const Vector2 &p_position, float p_pressure, bool p_pressed);
void add_joy_mapping(const String &p_mapping, bool p_update_existing = false); void add_joy_mapping(const String &p_mapping, bool p_update_existing = false);
void remove_joy_mapping(const String &p_guid); void remove_joy_mapping(const String &p_guid);

View file

@ -100,7 +100,12 @@ enum class JoyButton {
PADDLE3 = 18, PADDLE3 = 18,
PADDLE4 = 19, PADDLE4 = 19,
TOUCHPAD = 20, TOUCHPAD = 20,
SDL_MAX = 21, MISC2 = 21,
MISC3 = 22,
MISC4 = 23,
MISC5 = 24,
MISC6 = 25,
SDL_MAX = 26,
MAX = 128, // Android supports up to 36 buttons. DirectInput supports up to 128 buttons. MAX = 128, // Android supports up to 36 buttons. DirectInput supports up to 128 buttons.
}; };

View file

@ -32,8 +32,11 @@
#include "core/input/input_map.h" #include "core/input/input_map.h"
#include "core/input/shortcut.h" #include "core/input/shortcut.h"
#include "core/math/transform_2d.h"
#include "core/object/class_db.h"
#include "core/os/keyboard.h" #include "core/os/keyboard.h"
#include "core/os/os.h" #include "core/os/os.h"
#include "core/string/ustring.h"
void InputEvent::set_device(int p_device) { void InputEvent::set_device(int p_device) {
device = p_device; device = p_device;
@ -131,6 +134,8 @@ void InputEvent::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "device"), "set_device", "get_device"); ADD_PROPERTY(PropertyInfo(Variant::INT, "device"), "set_device", "get_device");
BIND_CONSTANT(DEVICE_ID_EMULATION); BIND_CONSTANT(DEVICE_ID_EMULATION);
BIND_CONSTANT(DEVICE_ID_KEYBOARD);
BIND_CONSTANT(DEVICE_ID_MOUSE);
} }
/////////////////////////////////// ///////////////////////////////////
@ -314,8 +319,7 @@ void InputEventWithModifiers::_validate_property(PropertyInfo &p_property) const
if (p_property.name == "meta_pressed") { if (p_property.name == "meta_pressed") {
p_property.usage ^= PROPERTY_USAGE_STORAGE; p_property.usage ^= PROPERTY_USAGE_STORAGE;
p_property.usage ^= PROPERTY_USAGE_EDITOR; p_property.usage ^= PROPERTY_USAGE_EDITOR;
} } else if (p_property.name == "ctrl_pressed") {
if (p_property.name == "ctrl_pressed") {
p_property.usage ^= PROPERTY_USAGE_STORAGE; p_property.usage ^= PROPERTY_USAGE_STORAGE;
p_property.usage ^= PROPERTY_USAGE_EDITOR; p_property.usage ^= PROPERTY_USAGE_EDITOR;
} }
@ -662,6 +666,10 @@ void InputEventKey::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "echo"), "set_echo", "is_echo"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "echo"), "set_echo", "is_echo");
} }
InputEventKey::InputEventKey() {
set_device(DEVICE_ID_KEYBOARD);
}
/////////////////////////////////// ///////////////////////////////////
void InputEventMouse::set_button_mask(BitField<MouseButtonMask> p_mask) { void InputEventMouse::set_button_mask(BitField<MouseButtonMask> p_mask) {
@ -704,6 +712,10 @@ void InputEventMouse::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "suffix:px"), "set_global_position", "get_global_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "suffix:px"), "set_global_position", "get_global_position");
} }
InputEventMouse::InputEventMouse() {
set_device(DEVICE_ID_MOUSE);
}
/////////////////////////////////// ///////////////////////////////////
void InputEventMouseButton::set_factor(float p_factor) { void InputEventMouseButton::set_factor(float p_factor) {
@ -1920,7 +1932,7 @@ void InputEventShortcut::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shortcut", "shortcut"), &InputEventShortcut::set_shortcut); ClassDB::bind_method(D_METHOD("set_shortcut", "shortcut"), &InputEventShortcut::set_shortcut);
ClassDB::bind_method(D_METHOD("get_shortcut"), &InputEventShortcut::get_shortcut); ClassDB::bind_method(D_METHOD("get_shortcut"), &InputEventShortcut::get_shortcut);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "Shortcut"), "set_shortcut", "get_shortcut"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, Shortcut::get_class_static()), "set_shortcut", "get_shortcut");
} }
String InputEventShortcut::as_text() const { String InputEventShortcut::as_text() const {

View file

@ -32,9 +32,7 @@
#include "core/input/input_enums.h" #include "core/input/input_enums.h"
#include "core/io/resource.h" #include "core/io/resource.h"
#include "core/math/transform_2d.h"
#include "core/os/keyboard.h" #include "core/os/keyboard.h"
#include "core/string/ustring.h"
#include "core/typedefs.h" #include "core/typedefs.h"
/** /**
@ -43,6 +41,8 @@
*/ */
class Shortcut; class Shortcut;
class String;
struct Transform2D;
/** /**
* Input Modifier Status * Input Modifier Status
@ -63,6 +63,8 @@ protected:
public: public:
static constexpr int DEVICE_ID_EMULATION = -1; static constexpr int DEVICE_ID_EMULATION = -1;
static constexpr int DEVICE_ID_INTERNAL = -2; static constexpr int DEVICE_ID_INTERNAL = -2;
static constexpr int DEVICE_ID_KEYBOARD = 16; // IDs 0-15 are reserved for joypads.
static constexpr int DEVICE_ID_MOUSE = 32; // IDs 17-31 are reserved for multiple keyboard support in the future.
void set_device(int p_device); void set_device(int p_device);
int get_device() const; int get_device() const;
@ -199,6 +201,8 @@ public:
static Ref<InputEventKey> create_reference(Key p_keycode_with_modifier_masks, bool p_physical = false); static Ref<InputEventKey> create_reference(Key p_keycode_with_modifier_masks, bool p_physical = false);
InputEventType get_type() const final override { return InputEventType::KEY; } InputEventType get_type() const final override { return InputEventType::KEY; }
InputEventKey();
}; };
class InputEventMouse : public InputEventWithModifiers { class InputEventMouse : public InputEventWithModifiers {
@ -221,6 +225,8 @@ public:
void set_global_position(const Vector2 &p_global_pos); void set_global_position(const Vector2 &p_global_pos);
Vector2 get_global_position() const; Vector2 get_global_position() const;
InputEventMouse();
}; };
class InputEventMouseButton : public InputEventMouse { class InputEventMouseButton : public InputEventMouse {

View file

@ -32,7 +32,6 @@
#include "core/input/input.h" #include "core/input/input.h"
#include "core/io/marshalls.h" #include "core/io/marshalls.h"
#include "core/os/os.h"
enum class BoolShift : uint8_t { enum class BoolShift : uint8_t {
SHIFT = 0, SHIFT = 0,

View file

@ -30,6 +30,10 @@
#ifndef DISABLE_DEPRECATED #ifndef DISABLE_DEPRECATED
#include "input_map.h"
#include "core/object/class_db.h"
void InputMap::_add_action_bind_compat_97281(const StringName &p_action, float p_deadzone) { void InputMap::_add_action_bind_compat_97281(const StringName &p_action, float p_deadzone) {
add_action(p_action, p_deadzone); add_action(p_action, p_deadzone);
} }

View file

@ -33,6 +33,7 @@
#include "core/config/project_settings.h" #include "core/config/project_settings.h"
#include "core/input/input.h" #include "core/input/input.h"
#include "core/object/class_db.h"
#include "core/os/keyboard.h" #include "core/os/keyboard.h"
#include "core/os/os.h" #include "core/os/os.h"
#include "core/variant/typed_array.h" #include "core/variant/typed_array.h"
@ -205,6 +206,22 @@ void InputMap::action_add_event(const StringName &p_action, RequiredParam<InputE
return; // Already added. return; // Already added.
} }
// Normalize legacy device IDs: before the device ID change,
// keyboard and mouse events defaulted to device=0.
if (p_event->get_device() == 0) {
switch (p_event->get_type()) {
case InputEventType::KEY:
p_event->set_device(InputEvent::DEVICE_ID_KEYBOARD);
break;
case InputEventType::MOUSE_BUTTON:
case InputEventType::MOUSE_MOTION:
p_event->set_device(InputEvent::DEVICE_ID_MOUSE);
break;
default:
break;
}
}
input_map[p_action].inputs.push_back(p_event); input_map[p_action].inputs.push_back(p_event);
} }
@ -319,6 +336,11 @@ void InputMap::load_from_project_settings() {
String name = pi.name.substr(pi.name.find_char('/') + 1); String name = pi.name.substr(pi.name.find_char('/') + 1);
Dictionary action = GLOBAL_GET(pi.name); Dictionary action = GLOBAL_GET(pi.name);
if (!action.has("events")) {
continue;
}
float deadzone = action.has("deadzone") ? (float)action["deadzone"] : DEFAULT_DEADZONE; float deadzone = action.has("deadzone") ? (float)action["deadzone"] : DEFAULT_DEADZONE;
Array events = action["events"]; Array events = action["events"];
@ -411,6 +433,7 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = {
{ "ui_unicode_start", TTRC("Start Unicode Character Input") }, { "ui_unicode_start", TTRC("Start Unicode Character Input") },
{ "ui_colorpicker_delete_preset", TTRC("ColorPicker: Delete Preset") }, { "ui_colorpicker_delete_preset", TTRC("ColorPicker: Delete Preset") },
{ "ui_accessibility_drag_and_drop", TTRC("Accessibility: Keyboard Drag and Drop") }, { "ui_accessibility_drag_and_drop", TTRC("Accessibility: Keyboard Drag and Drop") },
{ "ui_toggle_fullscreen", TTRC("Toggle Fullscreen") },
{ "", ""} { "", ""}
/* clang-format on */ /* clang-format on */
}; };
@ -559,6 +582,8 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs = List<Ref<InputEvent>>(); inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(Key::ENTER)); inputs.push_back(InputEventKey::create_reference(Key::ENTER));
inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER)); inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));
inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::ENTER));
inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::KP_ENTER));
default_builtin_cache.insert("ui_text_newline", inputs); default_builtin_cache.insert("ui_text_newline", inputs);
inputs = List<Ref<InputEvent>>(); inputs = List<Ref<InputEvent>>();
@ -856,6 +881,16 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE)); inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE));
default_builtin_cache.insert("ui_colorpicker_delete_preset", inputs); default_builtin_cache.insert("ui_colorpicker_delete_preset", inputs);
// ///// Miscellaneous Shortcuts /////
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::ALT));
default_builtin_cache.insert("ui_toggle_fullscreen", inputs);
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(Key::F | KeyModifierMask::CTRL | KeyModifierMask::META));
inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::ALT));
default_builtin_cache.insert("ui_toggle_fullscreen.macos", inputs);
return default_builtin_cache; return default_builtin_cache;
} }
@ -904,7 +939,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with_featur
} }
void InputMap::load_default() { void InputMap::load_default() {
HashMap<String, List<Ref<InputEvent>>> builtins = get_builtins_with_feature_overrides_applied(); HashMap<String, List<Ref<InputEvent>>> builtins(get_builtins_with_feature_overrides_applied());
for (const KeyValue<String, List<Ref<InputEvent>>> &E : builtins) { for (const KeyValue<String, List<Ref<InputEvent>>> &E : builtins) {
String name = E.key; String name = E.key;

View file

@ -30,6 +30,8 @@
#include "shortcut.h" #include "shortcut.h"
#include "core/object/class_db.h"
void Shortcut::set_events(const Array &p_events) { void Shortcut::set_events(const Array &p_events) {
for (int i = 0; i < p_events.size(); i++) { for (int i = 0; i < p_events.size(); i++) {
Ref<InputEventShortcut> ies = p_events[i]; Ref<InputEventShortcut> ies = p_events[i];

View file

@ -30,10 +30,9 @@
#include "compression.h" #include "compression.h"
#include "core/config/project_settings.h"
#include "core/io/zip_io.h" #include "core/io/zip_io.h"
#include "thirdparty/misc/fastlz.h" #include <thirdparty/misc/fastlz.h>
#include <zstd.h> #include <zstd.h>
@ -41,11 +40,34 @@
#include <brotli/decode.h> #include <brotli/decode.h>
#endif #endif
// Caches for zstd. namespace {
static BinaryMutex mutex; struct ZstdDecompressorContext {
static ZSTD_DCtx *current_zstd_d_ctx = nullptr; ZSTD_DCtx *zstd_d_ctx = nullptr;
static bool current_zstd_long_distance_matching; bool zstd_long_distance_matching = false;
static int current_zstd_window_log_size; int zstd_window_log_size = 0;
~ZstdDecompressorContext() {
if (zstd_d_ctx) {
ZSTD_freeDCtx(zstd_d_ctx);
}
}
void invalidate(bool p_zstd_long_distance_matching, int p_zstd_window_log_size) {
if (!zstd_d_ctx || zstd_long_distance_matching != p_zstd_long_distance_matching || zstd_window_log_size != p_zstd_window_log_size) {
if (zstd_d_ctx) {
ZSTD_freeDCtx(zstd_d_ctx);
}
zstd_d_ctx = ZSTD_createDCtx();
if (p_zstd_long_distance_matching) {
ZSTD_DCtx_setParameter(zstd_d_ctx, ZSTD_d_windowLogMax, p_zstd_window_log_size);
}
zstd_long_distance_matching = p_zstd_long_distance_matching;
zstd_window_log_size = p_zstd_window_log_size;
}
}
};
} //namespace
int64_t Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int64_t p_src_size, Mode p_mode) { int64_t Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int64_t p_src_size, Mode p_mode) {
switch (p_mode) { switch (p_mode) {
@ -198,22 +220,10 @@ int64_t Compression::decompress(uint8_t *p_dst, int64_t p_dst_max_size, const ui
return total; return total;
} break; } break;
case MODE_ZSTD: { case MODE_ZSTD: {
MutexLock lock(mutex); thread_local ZstdDecompressorContext decompressor_ctx;
decompressor_ctx.invalidate(zstd_long_distance_matching, zstd_window_log_size);
if (!current_zstd_d_ctx || current_zstd_long_distance_matching != zstd_long_distance_matching || current_zstd_window_log_size != zstd_window_log_size) { size_t ret = ZSTD_decompressDCtx(decompressor_ctx.zstd_d_ctx, p_dst, p_dst_max_size, p_src, p_src_size);
if (current_zstd_d_ctx) {
ZSTD_freeDCtx(current_zstd_d_ctx);
}
current_zstd_d_ctx = ZSTD_createDCtx();
if (zstd_long_distance_matching) {
ZSTD_DCtx_setParameter(current_zstd_d_ctx, ZSTD_d_windowLogMax, zstd_window_log_size);
}
current_zstd_long_distance_matching = zstd_long_distance_matching;
current_zstd_window_log_size = zstd_window_log_size;
}
size_t ret = ZSTD_decompressDCtx(current_zstd_d_ctx, p_dst, p_dst_max_size, p_src, p_src_size);
return (int64_t)ret; return (int64_t)ret;
} break; } break;
} }
@ -292,7 +302,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int64_t p_max_d
#endif #endif
} else { } else {
// This function only supports GZip and Deflate. // This function only supports GZip and Deflate.
ERR_FAIL_COND_V(p_mode != MODE_DEFLATE && p_mode != MODE_GZIP, Z_ERRNO); ERR_FAIL_COND_V_MSG(p_mode != MODE_DEFLATE && p_mode != MODE_GZIP, Z_ERRNO, "Dynamic decompression is only supported with gzip, DEFLATE, and Brotli compression methods.");
int ret; int ret;
z_stream strm; z_stream strm;

View file

@ -31,6 +31,7 @@
#include "config_file.h" #include "config_file.h"
#include "core/io/file_access_encrypted.h" #include "core/io/file_access_encrypted.h"
#include "core/object/class_db.h"
#include "core/string/string_builder.h" #include "core/string/string_builder.h"
#include "core/variant/variant_parser.h" #include "core/variant/variant_parser.h"

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