feat: updated engine

This commit is contained in:
Sara Gerretsen 2026-07-10 17:04:34 +02:00
parent cbe99774ff
commit f4cf6b3999
6607 changed files with 910135 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.
# 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).
@ -38,7 +41,7 @@ AlignAfterOpenBracket: DontAlign
# AcrossEmptyLines: false
# AcrossComments: false
# AlignCaseColons: false
# AlignEscapedNewlines: Right
AlignEscapedNewlines: DontAlign # Aligning leads to long diffs
AlignOperands: DontAlign
AlignTrailingComments:
Kind: Never
@ -114,14 +117,25 @@ Cpp11BracedListStyle: false
# - BOOST_FOREACH
# IfMacros:
# - KJ_IF_MAYBE
# IncludeBlocks: Preserve
IncludeBlocks: Regroup
IncludeCategories:
- Regex: ^".*"$
Priority: 1
- Regex: ^<.*\.h>$
Priority: 2
- Regex: ^"(core|drivers|editor|main|scene|servers|tests)/.*"$
Priority: 20
- Regex: ^"(modules|platform)/.*"$
Priority: 30
- Regex: ^<thirdparty/.*>$
Priority: 40
- Regex: ^<(windows|Jolt/Jolt|platform_gl)\.h>$
Priority: 50
- Regex: ^<.*\.(h|hpp)>$
Priority: 60
- Regex: ^<.*>$
Priority: 3
Priority: 70
- Regex: ^".*\.compat\.inc"$
Priority: 0
SortPriority: 1
- Regex: ^".*"$
Priority: 10
# IncludeIsMainRegex: (Test)?$
# IncludeIsMainSourceRegex: ""
# 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:
- -*
- cppcoreguidelines-pro-type-member-init
- performance-move-const-arg
- bugprone-use-after-move
- modernize-deprecated-headers
- modernize-redundant-void-arg
- modernize-use-bool-literals
- modernize-use-default-member-init
# - modernize-use-default-member-init # TODO Re-activate
- modernize-use-nullptr
- readability-braces-around-statements
- readability-redundant-member-init
- readability-operators-representation
HeaderFileExtensions: ["", h, hh, hpp, hxx, inc, glsl]
ImplementationFileExtensions: [c, cc, cpp, cxx, m, mm, java]
HeaderFilterRegex: (core|doc|drivers|editor|main|modules|platform|scene|servers|tests)/
FormatStyle: file
CheckOptions:
cppcoreguidelines-pro-type-member-init.IgnoreArrays: true
cppcoreguidelines-pro-type-member-init.UseAssignment: true
modernize-deprecated-headers.CheckHeaderFile: true
modernize-use-bool-literals.IgnoreMacros: false
modernize-use-default-member-init.IgnoreMacros: false
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
---
# Default conditions, apply everywhere.
CompileFlags:
Add:
# Some thirdparty includes seem to require a high -ferror-limit to pass checks.
- -ferror-limit=100
Diagnostics:
Includes:
IgnoreHeader:
- \.compat\.inc
---
# Header-specific conditions.
If:
@ -20,11 +29,19 @@ CompileFlags:
- -Wno-unused-const-variable
- -Wno-unused-function
- -Wno-unused-variable
---
# Suppress all third-party warnings.
# Suppress warnings for third-party or partial code.
If:
PathMatch: thirdparty/.*
PathMatch:
- bin/build_deps/.*
- misc/dist/apple_embedded_xcode/.*
- tests/compatibility_test/.*
- thirdparty/.*
- .*/thirdparty/.*
- .*-so_wrap.[ch]
Diagnostics:
Suppress: "*"

View file

@ -72,3 +72,18 @@ e06d83860d798b6766b23d6eae48557387a7db85
# Style: Replace header guards with `#pragma once`
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
## OS
/drivers/apple*/ @godotengine/macos
/drivers/apple/ @godotengine/macos @godotengine/ios
/drivers/apple_embedded/ @godotengine/ios
/drivers/unix/ @godotengine/linux-bsd
/drivers/windows/ @godotengine/windows
@ -113,7 +114,6 @@
/modules/hdr/ @godotengine/asset-pipeline
/modules/jpg/ @godotengine/asset-pipeline
/modules/ktx/ @godotengine/asset-pipeline
/modules/squish/ @godotengine/asset-pipeline
/modules/svg/ @godotengine/asset-pipeline
/modules/tga/ @godotengine/asset-pipeline
/modules/tinyexr/ @godotengine/asset-pipeline
@ -147,6 +147,10 @@
/modules/meshoptimizer/ @godotengine/rendering
/modules/raycast/ @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
## Scripting
@ -158,7 +162,7 @@
/modules/jsonrpc/tests/ @godotengine/gdscript @godotengine/network @godotengine/tests
/modules/mono/ @godotengine/dotnet
/modules/mono/doc_classes/ @godotengine/dotnet @godotengine/documentation
/modules/mono/editor/ @godotengine/dotnet @godotengine/script-editor
/modules/mono/editor/ @godotengine/dotnet @godotengine/editor
## Text
/modules/freetype/ @godotengine/buildsystem
@ -187,6 +191,7 @@
/modules/gridmap/ @godotengine/3d-nodes
/modules/gridmap/doc_classes/ @godotengine/3d-nodes @godotengine/documentation
/modules/gridmap/editor/ @godotengine/3d-nodes @godotengine/editor
/modules/gridmap/tests/ @godotengine/3d-nodes @godotengine/tests
/modules/navigation_2d/ @godotengine/navigation
/modules/navigation_2d/editor/ @godotengine/navigation @godotengine/editor
/modules/navigation_3d/ @godotengine/navigation
@ -252,7 +257,6 @@
/scene/resources/shader* @godotengine/shaders
/scene/resources/skeleton* @godotengine/animation
/scene/resources/text_* @godotengine/gui-nodes
/scene/resources/visual_shader* @godotengine/shaders
/scene/theme/ @godotengine/gui-nodes
# 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:
https://contributing.godotengine.org/en/latest/engine/guidelines/code_style.html
Use of AI must be disclosed and should include a description of how it was used.
-->
## 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
steps:
- name: Download Godot Artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: ${{ inputs.name }}
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
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
submodules: recursive
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
on:
workflow_call:
secrets:
SERVICE_ACCOUNT_KEY:
required: true
workflow_dispatch:
# Global Settings
@ -8,7 +12,6 @@ env:
SCONS_FLAGS: >-
dev_mode=yes
module_text_server_fb_enabled=yes
tests=no
swappy=yes
jobs:
@ -23,6 +26,7 @@ jobs:
- name: Editor (target=editor)
cache-name: android-editor
target: editor
instrumented_tests: true
scons-flags: >-
arch=arm64
production=yes
@ -30,21 +34,23 @@ jobs:
- name: Template arm32 (target=template_debug, arch=arm32)
cache-name: android-template-arm32
target: template_debug
instrumented_tests: false
scons-flags: arch=arm32
- name: Template arm64 (target=template_debug, arch=arm64)
cache-name: android-template-arm64
target: template_debug
instrumented_tests: true
scons-flags: arch=arm64
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
submodules: recursive
- name: Set up Java 17
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 17
@ -116,3 +122,56 @@ jobs:
with:
name: ${{ matrix.cache-name }}-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: >-
dev_mode=yes
module_text_server_fb_enabled=yes
tests=no
debug_symbols=no
jobs:
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
runs-on: macos-latest
runs-on: macos-26
name: Template (target=template_release)
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
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
uses: ./.github/actions/godot-cache-restore
continue-on-error: true

View file

@ -1,6 +1,11 @@
name: 🐧 Linux Builds
on:
workflow_call:
inputs:
changed-files:
description: A list of changed files, pre-filtered for parsing by clangd-tidy.
required: true
type: string
workflow_dispatch:
# Global Settings
@ -8,7 +13,6 @@ env:
SCONS_FLAGS: >-
dev_mode=yes
module_text_server_fb_enabled=yes
"accesskit_sdk_path=${{ github.workspace }}/accesskit-c-0.21.2/"
GODOT_CPP_BRANCH: 4.5
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
@ -30,15 +34,17 @@ jobs:
- name: Editor w/ Mono (target=editor)
cache-name: linux-editor-mono
target: editor
scons-flags: module_mono_enabled=yes
scons-flags: module_mono_enabled=yes compiledb=yes
bin: ./bin/godot.linuxbsd.editor.x86_64.mono
build-mono: true
doc-test: true
proj-conv: true
proj-export: true
api-compat: true
artifact: true
# Validate godot-cpp compatibility on one arbitrary editor build.
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)
cache-name: linux-editor-double-sanitizers
@ -54,6 +60,7 @@ jobs:
linker=mold
bin: ./bin/godot.linuxbsd.editor.dev.double.x86_64.san
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)
cache-name: linux-editor-llvm-sanitizers
@ -67,6 +74,7 @@ jobs:
bin: ./bin/godot.linuxbsd.editor.dev.x86_64.llvm.san
# Test our oldest supported SCons/Python versions on one arbitrary editor build.
legacy-scons: true
free-space: true
- name: Editor with ThreadSanitizer (target=editor, dev_build=yes, use_tsan=yes, use_llvm=yes, linker=lld)
cache-name: linux-editor-thread-sanitizer
@ -77,6 +85,7 @@ jobs:
use_llvm=yes
linker=lld
bin: ./bin/godot.linuxbsd.editor.dev.x86_64.llvm.san
free-space: true
- name: Template w/ Mono, release (target=template_release)
cache-name: linux-template-mono
@ -110,7 +119,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
submodules: recursive
@ -118,11 +127,12 @@ jobs:
run: |
sudo apt-get update
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
fi
- name: Free disk space on runner
if: matrix.free-space
run: |
echo "Disk usage before:" && df -h
sudo rm -rf /usr/local/lib/android
@ -143,8 +153,8 @@ jobs:
uses: ./.github/actions/godot-deps
with:
# Sync with Ensure*Version in SConstruct.
python-version: 3.8
scons-version: 4.0
python-version: 3.9
scons-version: 4.4
- name: Force remove preinstalled .NET SDKs
if: matrix.build-mono
@ -153,21 +163,21 @@ jobs:
- name: Setup older .NET SDK as baseline
if: matrix.build-mono
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
# Targeting the oldest version we want to support to ensure it still builds.
dotnet-version: 8.0.100
- name: Download pre-built AccessKit
uses: dsaltares/fetch-gh-release-asset@1.1.2
with:
repo: godotengine/godot-accesskit-c-static
version: tags/0.21.2
file: accesskit-c-0.21.2.zip
target: accesskit-c-0.21.2/accesskit_c.zip
- name: Extract pre-built AccessKit
run: unzip -o accesskit-c-0.21.2/accesskit_c.zip
shell: sh
id: accesskit-sdk
run: |
if python ./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: Install mold linker
if: matrix.proj-test
@ -176,10 +186,16 @@ jobs:
- name: Compilation
uses: ./.github/actions/godot-build
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
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)
uses: ./.github/actions/godot-cpp-build
if: matrix.godot-cpp
@ -208,6 +224,7 @@ jobs:
- name: Prepare artifact
if: matrix.artifact
run: |
rm -rf ./bin/build_deps
strip 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$'
# Check API backwards compatibility
- name: Check for GDExtension compatibility
- name: Check for GDExtension compatibility JSON check
if: matrix.api-compat
run: |
./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
- name: Test Godot project
uses: ./.github/actions/godot-project-test
@ -250,6 +274,13 @@ jobs:
with:
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
- name: Test project converter
uses: ./.github/actions/godot-converter-test

View file

@ -8,12 +8,11 @@ env:
SCONS_FLAGS: >-
dev_mode=yes
module_text_server_fb_enabled=yes
"accesskit_sdk_path=${{ github.workspace }}/accesskit-c-0.21.2/"
jobs:
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
runs-on: macos-latest
runs-on: macos-26
name: ${{ matrix.name }}
timeout-minutes: 120
strategy:
@ -33,14 +32,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
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
uses: ./.github/actions/godot-cache-restore
with:
@ -50,16 +45,27 @@ jobs:
- name: Setup Python and SCons
uses: ./.github/actions/godot-deps
- name: Download pre-built AccessKit
uses: dsaltares/fetch-gh-release-asset@1.1.2
with:
repo: godotengine/godot-accesskit-c-static
version: tags/0.21.2
file: accesskit-c-0.21.2.zip
target: accesskit-c-0.21.2/accesskit_c.zip
- name: Download pre-built ANGLE
shell: sh
id: angle-sdk
run: |
if python ./misc/scripts/install_angle.py; then
echo "ANGLE_ENABLED=yes" >> "$GITHUB_OUTPUT"
else
echo "::warning::ANGLE SDK installation failed, building without ANGLE support."
echo "ANGLE_ENABLED=no" >> "$GITHUB_OUTPUT"
fi
- name: Extract pre-built AccessKit
run: unzip -o accesskit-c-0.21.2/accesskit_c.zip
- name: Download pre-built AccessKit
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
id: vulkan-sdk
@ -75,14 +81,14 @@ jobs:
- name: Compilation (x86_64)
uses: ./.github/actions/godot-build
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
target: ${{ matrix.target }}
- name: Compilation (arm64)
uses: ./.github/actions/godot-build
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
target: ${{ matrix.target }}
@ -96,6 +102,7 @@ jobs:
run: |
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 -rf ./bin/build_deps
strip bin/godot.*
chmod +x bin/godot.*

View file

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

View file

@ -1,6 +1,13 @@
name: 📊 Static Checks
on:
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:
jobs:
@ -8,11 +15,15 @@ jobs:
name: Code style, file formatting, and docs
runs-on: ubuntu-24.04
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:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
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.
- name: .gitignore checks (gitignore_check.sh)
@ -20,18 +31,17 @@ jobs:
bash ./misc/scripts/gitignore_check.sh
- name: Get changed files
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
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
id: changed-files
uses: tj-actions/changed-files@v47
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:
SCONS_FLAGS: >-
dev_mode=yes
tests=no
debug_symbols=no
use_closure_compiler=yes
EM_VERSION: 4.0.11
@ -21,26 +20,26 @@ jobs:
fail-fast: false
matrix:
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
target: template_release
scons-flags: threads=yes
scons-flags: threads=yes arch=wasm64
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
target: template_release
scons-flags: threads=no
scons-flags: threads=no arch=wasm32
artifact: true
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
submodules: recursive
- name: Set up Emscripten latest
uses: mymindstorm/setup-emsdk@v14
uses: emscripten-core/setup-emsdk@v16
with:
version: ${{ env.EM_VERSION }}
no-cache: true

View file

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

3
engine/.gitignore vendored
View file

@ -23,7 +23,6 @@
###########################
/custom.py
misc/hooks/pre-commit-custom-*
#############################
### 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_64/abi.json
thirdparty/perfetto/
# Visual Studio 2015/2017 cache/options directory
.vs/

View file

@ -43,6 +43,7 @@ DmitriySalnikov <salnikov.mine@yandex.ru> <dimokgamer@gmail.com>
Dominik 'dreamsComeTrue' Jasiński <dominikjasinski@o2.pl>
DeeJayLSP <djlsplays@gmail.com> <60024671+DeeJayLSP@users.noreply.github.com>
Emmanuel Barroga <emmanuelbarroga@gmail.com>
Enzo Novoselic <41305715+StarryWorm@users.noreply.github.com>
Eric M <itsjusteza@gmail.com>
Eric Rybicki <info@ericrybicki.com> <stratos695@googlemail.com>
Erik Selecký <35656626+rxlecky@users.noreply.github.com>
@ -67,7 +68,7 @@ Guilherme Felipe <guilhermefelipecgs@gmail.com>
Hakim <hakim.rouatbi@gmail.com>
Hanif Bin Ariffin <hanif.ariffin.4326@gmail.com>
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> <Calinou@users.noreply.github.com>
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> <fire@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> <eska014@users.noreply.github.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>
Ricardo Subtil <ricasubtil@gmail.com>
Rindbee <idleman@yeah.net>
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 <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>
romulox_x <romulox_x@yahoo.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> <schtroumps31@gmail.com>
Will Nations <willnationsdev@gmail.com>
Xu Huisheng <xyz20003@gmail.com>
ydeltastar <ydeltastar@gmail.com>
yg2f <yoann@terminajones.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.
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v21.1.7
rev: v22.1.5
hooks:
- id: clang-format
files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java)$
@ -29,43 +29,49 @@ repos:
types_or: [text]
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
rev: v1.3.5
hooks:
- id: clang-tidy
files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java|glsl)$
args: [--fix, --quiet, --use-color]
# TODO .inc ignored for now because they don't include their parent header.
# 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]
additional_dependencies: [clang-tidy==21.1.6]
require_serial: true
stages: [manual] # Not automatically triggered, invoked via `pre-commit run --hook-stage manual clang-tidy`
additional_dependencies: [clang-tidy==22.1.7]
require_serial: false
stages: [manual]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.7
rev: v0.15.20
hooks:
- id: ruff-check
args: [--fix]
args: [--color=always]
files: (\.py|SConstruct|SCsub)$
types_or: [text]
- id: ruff-format
args: [--color=always]
files: (\.py|SConstruct|SCsub)$
types_or: [text]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.14.1 # Latest version that supports Python 3.8
rev: v1.19.1
hooks:
- id: mypy
files: \.py$
types_or: [text]
- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
rev: v2.4.2
hooks:
- id: codespell
additional_dependencies: [tomli]
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.34.1
rev: 0.37.4
hooks:
- id: check-jsonschema
files: ^core/extension/gdextension_interface\.json$
@ -103,6 +109,18 @@ repos:
files: ^(doc/classes|.*/doc_classes)/.*\.xml$
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
name: eslint
language: node
@ -148,7 +166,7 @@ repos:
entry: svgo
files: \.svg$
args: [--quiet, --config, misc/utility/svgo.config.mjs]
additional_dependencies: [svgo@3.3.2]
additional_dependencies: [svgo@4.0.1]
- id: copyright-headers
name: copyright-headers

View file

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

View file

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

View file

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

View file

@ -2,7 +2,7 @@
<p align="center">
<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>
</p>

View file

@ -1,8 +1,8 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
EnsureSConsVersion(4, 0)
EnsurePythonVersion(3, 8)
EnsureSConsVersion(4, 4)
EnsurePythonVersion(3, 9)
# System
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("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("accesskit", "Use AccessKit C SDK", True))
opts.Add(("accesskit_sdk_path", "Path to the AccessKit C SDK", ""))
opts.Add(BoolVariable("accesskit", "Enable the AccessKit driver for screen reader support", True))
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(
EnumVariable(
@ -222,6 +222,13 @@ opts.Add(
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
@ -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("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_gen_only", "Exit after building the compilation database", False))
opts.Add(
"num_jobs",
"Use up to N jobs when compiling (equivalent to `-j N`). Defaults to max jobs - 1. Ignored if -j is used.",
@ -648,8 +656,11 @@ if env["build_profile"] != "":
dbo = ft["disabled_build_options"]
for c in dbo:
env[c] = dbo[c]
except json.JSONDecodeError:
print_error(f'Failed to open feature build profile: "{env["build_profile"]}"')
except json.JSONDecodeError as err:
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)
# '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."
)
elif methods.using_gcc(env):
if cc_version_major < 9:
print_error(
"Detected GCC version older than 9, which does not fully support "
"C++17, or has bugs when compiling Godot. Supported versions are 9 "
"and later. Use a newer GCC version, or Clang 6 or later by passing "
'"use_llvm=yes" to the SCons command line.'
)
Exit(255)
elif cc_version_metadata1 == "win32":
if env.get("winrt"):
if cc_version_major < 11:
print_error(
"Detected GCC version older than 11, which does not fully support "
"C++20, or has bugs when compiling Godot. Supported versions are 12 "
"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 '
'passing "winrt=no" to the SCons command line.'
)
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(
"Detected mingw version is not using posix threads. Only posix "
"version of mingw is supported. "
@ -735,35 +757,38 @@ elif methods.using_clang(env):
)
Exit(255)
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
if env.get("winrt"):
if cc_version_major < 13:
print_error(
"Detected Clang version older than 13, which does not fully support "
"C++20. Supported versions are Clang 14 and later, or disable WinRT "
'support by passing "winrt=no" to the SCons command line.'
)
Exit(255)
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:
# 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:
# Ensure latest minor build of Visual Studio 2019.
# https://github.com/godotengine/godot/pull/94995#issuecomment-2336464574
print_error(
"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)
if cc_version_major == 15 and cc_version_minor < 9:
elif cc_version_major < 16:
print_error(
"Detected Visual Studio 2017 version older than 15.9, which has bugs "
"when compiling Godot. Use a newer VS2017 version, or VS2019/VS2022."
)
Exit(255)
if cc_version_major < 15:
print_error(
"Detected Visual Studio 2015 or earlier, which is unsupported in Godot. "
"Supported versions are Visual Studio 2017 and later."
"Detected Visual Studio 2017 or earlier, which is unsupported in Godot. "
"Supported versions are Visual Studio 2019 and later."
)
Exit(255)
@ -891,12 +916,9 @@ if not env.msvc:
env.Prepend(CXXFLAGS=["-std=gnu++17"])
else:
# 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"])
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.
# Note that this is still not complete conformance, as certain Windows-related headers
# don't compile under complete conformance.
@ -964,6 +986,9 @@ else: # GCC, Clang
common_warnings += ["-Wno-return-type"]
if cc_version_major >= 11:
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):
common_warnings += ["-Wshadow-field-in-constructor", "-Wshadow-uncaptured-local"]
# We often implement `operator<` for structs of pointers as a requirement
@ -1005,6 +1030,7 @@ else: # GCC, Clang
if env["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"):
suffix = "." + detect.get_program_suffix()
@ -1169,16 +1195,11 @@ env.Append(BUILDERS=GLSL_BUILDERS)
if env["compiledb"]:
env.Tool("compilation_db")
env.Alias("compiledb", env.CompilationDatabase())
env.NoCache(env.CompilationDatabase())
if not env["verbose"]:
env["COMPILATIONDB_COMSTR"] = "$GENCOMSTR"
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")
env["NINJA_FILE_NAME"] = env["ninja_file"]
env["NINJA_DISABLE_AUTO_RUN"] = not env["ninja_auto_run"]
@ -1229,6 +1250,12 @@ if env["vsproj"]:
# Miscellaneous & post-build methods.
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.show_progress(env)
methods.prepare_purge(env)

View file

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

View file

@ -34,6 +34,7 @@
#include "core/config/project_settings.h"
#include "core/donors.gen.h"
#include "core/license.gen.h"
#include "core/object/object.h"
#include "core/variant/typed_array.h"
#include "core/version.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) {
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);
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),
ptr(p_ptr),
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 "core/core_bind.h" // For Compression enum.
#include "core/input/input_map.h"
#include "core/io/compression.h"
#include "core/io/config_file.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "core/io/file_access_pack.h"
#include "core/io/marshalls.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/os/os.h"
#include "core/templates/rb_set.h"
#include "core/variant/typed_array.h"
#include "core/variant/variant_parser.h"
#include "core/version.h"
#include "servers/rendering/rendering_server.h"
#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
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) {
props.erase(p_name);
if (p_name.operator String().begins_with("autoload/")) {
String node_name = p_name.operator String().get_slicec('/', 1);
if (p_name.string().begins_with("autoload/")) {
String node_name = p_name.string().get_slicec('/', 1);
if (autoloads.has(node_name)) {
remove_autoload(node_name);
}
} else if (p_name.operator String().begins_with("global_group/")) {
String group_name = p_name.operator String().get_slicec('/', 1);
} else if (p_name.string().begins_with("global_group/")) {
String group_name = p_name.string().get_slicec('/', 1);
if (global_groups.has(group_name)) {
remove_global_group(group_name);
}
@ -317,9 +322,9 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
}
{ // Feature overrides.
int dot = p_name.operator String().find_char('.');
int dot = p_name.string().find_char('.');
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++) {
String feature = s[i].strip_edges();
@ -339,8 +344,8 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
} else {
props[p_name] = VariantContainer(p_value, last_order++);
}
if (p_name.operator String().begins_with("autoload_prepend/")) {
String node_name = p_name.operator String().get_slicec('/', 1);
if (p_name.string().begins_with("autoload_prepend/")) {
String node_name = p_name.string().get_slicec('/', 1);
AutoloadInfo autoload;
autoload.name = node_name;
String path = p_value;
@ -351,8 +356,8 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
autoload.path = path.simplify_path();
}
add_autoload(autoload, true);
} else if (p_name.operator String().begins_with("autoload/")) {
String node_name = p_name.operator String().get_slicec('/', 1);
} else if (p_name.string().begins_with("autoload/")) {
String node_name = p_name.string().get_slicec('/', 1);
AutoloadInfo autoload;
autoload.name = node_name;
String path = p_value;
@ -363,8 +368,8 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
autoload.path = path.simplify_path();
}
add_autoload(autoload);
} else if (p_name.operator String().begins_with("global_group/")) {
String group_name = p_name.operator String().get_slicec('/', 1);
} else if (p_name.string().begins_with("global_group/")) {
String group_name = p_name.string().get_slicec('/', 1);
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) {
// 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")) {
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());
}
}
@ -1057,6 +1063,7 @@ bool ProjectSettings::is_builtin_setting(const String &p_name) const {
void ProjectSettings::clear(const String &p_name) {
ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name));
props.erase(p_name);
_queue_changed(p_name);
}
Error ProjectSettings::save() {
@ -1640,7 +1647,7 @@ void ProjectSettings::_bind_methods() {
void ProjectSettings::_add_builtin_input_map() {
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) {
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/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:
// - 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/minimize_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_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("animation/warnings/check_invalid_skeleton_modifier_node_paths", true);
GLOBAL_DEF("animation/warnings/check_invalid_track_paths", true);
GLOBAL_DEF("animation/warnings/check_angle_interpolation_type_conflicting", true);
#ifndef DISABLE_DEPRECATED
@ -1751,10 +1763,10 @@ ProjectSettings::ProjectSettings() {
_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");
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");
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_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_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
#include "core/object/object.h"
#include "core/os/thread_safe.h"
#include "core/templates/rb_map.h"
template <typename T>
@ -204,7 +205,7 @@ public:
const HashMap<StringName, PropertyInfo> &get_custom_property_info() const;
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_and_custom_features(const StringName &p_name, const Vector<String> &p_features) const;

View file

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

View file

@ -31,6 +31,7 @@
#include "core_bind.h"
#include "core_bind.compat.inc"
#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/crypto/crypto_core.h"
#include "core/debugger/engine_debugger.h"
@ -39,8 +40,11 @@
#include "core/io/marshalls.h"
#include "core/math/geometry_2d.h"
#include "core/math/geometry_3d.h"
#include "core/object/class_db.h"
#include "core/os/keyboard.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/variant/typed_array.h"
@ -90,11 +94,13 @@ Vector<String> ResourceLoader::get_recognized_extensions_for_type(const String &
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);
}
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);
}
@ -132,6 +138,10 @@ ResourceUID::ID ResourceLoader::get_resource_uid(const String &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) {
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("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_type", "path"), &ResourceLoader::get_resource_type);
ClassDB::bind_method(D_METHOD("list_directory", "directory_path"), &ResourceLoader::list_directory);
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);
}
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;
::ResourceSaver::get_recognized_extensions(p_resource, &exts);
Vector<String> ret;
@ -185,11 +197,13 @@ Vector<String> ResourceSaver::get_recognized_extensions(const Ref<Resource> &p_r
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);
}
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);
}
@ -425,7 +439,7 @@ int OS::create_instance(const Vector<String> &p_arguments) {
for (const String &arg : p_arguments) {
args.push_back(arg);
}
::OS::ProcessID pid = 0;
ProcessID pid = 0;
Error err = ::OS::get_singleton()->create_instance(args, &pid);
if (err != OK) {
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) {
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);
if (err != OK) {
return -1;
@ -553,6 +567,10 @@ String OS::get_locale() const {
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 {
return ::OS::get_singleton()->get_locale_language();
}
@ -704,8 +722,8 @@ String OS::get_unique_id() const {
return ::OS::get_singleton()->get_unique_id();
}
void OS::add_logger(const Ref<Logger> &p_logger) {
ERR_FAIL_COND(p_logger.is_null());
void OS::add_logger(RequiredParam<Logger> rp_logger) {
EXTRACT_PARAM_OR_FAIL(p_logger, rp_logger);
if (!logger_bind) {
logger_bind = memnew(LoggerBind);
@ -716,8 +734,8 @@ void OS::add_logger(const Ref<Logger> &p_logger) {
logger_bind->loggers.push_back(p_logger);
}
void OS::remove_logger(const Ref<Logger> &p_logger) {
ERR_FAIL_COND(p_logger.is_null());
void OS::remove_logger(RequiredParam<Logger> rp_logger) {
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.");
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_msec", "msec"), &OS::delay_msec);
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_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);
}
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;
::ClassDB::get_property(p_object, p_property, 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;
bool 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);
for (const StringName &E : classes) {
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);
}
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(!String(p_name).is_valid_ascii_identifier(), vformat("Singleton name is not a valid identifier: '%s'.", p_name));
::Engine::Singleton s;
@ -2012,11 +2034,13 @@ Vector<String> Engine::get_singleton_list() const {
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);
}
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);
}

View file

@ -31,8 +31,10 @@
#pragma once
#include "core/debugger/engine_profiler.h"
#include "core/io/logger.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/object/class_db.h"
#include "core/object/script_backtrace.h"
#include "core/os/semaphore.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);
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 remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader);
void add_resource_format_loader(RequiredParam<ResourceFormatLoader> p_format_loader, bool p_at_front);
void remove_resource_format_loader(RequiredParam<ResourceFormatLoader> p_format_loader);
void set_abort_on_missing_resources(bool p_abort);
PackedStringArray get_dependencies(const String &p_path);
bool has_cached(const String &p_path);
Ref<Resource> get_cached_ref(const String &p_path);
bool exists(const String &p_path, const String &p_type_hint = "");
ResourceUID::ID get_resource_uid(const String &p_path);
String get_resource_type(const String &p_path);
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 set_uid(const String &p_path, ResourceUID::ID p_uid);
Vector<String> get_recognized_extensions(const Ref<Resource> &p_resource);
void add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front);
void remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver);
Vector<String> get_recognized_extensions(RequiredParam<Resource> p_resource);
void add_resource_format_saver(RequiredParam<ResourceFormatSaver> p_format_saver, bool p_at_front);
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);
@ -247,6 +250,7 @@ public:
Vector<String> get_video_adapter_driver_info() const;
String get_locale() const;
Vector<String> get_preferred_locales() const;
String get_locale_language() const;
String get_model_name() const;
@ -309,8 +313,8 @@ public:
Vector<String> get_granted_permissions() const;
void revoke_granted_permissions();
void add_logger(const Ref<Logger> &p_logger);
void remove_logger(const Ref<Logger> &p_logger);
void add_logger(RequiredParam<Logger> p_logger);
void remove_logger(RequiredParam<Logger> p_logger);
void remove_script_loggers(const ScriptLanguage *p_script);
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;
StringName class_get_property_getter(const StringName &p_class, const StringName &p_property);
StringName class_get_property_setter(const StringName &p_class, const StringName &p_property);
Variant class_get_property(Object *p_object, const StringName &p_property) const;
Error class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const;
Variant class_get_property(RequiredParam<Object> p_object, const StringName &p_property) 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;
@ -609,12 +613,12 @@ public:
bool has_singleton(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);
Vector<String> get_singleton_list() const;
Error register_script_language(ScriptLanguage *p_language);
Error unregister_script_language(const ScriptLanguage *p_language);
Error register_script_language(RequiredParam<ScriptLanguage> p_language);
Error unregister_script_language(RequiredParam<const ScriptLanguage> p_language);
int get_script_language_count();
ScriptLanguage *get_script_language(int p_index) 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"
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())
)
@ -63,10 +63,10 @@ def encryption_key_builder(target, source, env):
with methods.generated_wrapper(str(target[0])) as file:
file.write(
f"""\
#include "core/config/project_settings.h"
#include <cstdint>
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_uncompressed_size = {decomp_size};
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/input/input_event.h"
#include "core/object/class_db.h"
#include "core/input/input_enums.h"
#include "core/object/method_bind_common.h" // IWYU pragma: keep. For `VARIANT_BITFIELD_CAST(MethodFlags)`.
#include "core/os/keyboard.h"
#include "core/variant/type_info.h"
#include "core/variant/variant.h"
#include "core/variant/variant_caster.h"
struct _CoreConstant {
#ifdef DEBUG_ENABLED
@ -69,183 +71,183 @@ static HashMap<StringName, Vector<_CoreConstant>> _global_enums;
#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_map[#m_constant] = _global_constants.size() - 1;
#define BIND_CORE_ENUM_CONSTANT(m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \
#define BIND_CORE_ENUM_CONSTANT(m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_BITFIELD_FLAG(m_constant) \
{ \
StringName enum_name = __constant_get_bitfield_name(m_constant); \
#define BIND_CORE_BITFIELD_FLAG(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_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]); \
}
// This just binds enum classes as if they were regular enum constants.
#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \
{ \
StringName enum_name = __constant_get_enum_name(m_enum::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); \
_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]); \
_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); \
#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, false, 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_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_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_CUSTOM(m_enum, m_name, m_member) \
{ \
StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \
#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, false, true)); \
_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_constants_map[#m_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) \
#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_map[#m_constant] = _global_constants.size() - 1;
#define BIND_CORE_ENUM_CONSTANT_NO_VAL(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_map[#m_constant] = _global_constants.size() - 1; \
#define BIND_CORE_ENUM_CONSTANT_NO_VAL(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_map[#m_constant] = _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) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant, true)); \
_global_constants_map[m_custom_name] = _global_constants.size() - 1; \
#define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(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, true)); \
_global_constants_map[m_custom_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#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_map[#m_constant] = _global_constants.size() - 1;
#define BIND_CORE_ENUM_CONSTANT(m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \
#define BIND_CORE_ENUM_CONSTANT(m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_BITFIELD_FLAG(m_constant) \
{ \
StringName enum_name = __constant_get_bitfield_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \
#define BIND_CORE_BITFIELD_FLAG(m_constant) \
{ \
StringName enum_name = __constant_get_bitfield_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _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.
#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \
{ \
StringName enum_name = __constant_get_enum_name(m_enum::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); \
_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_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_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_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); \
#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_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_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_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_map[#m_constant] = _global_constants.size() - 1;
#define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \
#define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _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) \
{ \
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; \
#define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(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]); \
}
@ -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, PADDLE4);
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, MAX);
@ -729,6 +736,18 @@ void register_global_constants() {
BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_VIRTUAL_REQUIRED);
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_BOOL", Variant::BOOL);
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 print_line_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")
def _escape_define(env, config):
return f"<{config}>" if env["ninja"] and env.msvc else f'\\"{config}\\"'
env_crypto = env.Clone()
env_crypto.AddMethod(_escape_define, "EscapeDefine")
is_builtin = env["builtin_mbedtls"]
has_module = env["module_mbedtls_enabled"]
thirdparty_obj = []
configs = []
if is_builtin or not has_module:
# 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
# be easily extracted.
env_crypto.Prepend(CPPPATH=["#thirdparty/mbedtls/include"])
# We decided not to depend on system mbedtls and bundle a minimal version
# when building without the module.
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).
# 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
# to make a "light" build with only the necessary mbedtls files.
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).
env_thirdparty = env_crypto.Clone()
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 = [
"aes.c",
"base64.c",
"constant_time.c",
"ctr_drbg.c",
"entropy.c",
"md.c",
"md5.c",
"sha1.c",
"sha256.c",
"godot_core_mbedtls_platform.c",
"tf-psa-crypto/core/psa_crypto.c",
"tf-psa-crypto/core/psa_crypto_client.c",
"tf-psa-crypto/core/psa_crypto_driver_wrappers_no_static.c",
"tf-psa-crypto/core/psa_crypto_random.c",
"tf-psa-crypto/core/psa_crypto_slot_management.c",
"tf-psa-crypto/utilities/base64.c",
"tf-psa-crypto/utilities/constant_time.c",
"tf-psa-crypto/platform/threading.c",
"tf-psa-crypto/extras/md.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]
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_mbedtls_sources)
# 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
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
core_obj = []
env_crypto.add_source_files(core_obj, "*.cpp")
env.core_sources += core_obj
# 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. */
/**************************************************************************/
#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) {
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.
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");
// Initialization vector.
if (p_mode == MODE_CBC_ENCRYPT || p_mode == MODE_CBC_DECRYPT) {
ERR_FAIL_COND_V_MSG(p_iv.size() != 16, ERR_INVALID_PARAMETER, "The initialization vector (IV) must be exactly 16 bytes.");
iv.resize(0);
iv.append_array(p_iv);
AESMode ctx_mode = AESMode::NONE;
AESCipher ctx_cipher = AESCipher::NONE;
switch (p_mode) {
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) {
ctx.set_encode_key(p_key.ptr(), key_bits);
} else {
ctx.set_decode_key(p_key.ptr(), key_bits);
// Initialization vector.
PackedByteArray iv = p_iv;
ERR_FAIL_COND_V_MSG(ctx_cipher == AESCipher::CBC && iv.size() != 16, ERR_INVALID_PARAMETER, "The initialization vector (IV) must be exactly 16 bytes.");
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;
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");
PackedByteArray out;
out.resize(len);
const uint8_t *src_ptr = p_src.ptr();
uint8_t *out_ptr = out.ptrw();
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!");
}
Error err = ctx.update(p_src.ptr(), len, out.ptrw(), len);
ERR_FAIL_COND_V(err != OK, PackedByteArray());
return out;
}
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.");
PackedByteArray out;
out.append_array(iv);
return out;
ERR_FAIL_V_MSG(PackedByteArray(), "Calling 'get_iv_state' is no longer supported.");
}
void AESContext::finish() {
ctx.finish(nullptr, 0);
mode = MODE_MAX;
iv.resize(0);
}
void AESContext::_bind_methods() {

View file

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

View file

@ -30,6 +30,8 @@
#include "crypto.h"
#include "core/object/class_db.h"
/// Resources
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("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/io/resource.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/object/ref_counted.h"
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
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 "core/os/os.h"
#include "core/string/ustring.h"
#include <mbedtls/aes.h>
#include <mbedtls/base64.h>
#include <mbedtls/ctr_drbg.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;
}
#include <psa/crypto.h>
// MD5
CryptoCore::MD5Context::MD5Context() {
ctx = memalloc(sizeof(mbedtls_md5_context));
mbedtls_md5_init((mbedtls_md5_context *)ctx);
ctx = memalloc_zeroed(sizeof(psa_hash_operation_t));
}
CryptoCore::MD5Context::~MD5Context() {
mbedtls_md5_free((mbedtls_md5_context *)ctx);
memfree((mbedtls_md5_context *)ctx);
psa_hash_abort((psa_hash_operation_t *)ctx);
memfree((psa_hash_operation_t *)ctx);
}
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;
}
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;
}
Error CryptoCore::MD5Context::finish(unsigned char r_hash[16]) {
int ret = mbedtls_md5_finish_ret((mbedtls_md5_context *)ctx, r_hash);
return ret ? FAILED : OK;
size_t size = 0;
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
CryptoCore::SHA1Context::SHA1Context() {
ctx = memalloc(sizeof(mbedtls_sha1_context));
mbedtls_sha1_init((mbedtls_sha1_context *)ctx);
ctx = memalloc_zeroed(sizeof(psa_hash_operation_t));
}
CryptoCore::SHA1Context::~SHA1Context() {
mbedtls_sha1_free((mbedtls_sha1_context *)ctx);
memfree((mbedtls_sha1_context *)ctx);
psa_hash_abort((psa_hash_operation_t *)ctx);
memfree((psa_hash_operation_t *)ctx);
}
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;
}
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;
}
Error CryptoCore::SHA1Context::finish(unsigned char r_hash[20]) {
int ret = mbedtls_sha1_finish_ret((mbedtls_sha1_context *)ctx, r_hash);
return ret ? FAILED : OK;
size_t size = 0;
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
CryptoCore::SHA256Context::SHA256Context() {
ctx = memalloc(sizeof(mbedtls_sha256_context));
mbedtls_sha256_init((mbedtls_sha256_context *)ctx);
ctx = memalloc_zeroed(sizeof(psa_hash_operation_t));
}
CryptoCore::SHA256Context::~SHA256Context() {
mbedtls_sha256_free((mbedtls_sha256_context *)ctx);
memfree((mbedtls_sha256_context *)ctx);
psa_hash_abort((psa_hash_operation_t *)ctx);
memfree((psa_hash_operation_t *)ctx);
}
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;
}
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;
}
Error CryptoCore::SHA256Context::finish(unsigned char r_hash[32]) {
int ret = mbedtls_sha256_finish_ret((mbedtls_sha256_context *)ctx, r_hash);
return ret ? FAILED : OK;
size_t size = 0;
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
CryptoCore::AESContext::AESContext() {
ctx = memalloc(sizeof(mbedtls_aes_context));
mbedtls_aes_init((mbedtls_aes_context *)ctx);
ctx = memalloc_zeroed(sizeof(psa_cipher_operation_t));
}
CryptoCore::AESContext::~AESContext() {
mbedtls_aes_free((mbedtls_aes_context *)ctx);
memfree((mbedtls_aes_context *)ctx);
psa_cipher_abort((psa_cipher_operation_t *)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) {
int ret = mbedtls_aes_setkey_enc((mbedtls_aes_context *)ctx, p_key, p_bits);
return ret ? FAILED : OK;
void CryptoCore::AESContext::reset() {
psa_cipher_abort((psa_cipher_operation_t *)ctx);
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) {
int ret = mbedtls_aes_setkey_dec((mbedtls_aes_context *)ctx, p_key, p_bits);
return ret ? FAILED : OK;
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) {
reset();
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]) {
int ret = mbedtls_aes_crypt_ecb((mbedtls_aes_context *)ctx, MBEDTLS_AES_ENCRYPT, p_src, r_dst);
return ret ? FAILED : OK;
Error CryptoCore::AESContext::update(const uint8_t *p_src, size_t p_src_size, uint8_t *r_dst, size_t p_dst_size) {
size_t size = 0;
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) {
int ret = mbedtls_aes_crypt_cbc((mbedtls_aes_context *)ctx, MBEDTLS_AES_ENCRYPT, p_length, r_iv, p_src, r_dst);
return ret ? FAILED : OK;
}
Error CryptoCore::AESContext::encrypt_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_ENCRYPT, p_length, &iv_off, p_iv, p_src, r_dst);
return ret ? FAILED : 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;
Error CryptoCore::AESContext::finish(uint8_t *r_dst, size_t p_dst_size) {
size_t size = 0;
int ret = psa_cipher_finish((psa_cipher_operation_t *)ctx, r_dst, p_dst_size, &size);
reset();
if (ret) {
return FAILED;
}
ERR_FAIL_COND_V(p_dst_size != size, ERR_BUG);
return OK;
}
// 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) {
size_t b64len = p_src_len / 3 * 4 + 4 + 1;
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]) {
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;
}
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;
}
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;
}
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
#include "core/object/ref_counted.h"
#include "core/error/error_list.h"
#include <cstddef>
#include <cstdint>
class String;
class CryptoCore {
private:
static bool initialized;
public:
class RandomGenerator {
private:
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);
};
static void initialize();
static void finalize();
class MD5Context {
private:
@ -89,23 +85,37 @@ public:
};
class AESContext {
public:
enum class Mode {
NONE,
ENCRYPT,
DECRYPT,
};
enum class Cipher {
NONE,
CBC,
CFB,
ECB,
};
private:
uint32_t key_id = 0;
uint32_t alg = 0;
void *ctx = nullptr;
void reset();
public:
AESContext();
~AESContext();
Error set_encode_key(const uint8_t *p_key, size_t p_bits);
Error set_decode_key(const uint8_t *p_key, size_t p_bits);
Error encrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]);
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);
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 update(const uint8_t *p_src, size_t p_src_size, uint8_t *r_dst, size_t p_dst_size);
Error finish(uint8_t *r_dst, size_t p_dst_size);
};
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 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);
@ -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 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 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 "core/crypto/crypto_core.h"
#include "core/object/class_db.h"
Error HashingContext::start(HashType p_type) {
ERR_FAIL_COND_V(ctx != nullptr, ERR_ALREADY_IN_USE);

View file

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

View file

@ -81,16 +81,20 @@ Array DebuggerMarshalls::ScriptStackVariable::serialize(int max_size) {
} else {
arr.push_back(var);
}
arr.push_back(type_hint);
return 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];
type = p_arr[1];
var_type = p_arr[2];
value = p_arr[3];
CHECK_END(p_arr, 4, "ScriptStackVariable");
type_hint = p_arr[4];
CHECK_END(p_arr, 5, "ScriptStackVariable");
return true;
}
@ -177,3 +181,27 @@ Ref<Shortcut> DebuggerMarshalls::deserialize_key_shortcut(const Array &p_keys) {
shortcut->set_events(key_events);
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;
int type = -1;
int var_type = -1;
String type_hint;
Array serialize(int max_size = 1 << 20); // 1 MiB default.
bool deserialize(const Array &p_arr);
@ -70,4 +71,6 @@ struct DebuggerMarshalls {
static Array serialize_key_shortcut(const Ref<Shortcut> &p_shortcut);
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);
ERR_FAIL_NULL_MSG(create_fn, vformat("Invalid protocol: %s.", proto));
RemoteDebuggerPeer *peer = (*create_fn)(p_uri);
if (!peer) {
Ref<RemoteDebuggerPeer> peer = (*create_fn)(p_uri);
if (peer.is_null()) {
return;
}
singleton = memnew(RemoteDebugger(Ref<RemoteDebuggerPeer>(peer)));

View file

@ -30,6 +30,7 @@
#pragma once
#include "core/object/ref_counted.h"
#include "core/string/string_name.h"
#include "core/string/ustring.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 RemoteDebuggerPeer *(*CreatePeerFunc)(const String &p_uri);
typedef Ref<RemoteDebuggerPeer> (*CreatePeerFunc)(const String &p_uri);
class Profiler {
friend class EngineDebugger;

View file

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

View file

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

View file

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

View file

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

View file

@ -30,6 +30,7 @@
#include "remote_debugger.h"
#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/debugger/debugger_marshalls.h"
#include "core/debugger/engine_debugger.h"
@ -38,6 +39,7 @@
#include "core/input/input.h"
#include "core/io/resource_loader.h"
#include "core/math/expression.h"
#include "core/object/class_db.h"
#include "core/object/script_language.h"
#include "core/os/os.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.value = F->get();
stvar.type = p_type;
stvar.type_hint = DebuggerMarshalls::parse_type_from_variant(F->get());
send_message("stack_frame_var", stvar.serialize());
E = E->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);
Input::MouseMode mouse_mode = Input::MOUSE_MODE_VISIBLE;
Input::MouseMode mouse_mode = Input::MouseMode::MOUSE_MODE_VISIBLE;
if (Thread::is_main_thread()) {
mouse_mode = Input::get_singleton()->get_mouse_mode();
if (mouse_mode != Input::MOUSE_MODE_VISIBLE) {
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
if (mouse_mode != Input::MouseMode::MOUSE_MODE_VISIBLE) {
Input::get_singleton()->set_mouse_mode(Input::MouseMode::MOUSE_MODE_VISIBLE);
}
} else {
MutexLock mutex_lock(mutex);

View file

@ -33,8 +33,6 @@
#include "core/debugger/debugger_marshalls.h"
#include "core/debugger/engine_debugger.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/variant/array.h"

View file

@ -32,6 +32,9 @@
#include "core/config/project_settings.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"
bool RemoteDebuggerPeerTCP::is_peer_connected() {
@ -126,6 +129,7 @@ void RemoteDebuggerPeerTCP::_write_out() {
void RemoteDebuggerPeerTCP::_read_in() {
while (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED && tcp_client->wait(NetSocket::POLL_TYPE_IN) == OK) {
uint8_t *buf = in_buf.ptrw();
Error err = OK;
if (in_left <= 0) {
if (in_queue.size() > max_queued_messages) {
break; // Too many messages already in queue.
@ -135,18 +139,29 @@ void RemoteDebuggerPeerTCP::_read_in() {
}
uint32_t size = 0;
int read = 0;
Error err = tcp_client->get_partial_data((uint8_t *)&size, 4, read);
ERR_CONTINUE(read != 4 || err != OK || size > (uint32_t)in_buf.size());
err = tcp_client->get_partial_data((uint8_t *)&size, 4, read);
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_pos = 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_pos += read;
if (in_left == 0) {
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_MSG(var.get_type() != Variant::ARRAY, "Malformed packet received, not an Array.");
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);
String debug_host = p_uri.replace("tcp://", "");
@ -234,7 +249,7 @@ RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create_tcp(const String &p_uri) {
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);
String debug_path = p_uri.replace("unix://", "");
@ -249,3 +264,8 @@ RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create_unix(const String &p_uri) {
RemoteDebuggerPeer::RemoteDebuggerPeer() {
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
#include "core/io/stream_peer_tcp.h"
#include "core/io/stream_peer_uds.h"
#include "core/object/ref_counted.h"
#include "core/os/mutex.h"
#include "core/os/thread.h"
#include "core/string/ustring.h"
class StreamPeerSocket;
class RemoteDebuggerPeer : public RefCounted {
GDSOFTCLASS(RemoteDebuggerPeer, RefCounted);
@ -79,11 +79,12 @@ private:
void _poll();
void _write_out();
void _read_in();
void _disconnect_with_error(const String &p_reason);
static Error _try_connect(Ref<StreamPeerSocket> p_stream);
public:
static RemoteDebuggerPeer *create_tcp(const String &p_uri);
static RemoteDebuggerPeer *create_unix(const String &p_uri);
static Ref<RemoteDebuggerPeer> create_tcp(const String &p_uri);
static Ref<RemoteDebuggerPeer> create_unix(const String &p_uri);
bool is_peer_connected() override;
int get_max_message_size() const override;

View file

@ -30,6 +30,9 @@
#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) {
const Variant::Type type = p_value.get_type();
if (type == Variant::ARRAY) {

View file

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

View file

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

View file

@ -30,12 +30,15 @@
#include "error_macros.h"
#include "core/core_globals.h"
#include "core/io/logger.h"
#include "core/object/object_id.h"
#include "core/object/script_language.h"
#include "core/os/os.h"
#include "core/string/ustring.h"
#include <cstdio>
// Optional physics interpolation warnings try to include the path to the relevant node.
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
#include "core/config/project_settings.h"
@ -104,6 +107,12 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co
// 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) {
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) {
// Fallback if we're already printing an error, to prevent infinite recursion.
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
// been printing by a preceding _err_print_error).
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) {
// Fallback if we're already printing an error, to prevent infinite recursion.

View file

@ -32,10 +32,6 @@
#include "core/typedefs.h"
#ifdef _MSC_VER
#include <intrin.h> // `__fastfail()`.
#endif
class String;
class ObjectID;
@ -95,11 +91,11 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
#define FUNCTION_STR __FUNCTION__
#endif
#ifdef _MSC_VER
#if defined(_MSC_VER) && !defined(__clang__)
/**
* 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
/**
* 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.
* If not, the current function returns.
*/
#define ERR_FAIL_INDEX(m_index, m_size) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
#define ERR_FAIL_INDEX(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)); \
return; \
} else \
return; \
} else \
((void)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.
*/
#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \
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); \
return; \
} else \
return; \
} else \
((void)0)
/**
* Same as `ERR_FAIL_INDEX_MSG` but also notifies the editor.
*/
#define ERR_FAIL_INDEX_EDMSG(m_index, m_size, m_msg) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
#define ERR_FAIL_INDEX_EDMSG(m_index, m_size, m_msg) \
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); \
return; \
} else \
return; \
} else \
((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.
* If not, the current function returns `m_retval`.
*/
#define ERR_FAIL_INDEX_V(m_index, m_size, m_retval) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
#define ERR_FAIL_INDEX_V(m_index, m_size, m_retval) \
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)); \
return m_retval; \
} else \
return m_retval; \
} else \
((void)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`.
*/
#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
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); \
return m_retval; \
} else \
return m_retval; \
} else \
((void)0)
/**
* 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) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
#define ERR_FAIL_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg) \
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); \
return m_retval; \
} else \
return m_retval; \
} else \
((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.
* If not, the application crashes.
*/
#define CRASH_BAD_INDEX(m_index, m_size) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
#define CRASH_BAD_INDEX(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_flush_stdout(); \
GENERATE_TRAP(); \
} else \
_err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((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.
* If not, prints `m_msg` and the application crashes.
*/
#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \
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_flush_stdout(); \
GENERATE_TRAP(); \
} else \
_err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((void)0)
// 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`.
* If not, the current function returns.
*/
#define ERR_FAIL_UNSIGNED_INDEX(m_index, m_size) \
if (unlikely((m_index) >= (m_size))) { \
#define ERR_FAIL_UNSIGNED_INDEX(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)); \
return; \
} else \
return; \
} else \
((void)0)
/**
* Ensures an unsigned integer index `m_index` is less than `m_size`.
* If not, prints `m_msg` and the current function returns.
*/
#define ERR_FAIL_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \
if (unlikely((m_index) >= (m_size))) { \
#define ERR_FAIL_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \
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); \
return; \
} else \
return; \
} else \
((void)0)
/**
* Same as `ERR_FAIL_UNSIGNED_INDEX_MSG` but also notifies the editor.
*/
#define ERR_FAIL_UNSIGNED_INDEX_EDMSG(m_index, m_size, m_msg) \
if (unlikely((m_index) >= (m_size))) { \
#define ERR_FAIL_UNSIGNED_INDEX_EDMSG(m_index, m_size, m_msg) \
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); \
return; \
} else \
return; \
} else \
((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`.
* If not, the current function returns `m_retval`.
*/
#define ERR_FAIL_UNSIGNED_INDEX_V(m_index, m_size, m_retval) \
if (unlikely((m_index) >= (m_size))) { \
#define ERR_FAIL_UNSIGNED_INDEX_V(m_index, m_size, m_retval) \
if (unlikely((m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
return m_retval; \
} else \
return m_retval; \
} else \
((void)0)
/**
* Ensures an unsigned integer index `m_index` is less than `m_size`.
* 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) \
if (unlikely((m_index) >= (m_size))) { \
#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
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); \
return m_retval; \
} else \
return m_retval; \
} else \
((void)0)
/**
* 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) \
if (unlikely((m_index) >= (m_size))) { \
#define ERR_FAIL_UNSIGNED_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg) \
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); \
return m_retval; \
} else \
return m_retval; \
} else \
((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`.
* If not, the application crashes.
*/
#define CRASH_BAD_UNSIGNED_INDEX(m_index, m_size) \
if (unlikely((m_index) >= (m_size))) { \
#define CRASH_BAD_UNSIGNED_INDEX(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_flush_stdout(); \
GENERATE_TRAP(); \
} else \
_err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((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`.
* If not, prints `m_msg` and the application crashes.
*/
#define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \
if (unlikely((m_index) >= (m_size))) { \
#define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \
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_flush_stdout(); \
GENERATE_TRAP(); \
} else \
_err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((void)0)
// 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.
* If it is null, the current function returns.
*/
#define ERR_FAIL_NULL(m_param) \
if (unlikely(m_param == nullptr)) { \
#define ERR_FAIL_NULL(m_param) \
if (unlikely(m_param == nullptr)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \
return; \
} else \
return; \
} else \
((void)0)
/**
* Ensures a pointer `m_param` is not null.
* If it is null, prints `m_msg` and the current function returns.
*/
#define ERR_FAIL_NULL_MSG(m_param, m_msg) \
if (unlikely(m_param == nullptr)) { \
#define ERR_FAIL_NULL_MSG(m_param, m_msg) \
if (unlikely(m_param == nullptr)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \
return; \
} else \
return; \
} else \
((void)0)
/**
* Same as `ERR_FAIL_NULL_MSG` but also notifies the editor.
*/
#define ERR_FAIL_NULL_EDMSG(m_param, m_msg) \
if (unlikely(m_param == nullptr)) { \
#define ERR_FAIL_NULL_EDMSG(m_param, m_msg) \
if (unlikely(m_param == nullptr)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \
return; \
} else \
return; \
} else \
((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.
* If it is null, the current function returns `m_retval`.
*/
#define ERR_FAIL_NULL_V(m_param, m_retval) \
if (unlikely(m_param == nullptr)) { \
#define ERR_FAIL_NULL_V(m_param, m_retval) \
if (unlikely(m_param == nullptr)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \
return m_retval; \
} else \
return m_retval; \
} else \
((void)0)
/**
* Ensures a pointer `m_param` is not null.
* 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) \
if (unlikely(m_param == nullptr)) { \
#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \
if (unlikely(m_param == nullptr)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \
return m_retval; \
} else \
return m_retval; \
} else \
((void)0)
/**
* Same as `ERR_FAIL_NULL_V_MSG` but also notifies the editor.
*/
#define ERR_FAIL_NULL_V_EDMSG(m_param, m_retval, m_msg) \
if (unlikely(m_param == nullptr)) { \
#define ERR_FAIL_NULL_V_EDMSG(m_param, m_retval, m_msg) \
if (unlikely(m_param == nullptr)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \
return m_retval; \
} else \
return m_retval; \
} else \
((void)0)
/**
@ -413,11 +409,11 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures `m_cond` is false.
* If `m_cond` is true, the current function returns.
*/
#define ERR_FAIL_COND(m_cond) \
if (unlikely(m_cond)) { \
#define ERR_FAIL_COND(m_cond) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true."); \
return; \
} else \
return; \
} else \
((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 index bounds use ERR_FAIL_INDEX_MSG instead.
*/
#define ERR_FAIL_COND_MSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
#define ERR_FAIL_COND_MSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg); \
return; \
} else \
return; \
} else \
((void)0)
/**
* Same as `ERR_FAIL_COND_MSG` but also notifies the editor.
*/
#define ERR_FAIL_COND_EDMSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
#define ERR_FAIL_COND_EDMSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg, true); \
return; \
} else \
return; \
} else \
((void)0)
/**
@ -453,11 +449,11 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures `m_cond` is false.
* If `m_cond` is true, the current function returns `m_retval`.
*/
#define ERR_FAIL_COND_V(m_cond, m_retval) \
if (unlikely(m_cond)) { \
#define ERR_FAIL_COND_V(m_cond, m_retval) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval)); \
return m_retval; \
} else \
return m_retval; \
} else \
((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 index bounds use ERR_FAIL_INDEX_V_MSG instead.
*/
#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \
if (unlikely(m_cond)) { \
#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg); \
return m_retval; \
} else \
return m_retval; \
} else \
((void)0)
/**
* Same as `ERR_FAIL_COND_V_MSG` but also notifies the editor.
*/
#define ERR_FAIL_COND_V_EDMSG(m_cond, m_retval, m_msg) \
if (unlikely(m_cond)) { \
#define ERR_FAIL_COND_V_EDMSG(m_cond, m_retval, m_msg) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg, true); \
return m_retval; \
} else \
return m_retval; \
} else \
((void)0)
/**
@ -491,32 +487,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures `m_cond` is false.
* If `m_cond` is true, the current loop continues.
*/
#define ERR_CONTINUE(m_cond) \
if (unlikely(m_cond)) { \
#define ERR_CONTINUE(m_cond) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing."); \
continue; \
} else \
continue; \
} else \
((void)0)
/**
* Ensures `m_cond` is false.
* If `m_cond` is true, prints `m_msg` and the current loop continues.
*/
#define ERR_CONTINUE_MSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
#define ERR_CONTINUE_MSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg); \
continue; \
} else \
continue; \
} else \
((void)0)
/**
* Same as `ERR_CONTINUE_MSG` but also notifies the editor.
*/
#define ERR_CONTINUE_EDMSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
#define ERR_CONTINUE_EDMSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg, true); \
continue; \
} else \
continue; \
} else \
((void)0)
/**
@ -526,32 +522,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures `m_cond` is false.
* If `m_cond` is true, the current loop breaks.
*/
#define ERR_BREAK(m_cond) \
if (unlikely(m_cond)) { \
#define ERR_BREAK(m_cond) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking."); \
break; \
} else \
break; \
} else \
((void)0)
/**
* Ensures `m_cond` is false.
* If `m_cond` is true, prints `m_msg` and the current loop breaks.
*/
#define ERR_BREAK_MSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
#define ERR_BREAK_MSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg); \
break; \
} else \
break; \
} else \
((void)0)
/**
* Same as `ERR_BREAK_MSG` but also notifies the editor.
*/
#define ERR_BREAK_EDMSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
#define ERR_BREAK_EDMSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg, true); \
break; \
} else \
break; \
} else \
((void)0)
/**
@ -562,12 +558,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures `m_cond` is false.
* If `m_cond` is true, the application crashes.
*/
#define CRASH_COND(m_cond) \
if (unlikely(m_cond)) { \
#define CRASH_COND(m_cond) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true."); \
_err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
_err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((void)0)
/**
@ -577,12 +573,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
* Ensures `m_cond` is false.
* If `m_cond` is true, prints `m_msg` and the application crashes.
*/
#define CRASH_COND_MSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
#define CRASH_COND_MSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", m_msg); \
_err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
_err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((void)0)
// Generic error macros.
@ -594,11 +590,11 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
*
* The current function returns.
*/
#define ERR_FAIL() \
if (true) { \
#define ERR_FAIL() \
if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed."); \
return; \
} else \
return; \
} else \
((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.
*/
#define ERR_FAIL_MSG(m_msg) \
if (true) { \
#define ERR_FAIL_MSG(m_msg) \
if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg); \
return; \
} else \
return; \
} else \
((void)0)
/**
* Same as `ERR_FAIL_MSG` but also notifies the editor.
*/
#define ERR_FAIL_EDMSG(m_msg) \
if (true) { \
#define ERR_FAIL_EDMSG(m_msg) \
if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg, true); \
return; \
} else \
return; \
} else \
((void)0)
/**
@ -631,11 +627,11 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
*
* The current function returns `m_retval`.
*/
#define ERR_FAIL_V(m_retval) \
if (true) { \
#define ERR_FAIL_V(m_retval) \
if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval)); \
return m_retval; \
} else \
return m_retval; \
} else \
((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`.
*/
#define ERR_FAIL_V_MSG(m_retval, m_msg) \
if (true) { \
#define ERR_FAIL_V_MSG(m_retval, m_msg) \
if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg); \
return m_retval; \
} else \
return m_retval; \
} else \
((void)0)
/**
* Same as `ERR_FAIL_V_MSG` but also notifies the editor.
*/
#define ERR_FAIL_V_EDMSG(m_retval, m_msg) \
if (true) { \
#define ERR_FAIL_V_EDMSG(m_retval, m_msg) \
if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg, true); \
return m_retval; \
} else \
return m_retval; \
} else \
((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.
*/
#define ERR_PRINT_ONCE(m_msg) \
if (true) { \
static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \
warning_shown = true; \
#define ERR_PRINT_ONCE(m_msg) \
if (true) { \
static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \
warning_shown = true; \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg); \
} \
} else \
} \
} else \
((void)0)
/**
* Same as `ERR_PRINT_ONCE` but also notifies the editor.
*/
#define ERR_PRINT_ONCE_ED(m_msg) \
if (true) { \
static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \
warning_shown = true; \
#define ERR_PRINT_ONCE_ED(m_msg) \
if (true) { \
static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \
warning_shown = true; \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true); \
} \
} else \
} \
} else \
((void)0)
// 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.
*/
#define WARN_PRINT_ONCE(m_msg) \
if (true) { \
static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \
warning_shown = true; \
#define WARN_PRINT_ONCE(m_msg) \
if (true) { \
static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \
warning_shown = true; \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, false, ERR_HANDLER_WARNING); \
} \
} else \
} \
} else \
((void)0)
/**
* Same as `WARN_PRINT_ONCE` but also notifies the editor.
*/
#define WARN_PRINT_ONCE_ED(m_msg) \
if (true) { \
static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \
warning_shown = true; \
#define WARN_PRINT_ONCE_ED(m_msg) \
if (true) { \
static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \
warning_shown = true; \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true, ERR_HANDLER_WARNING); \
} \
} else \
} \
} else \
((void)0)
/**
* 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()) { \
WARN_PRINT(m_msg); \
} \
WARN_PRINT(m_msg); \
} \
}
// 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.
*/
#define WARN_DEPRECATED \
if (true) { \
static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \
warning_shown = true; \
#define WARN_DEPRECATED \
if (true) { \
static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \
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); \
} \
} else \
} \
} else \
((void)0)
/**
* Warns that the current function is deprecated and prints `m_msg`.
*/
#define WARN_DEPRECATED_MSG(m_msg) \
if (true) { \
static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \
warning_shown = true; \
#define WARN_DEPRECATED_MSG(m_msg) \
if (true) { \
static bool warning_shown = false; \
if (unlikely(!warning_shown)) { \
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); \
} \
} else \
} \
} else \
((void)0)
/**
@ -791,12 +787,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
*
* The application crashes.
*/
#define CRASH_NOW() \
if (true) { \
#define CRASH_NOW() \
if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed."); \
_err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
_err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((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.
*/
#define CRASH_NOW_MSG(m_msg) \
if (true) { \
#define CRASH_NOW_MSG(m_msg) \
if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", m_msg); \
_err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
_err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((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.
*/
#ifdef DEV_ENABLED
#define DEV_ASSERT(m_cond) \
if (unlikely(!(m_cond))) { \
#define DEV_ASSERT(m_cond) \
if (unlikely(!(m_cond))) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: DEV_ASSERT failed \"" _STR(m_cond) "\" is false."); \
_err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
_err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((void)0)
#else
#define DEV_ASSERT(m_cond)
#endif
#ifdef DEV_ENABLED
#define DEV_CHECK_ONCE(m_cond) \
if (true) { \
static bool first_print = true; \
if (first_print && unlikely(!(m_cond))) { \
#define DEV_CHECK_ONCE(m_cond) \
if (true) { \
static bool first_print = true; \
if (first_print && unlikely(!(m_cond))) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "DEV_CHECK_ONCE failed \"" _STR(m_cond) "\" is false."); \
first_print = false; \
} \
} else \
first_print = false; \
} \
} else \
((void)0)
#else
#define DEV_CHECK_ONCE(m_cond)

View file

@ -7,7 +7,7 @@ import make_interface_dumper
import make_interface_header
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(
"gdextension_interface_dump.gen.h",
["gdextension_interface.json", "make_interface_dumper.py"],
@ -19,6 +19,4 @@ env.CommandNoCache(
env.Run(make_interface_header.run),
)
env_extension = env.Clone()
env_extension.add_source_files(env.core_sources, "*.cpp")
env.add_source_files(env.core_sources, "*.cpp")

View file

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

View file

@ -30,14 +30,17 @@
#pragma once
#include "core/extension/gdextension.h"
#ifdef TOOLS_ENABLED
#include "core/error/error_list.h"
#include "core/string/ustring.h"
#include "core/variant/dictionary.h"
class GDExtensionAPIDump {
public:
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 Error validate_extension_json_file(const String &p_path);
};
#endif

View file

@ -30,6 +30,10 @@
#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) {
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);
}
#endif
#endif // DISABLE_DEPRECATED

View file

@ -32,10 +32,10 @@
#include "gdextension.compat.inc"
#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/method_bind.h"
#include "gdextension_library_loader.h"
#include "gdextension_manager.h"
extern void gdextension_setup_interface();
extern GDExtensionInterfaceFunctionPtr gdextension_get_proc_address(const char *p_name);
@ -240,7 +240,7 @@ public:
#ifndef DISABLE_DEPRECATED
void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs) {
const GDExtensionClassCreationInfo5 class_info5 = {
const GDExtensionClassCreationInfo6 class_info6 = {
p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
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->reference_func, // GDExtensionClassReference reference_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 */
nullptr, // GDExtensionClassRecreateInstance recreate_instance_func;
nullptr, // GDExtensionClassGetVirtual get_virtual_func;
nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
nullptr, // GDExtensionClassCallVirtualWithData call_virtual_func;
nullptr, // GDExtensionClassGetVirtual2 get_virtual_func;
nullptr, // GDExtensionClassGetVirtualCallData2 get_virtual_call_data_func;
nullptr, // GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
p_extension_funcs->class_userdata, // void *class_userdata;
};
const ClassCreationDeprecatedInfo legacy = {
false,
false, // bool legacy_unexposed_class;
p_extension_funcs->notification_func, // GDExtensionClassNotification notification_func;
p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func;
p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
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;
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) {
const GDExtensionClassCreationInfo5 class_info5 = {
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;
@ -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->reference_func, // GDExtensionClassReference reference_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->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func;
nullptr, // GDExtensionClassGetVirtual get_virtual_func;
nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func;
nullptr, // GDExtensionClassGetVirtual2 get_virtual_func;
nullptr, // 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;
};
@ -310,15 +311,16 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar
nullptr, // GDExtensionClassNotification notification_func;
p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func;
p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
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_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) {
const GDExtensionClassCreationInfo5 class_info5 = {
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;
@ -327,7 +329,7 @@ void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_librar
p_extension_funcs->set_func, // GDExtensionClassSet set_func;
p_extension_funcs->get_func, // GDExtensionClassGet get_func;
p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
p_extension_funcs->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;
@ -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->reference_func, // GDExtensionClassReference reference_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->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func;
nullptr, // GDExtensionClassGetVirtual get_virtual_func;
nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func;
nullptr, // GDExtensionClassGetVirtual2 get_virtual_func;
nullptr, // 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;
};
@ -348,34 +350,101 @@ void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_librar
!p_extension_funcs->is_exposed, // bool legacy_unexposed_class;
nullptr, // GDExtensionClassNotification notification_func;
nullptr, // GDExtensionClassFreePropertyList free_property_list_func;
p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance2 create_instance_func;
p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func;
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_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) {
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 = {
!p_extension_funcs->is_exposed, // bool legacy_unexposed_class;
nullptr, // GDExtensionClassNotification notification_func;
nullptr, // GDExtensionClassFreePropertyList free_property_list_func;
nullptr, // GDExtensionClassCreateInstance2 create_instance_func;
nullptr, // GDExtensionClassGetRID get_rid;
nullptr, // GDExtensionClassGetVirtual get_virtual_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_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
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);
}
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);
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.free_property_list = p_deprecated_funcs->free_property_list_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_virtual = p_deprecated_funcs->get_virtual_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.unreference = p_extension_funcs->unreference_func;
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.recreate_instance = p_extension_funcs->recreate_instance_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_class3", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class3);
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);
#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_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);
@ -844,68 +915,7 @@ void GDExtension::finalize_gdextensions() {
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
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 {
return loader->has_library_changed();
@ -950,7 +960,7 @@ void GDExtension::prepare_reload() {
state.push_back(Pair<String, Variant>(P.name, value));
}
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;
};
}

View file

@ -32,9 +32,7 @@
#include "core/extension/gdextension_interface.gen.h"
#include "core/extension/gdextension_loader.h"
#include "core/io/config_file.h"
#include "core/io/resource_loader.h"
#include "core/object/ref_counted.h"
#include "core/io/resource.h"
class GDExtensionMethodBind;
@ -71,6 +69,7 @@ class GDExtension : public Resource {
GDExtensionClassNotification notification_func = nullptr;
GDExtensionClassFreePropertyList free_property_list_func = nullptr;
GDExtensionClassCreateInstance create_instance_func = nullptr;
GDExtensionClassCreateInstance2 create_instance2_func = nullptr;
GDExtensionClassGetRID get_rid_func = nullptr;
GDExtensionClassGetVirtual get_virtual_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_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);
#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_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_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);
@ -182,21 +182,6 @@ public:
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
class GDExtensionEditorPlugins {
private:

View file

@ -30,7 +30,7 @@
#include "gdextension_function_loader.h"
#include "gdextension.h"
#include "core/extension/gdextension.h"
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://");

View file

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

View file

@ -77,7 +77,7 @@ class CallableCustomExtension : public CallableCustom {
const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);
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;
}
@ -547,6 +547,11 @@ static void gdextension_variant_get_type_name(GDExtensionVariantType p_type, GDE
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) {
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;
}
#ifndef DISABLE_DEPRECATED
static GDExtensionObjectPtr gdextension_object_cast_to(GDExtensionConstObjectPtr p_object, void *p_class_tag) {
if (!p_object) {
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;
}
#endif
static GDObjectInstanceID gdextension_object_get_instance_id(GDExtensionConstObjectPtr 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);
return (GDExtensionObjectPtr)ClassDB::instantiate_no_placeholders(classname);
}
#endif
static GDExtensionObjectPtr gdextension_classdb_construct_object2(GDExtensionConstStringNamePtr p_classname) {
const StringName classname = *reinterpret_cast<const StringName *>(p_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) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(classname);
return class_info ? class_info->class_ptr : nullptr;
}
#endif
static void gdextension_editor_add_plugin(GDExtensionConstStringNamePtr p_classname) {
#ifdef TOOLS_ENABLED
@ -1743,6 +1757,7 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(variant_has_key);
REGISTER_INTERFACE_FUNC(variant_get_object_instance_id);
REGISTER_INTERFACE_FUNC(variant_get_type_name);
REGISTER_INTERFACE_FUNC(variant_get_type_by_name);
REGISTER_INTERFACE_FUNC(variant_can_convert);
REGISTER_INTERFACE_FUNC(variant_can_convert_strict);
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_set_instance);
REGISTER_INTERFACE_FUNC(object_get_class_name);
#ifndef DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(object_cast_to);
#endif // DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(object_get_instance_from_id);
REGISTER_INTERFACE_FUNC(object_get_instance_id);
REGISTER_INTERFACE_FUNC(object_has_script_method);
@ -1856,10 +1873,13 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(callable_custom_get_userdata);
#ifndef DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(classdb_construct_object);
#endif // DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(classdb_construct_object2);
#endif // DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(classdb_construct_object3);
REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
#ifndef DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(classdb_get_class_tag);
#endif // DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(editor_add_plugin);
REGISTER_INTERFACE_FUNC(editor_remove_plugin);
REGISTER_INTERFACE_FUNC(editor_help_load_xml_from_utf8_chars);

View file

@ -986,7 +986,11 @@
"name": "p_list",
"type": "const GDExtensionPropertyInfo*"
}
]
],
"deprecated": {
"since": "4.3",
"replace_with": "GDExtensionClassFreePropertyList2"
}
},
{
"name": "GDExtensionClassFreePropertyList2",
@ -1164,7 +1168,15 @@
"name": "p_class_userdata",
"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",
@ -1181,6 +1193,35 @@
"name": "p_notify_postinitialize",
"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",
"type": "GDExtensionConstStringNamePtr"
}
]
],
"deprecated": {
"since": "4.4",
"replace_with": "GDExtensionClassGetVirtual2"
}
},
{
"name": "GDExtensionClassGetVirtual2",
@ -1267,7 +1312,11 @@
"name": "p_name",
"type": "GDExtensionConstStringNamePtr"
}
]
],
"deprecated": {
"since": "4.4",
"replace_with": "GDExtensionClassGetVirtualCallData2"
}
},
{
"name": "GDExtensionClassGetVirtualCallData2",
@ -1372,7 +1421,7 @@
"name": "create_instance_func",
"type": "GDExtensionClassCreateInstance",
"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": {
"since": "4.2",
"replace_with": "GDExtensionClassCreationInfo4"
"replace_with": "GDExtensionClassCreationInfo6"
}
},
{
@ -1470,7 +1519,7 @@
"name": "create_instance_func",
"type": "GDExtensionClassCreateInstance",
"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": {
"since": "4.3",
"replace_with": "GDExtensionClassCreationInfo4"
"replace_with": "GDExtensionClassCreationInfo6"
}
},
{
@ -1595,7 +1644,7 @@
"name": "create_instance_func",
"type": "GDExtensionClassCreateInstance",
"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": {
"since": "4.4",
"replace_with": "GDExtensionClassCreationInfo4"
"replace_with": "GDExtensionClassCreationInfo6"
}
},
{
@ -1724,7 +1773,141 @@
"name": "create_instance_func",
"type": "GDExtensionClassCreateInstance2",
"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",
"kind": "handle"
@ -3375,7 +3553,11 @@
"name": "string",
"type": "const char*"
}
]
],
"deprecated": {
"since": "4.5",
"replace_with": "GDExtensionGodotVersion2"
}
},
{
"name": "GDExtensionGodotVersion2",
@ -4978,6 +5160,28 @@
],
"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",
"return_value": {
@ -5859,7 +6063,7 @@
"return_value": {
"type": "GDExtensionInt",
"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": [
@ -5896,7 +6100,7 @@
"return_value": {
"type": "GDExtensionInt",
"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": [
@ -5933,7 +6137,7 @@
"return_value": {
"type": "GDExtensionInt",
"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": [
@ -6007,7 +6211,7 @@
"return_value": {
"type": "GDExtensionInt",
"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": [
@ -7746,7 +7950,11 @@
"description": [
"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",
@ -8242,7 +8450,7 @@
"since": "4.1",
"deprecated": {
"since": "4.4",
"replace_with": "classdb_construct_object2"
"replace_with": "classdb_construct_object3"
}
},
{
@ -8268,7 +8476,37 @@
"",
"\"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",
@ -8326,7 +8564,11 @@
"description": [
"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",
@ -8367,7 +8609,7 @@
"since": "4.1",
"deprecated": {
"since": "4.2",
"replace_with": "classdb_register_extension_class5"
"replace_with": "classdb_register_extension_class6"
}
},
{
@ -8409,7 +8651,7 @@
"since": "4.2",
"deprecated": {
"since": "4.3",
"replace_with": "classdb_register_extension_class5"
"replace_with": "classdb_register_extension_class6"
}
},
{
@ -8451,7 +8693,7 @@
"since": "4.3",
"deprecated": {
"since": "4.4",
"replace_with": "classdb_register_extension_class5"
"replace_with": "classdb_register_extension_class6"
}
},
{
@ -8493,7 +8735,7 @@
"since": "4.4",
"deprecated": {
"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.",
"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",

View file

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

View file

@ -30,10 +30,12 @@
#include "gdextension_library_loader.h"
#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/extension/gdextension.h"
#include "core/io/dir_access.h"
#include "core/os/os.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> 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.
err = OS::get_singleton()->open_dynamic_library(abs_path, library, &data);
if (err != OK) {
#ifdef APPLE_EMBEDDED_ENABLED
err = OS::get_singleton()->open_dynamic_library(String(), library, &data);
#endif
if (err != OK) {
return err;
}

View file

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

View file

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

View file

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

View file

@ -30,7 +30,7 @@
#pragma once
#include "gdextension_interface.gen.h"
#include "core/extension/gdextension_interface.gen.h"
#ifdef __cplusplus
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_uncompressed_size = {decomp_size};
inline constexpr unsigned char _gdextension_interface_data_compressed[] = {{
{methods.format_buffer(buffer, 1)}
{methods.format_buffer(buffer, 1)}
}};
class GDExtensionInterfaceDump {{

View file

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

View file

@ -31,15 +31,21 @@
#include "input.h"
#include "input.compat.inc"
#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/input/default_controller_mappings.h"
#include "core/input/input_map.h"
#include "core/object/class_db.h"
#include "core/os/os.h"
#ifdef DEV_ENABLED
#include "core/os/thread.h"
#endif
#include <thirdparty/gamepadmotionhelpers/GamepadMotion.hpp>
#define STANDARD_GRAVITY 9.80665f
static const char *_joy_buttons[(size_t)JoyButton::SDL_MAX] = {
"a",
"b",
@ -62,6 +68,11 @@ static const char *_joy_buttons[(size_t)JoyButton::SDL_MAX] = {
"paddle3",
"paddle4",
"touchpad",
"misc2",
"misc3",
"misc4",
"misc5",
"misc6",
};
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() {
using namespace InputClassEnums;
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_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_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_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("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("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_accelerometer"), &Input::get_accelerometer);
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_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_accelerometer", "value"), &Input::set_accelerometer);
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, "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, "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_HIDDEN);
@ -296,24 +333,14 @@ bool Input::is_anything_pressed() const {
return false;
}
bool Input::is_anything_pressed_except_mouse() const {
bool Input::is_any_key_pressed() const {
_THREAD_SAFE_METHOD_
if (disable_input) {
return false;
}
if (!keys_pressed.is_empty() || !joy_buttons_pressed.is_empty()) {
return true;
}
for (const KeyValue<StringName, Input::ActionState> &E : action_states) {
if (E.value.cache.pressed) {
return true;
}
}
return false;
return !keys_pressed.is_empty();
}
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));
}
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) {
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 const char *dict = "0123456789abcdef";
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++) {
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;
@ -739,6 +802,64 @@ Vector3 Input::get_gyroscope() const {
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) {
// 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.
@ -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.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.event_type[event_index] = p_event->get_type();
// Update the action's global state and cache.
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) {
_THREAD_SAFE_METHOD_
if (_should_ignore_joypad_events()) {
return;
}
Joypad *joypad = joy_names.getptr(p_device);
if (!joypad || !joypad->has_light || joypad->features == nullptr) {
return;
@ -1018,8 +1146,218 @@ bool Input::has_joy_light(int p_device) const {
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) {
_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) {
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);
}
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) {
_THREAD_SAFE_METHOD_
@ -1311,17 +1660,52 @@ bool Input::is_using_accumulated_input() {
}
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
keys_pressed.clear();
physical_keys_pressed.clear();
key_label_pressed.clear();
joy_buttons_pressed.clear();
_joy_axis.clear();
if (ignore_joypad_on_unfocused_application) {
joy_buttons_pressed.clear();
_joy_axis.clear();
for (KeyValue<StringName, Input::ActionState> &E : action_states) {
if (E.value.cache.pressed) {
action_release(E.key);
for (KeyValue<StringName, Input::ActionState> &E : action_states) {
if (E.value.cache.pressed) {
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) {
_THREAD_SAFE_METHOD_;
if (_should_ignore_joypad_events()) {
return;
}
Joypad &joy = joy_names[p_device];
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) {
_THREAD_SAFE_METHOD_;
if (_should_ignore_joypad_events()) {
return;
}
ERR_FAIL_INDEX((int)p_axis, (int)JoyAxis::MAX);
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) {
_THREAD_SAFE_METHOD_;
if (_should_ignore_joypad_events()) {
return;
}
const Joypad &joy = joy_names[p_device];
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;
}
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) {
Ref<InputEventJoypadButton> ievent;
ievent.instantiate();
@ -1517,9 +1952,25 @@ void Input::_update_joypad_features(int p_device) {
if (!joypad || joypad->features == nullptr) {
return;
}
if (joypad->features->has_joy_vibration()) {
joypad->has_vibration = true;
}
if (joypad->features->has_joy_light()) {
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) {
@ -2004,6 +2455,7 @@ Input::Input() {
gravity_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gravity", false);
gyroscope_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gyroscope", false);
magnetometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_magnetometer", false);
ignore_joypad_on_unfocused_application = GLOBAL_DEF_RST_BASIC("input_devices/joypads/ignore_joypad_on_unfocused_application", false);
}
Input::~Input() {

View file

@ -38,6 +38,43 @@
#include "core/templates/rb_set.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 {
GDCLASS(Input, Object);
_THREAD_SAFE_CLASS_
@ -47,44 +84,23 @@ class Input : public Object {
static constexpr uint64_t MAX_EVENT = 32;
public:
// Keep synced with "DisplayServer::MouseMode" enum.
enum MouseMode {
MOUSE_MODE_VISIBLE,
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
};
// TODO: When we migrate to C++20, replace these with "using enum" and skip prefixing MouseMode and CursorShape in other files.
using MouseMode = InputClassEnums::MouseMode;
using CursorShape = InputClassEnums::CursorShape;
class JoypadFeatures {
public:
virtual ~JoypadFeatures() {}
virtual bool has_joy_vibration() const { return false; }
virtual bool has_joy_light() const { return false; }
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;
@ -112,6 +128,9 @@ private:
int64_t mouse_window = 0;
bool legacy_just_pressed_behavior = false;
bool disable_input = false;
bool ignore_joypad_on_unfocused_application = false;
bool application_focused = true;
bool embedder_focused = false;
struct ActionState {
uint64_t pressed_physics_frame = UINT64_MAX;
@ -126,6 +145,7 @@ private:
bool pressed[MAX_EVENT] = { false };
float strength[MAX_EVENT] = { 0.0 };
float raw_strength[MAX_EVENT] = { 0.0 };
InputEventType event_type[MAX_EVENT] = { InputEventType::INVALID };
};
bool api_pressed = false;
float api_strength = 0.0;
@ -157,6 +177,35 @@ private:
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 {
uint64_t last_tick = 0;
Vector2 velocity;
@ -184,6 +233,7 @@ private:
int hat_current = 0;
Dictionary info;
bool has_light = false;
bool has_vibration = false;
Input::JoypadFeatures *features = nullptr;
};
@ -195,7 +245,7 @@ private:
int fallback_mapping = -1; // Index of the guid in map_db.
CursorShape default_shape = CURSOR_ARROW;
CursorShape default_shape = CursorShape::CURSOR_ARROW;
enum JoyType {
TYPE_BUTTON,
@ -275,6 +325,8 @@ private:
#endif
friend class DisplayServer;
friend class SceneTree;
friend class SceneDebugger;
static void (*set_mouse_mode_func)(MouseMode);
static MouseMode (*get_mouse_mode_func)();
@ -289,6 +341,8 @@ private:
EventDispatchFunc event_dispatch_function = nullptr;
bool _should_ignore_joypad_events() const;
#ifndef DISABLE_DEPRECATED
void _vibrate_handheld_bind_compat_91143(int p_duration_ms = 500);
static void _bind_compatibility_methods();
@ -312,7 +366,7 @@ public:
static Input *get_singleton();
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_physical_key_pressed(Key p_keycode) const;
bool is_key_label_pressed(Key p_keycode) const;
@ -334,7 +388,10 @@ public:
TypedArray<int> get_connected_joypads();
Vector2 get_joy_vibration_strength(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);
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());
Vector3 get_gravity() const;
@ -342,6 +399,8 @@ public:
Vector3 get_magnetometer() const;
Vector3 get_gyroscope() const;
int get_joy_num_touchpads(int p_device) const;
Point2 get_mouse_position() const;
Vector2 get_last_mouse_velocity();
Vector2 get_last_mouse_screen_velocity();
@ -363,10 +422,39 @@ public:
void set_joy_light(int p_device, const Color &p_color);
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 stop_joy_vibration(int p_device);
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 action_press(const StringName &p_action, float p_strength = 1.f);
@ -382,12 +470,14 @@ public:
CursorShape get_default_cursor_shape() const;
void set_default_cursor_shape(CursorShape p_shape);
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 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_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 remove_joy_mapping(const String &p_guid);

View file

@ -100,7 +100,12 @@ enum class JoyButton {
PADDLE3 = 18,
PADDLE4 = 19,
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.
};

View file

@ -32,8 +32,11 @@
#include "core/input/input_map.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/os.h"
#include "core/string/ustring.h"
void InputEvent::set_device(int p_device) {
device = p_device;
@ -131,6 +134,8 @@ void InputEvent::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "device"), "set_device", "get_device");
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") {
p_property.usage ^= PROPERTY_USAGE_STORAGE;
p_property.usage ^= PROPERTY_USAGE_EDITOR;
}
if (p_property.name == "ctrl_pressed") {
} else if (p_property.name == "ctrl_pressed") {
p_property.usage ^= PROPERTY_USAGE_STORAGE;
p_property.usage ^= PROPERTY_USAGE_EDITOR;
}
@ -662,6 +666,10 @@ void InputEventKey::_bind_methods() {
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) {
@ -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");
}
InputEventMouse::InputEventMouse() {
set_device(DEVICE_ID_MOUSE);
}
///////////////////////////////////
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("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 {

View file

@ -32,9 +32,7 @@
#include "core/input/input_enums.h"
#include "core/io/resource.h"
#include "core/math/transform_2d.h"
#include "core/os/keyboard.h"
#include "core/string/ustring.h"
#include "core/typedefs.h"
/**
@ -43,6 +41,8 @@
*/
class Shortcut;
class String;
struct Transform2D;
/**
* Input Modifier Status
@ -63,6 +63,8 @@ protected:
public:
static constexpr int DEVICE_ID_EMULATION = -1;
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);
int get_device() const;
@ -199,6 +201,8 @@ public:
static Ref<InputEventKey> create_reference(Key p_keycode_with_modifier_masks, bool p_physical = false);
InputEventType get_type() const final override { return InputEventType::KEY; }
InputEventKey();
};
class InputEventMouse : public InputEventWithModifiers {
@ -221,6 +225,8 @@ public:
void set_global_position(const Vector2 &p_global_pos);
Vector2 get_global_position() const;
InputEventMouse();
};
class InputEventMouseButton : public InputEventMouse {

View file

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

View file

@ -30,6 +30,10 @@
#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) {
add_action(p_action, p_deadzone);
}

View file

@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "core/input/input.h"
#include "core/object/class_db.h"
#include "core/os/keyboard.h"
#include "core/os/os.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.
}
// 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);
}
@ -319,6 +336,11 @@ void InputMap::load_from_project_settings() {
String name = pi.name.substr(pi.name.find_char('/') + 1);
Dictionary action = GLOBAL_GET(pi.name);
if (!action.has("events")) {
continue;
}
float deadzone = action.has("deadzone") ? (float)action["deadzone"] : DEFAULT_DEADZONE;
Array events = action["events"];
@ -411,6 +433,7 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = {
{ "ui_unicode_start", TTRC("Start Unicode Character Input") },
{ "ui_colorpicker_delete_preset", TTRC("ColorPicker: Delete Preset") },
{ "ui_accessibility_drag_and_drop", TTRC("Accessibility: Keyboard Drag and Drop") },
{ "ui_toggle_fullscreen", TTRC("Toggle Fullscreen") },
{ "", ""}
/* clang-format on */
};
@ -559,6 +582,8 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(Key::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);
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));
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;
}
@ -904,7 +939,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with_featur
}
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) {
String name = E.key;

View file

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

View file

@ -30,10 +30,9 @@
#include "compression.h"
#include "core/config/project_settings.h"
#include "core/io/zip_io.h"
#include "thirdparty/misc/fastlz.h"
#include <thirdparty/misc/fastlz.h>
#include <zstd.h>
@ -41,11 +40,34 @@
#include <brotli/decode.h>
#endif
// Caches for zstd.
static BinaryMutex mutex;
static ZSTD_DCtx *current_zstd_d_ctx = nullptr;
static bool current_zstd_long_distance_matching;
static int current_zstd_window_log_size;
namespace {
struct ZstdDecompressorContext {
ZSTD_DCtx *zstd_d_ctx = nullptr;
bool zstd_long_distance_matching = false;
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) {
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;
} break;
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) {
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);
size_t ret = ZSTD_decompressDCtx(decompressor_ctx.zstd_d_ctx, p_dst, p_dst_max_size, p_src, p_src_size);
return (int64_t)ret;
} break;
}
@ -292,7 +302,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int64_t p_max_d
#endif
} else {
// 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;
z_stream strm;

View file

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

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