Compare commits

...

91 Commits

Author SHA1 Message Date
David Snopek
4b0ee13327 gdextension: Sync with upstream commit fe0e8e55752b0c2e64997025717b491703e0f8ad (4.1.4-stable) 2024-04-17 13:02:32 -05:00
David Snopek
e4978558e6 Merge pull request #1441 from dsnopek/4.1-cherrypicks-9
Cherry-picks for the godot-cpp 4.1 branch - 9th batch
2024-04-17 12:58:30 -05:00
Chris Cranford
30ebe5fdf9 Fix PropertyInfo to use hint/usage default constants
(cherry picked from commit e160966163)
2024-04-17 11:01:33 -05:00
thimenesup
e897dbe58a Fix Projection create_orthogonal being incorrect
Title

(cherry picked from commit e4ae69f607)
2024-04-17 11:00:56 -05:00
Thaddeus Crews
28a6609c0b Implement verbose toggle from godot repo
(cherry picked from commit b05c21bb1d)
2024-04-17 10:58:41 -05:00
dependabot[bot]
7f3e725a8a Bump mymindstorm/setup-emsdk from 13 to 14
Bumps [mymindstorm/setup-emsdk](https://github.com/mymindstorm/setup-emsdk) from 13 to 14.
- [Release notes](https://github.com/mymindstorm/setup-emsdk/releases)
- [Commits](https://github.com/mymindstorm/setup-emsdk/compare/v13...v14)

---
updated-dependencies:
- dependency-name: mymindstorm/setup-emsdk
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 32ca574f49)
2024-04-17 10:58:31 -05:00
Thaddeus Crews
8e5d7c9268 Use GDREGISTER defines in example
(cherry picked from commit a537b4af4d)
2024-04-17 10:58:16 -05:00
David Snopek
974e6c6f86 Merge pull request #1411 from dsnopek/4.1-cherrypicks-8
Cherry-picks for the godot-cpp 4.1 branch - 8th batch
2024-04-08 13:09:01 -05:00
A Thousand Ships
c8fa4c0fd0 Fix incorrect utility call signature
(cherry picked from commit d055b575fb)
2024-04-08 11:40:05 -05:00
ytnuf
594a93f8ac Change cmake_minimum_required to match actual requirements
This is because target_link_options was added in v3.13
So this wouldn't build with cmake v3.12

Likewise in CMAKE_CXX_STANDARD only supports value of 17 starting with
cmake v3.9
So the test wouldn't build properly with cmake v3.6

(cherry picked from commit 5c12bd2287)
2024-04-08 11:39:53 -05:00
Thaddeus Crews
9e48c45bfc Enforce template syntax typename over class
(cherry picked from commit 87f5fb0691)
2024-04-08 11:39:21 -05:00
David Snopek
76d6ce7136 Avoid creating most objects that Godot is going to use placement new to initialize
(cherry picked from commit c4fde852e6)
2024-04-08 11:30:23 -05:00
bruvzg
e99d7b3b7e [Packed*Array] Add support for initializer lists.
(cherry picked from commit 8c98a90f32)
2024-04-08 11:29:39 -05:00
A Thousand Ships
07e245e3e4 Fix invalid void return in BitField
(cherry picked from commit 7ed8ef7221)
2024-03-11 13:33:57 -05:00
Marc Gilleron
ed576f8318 Fix explicit namespaces in macros
(cherry picked from commit e607790647)
2024-03-11 13:33:44 -05:00
David Snopek
f7a9d32f32 Fix _notification with parent and child classes
(cherry picked from commit 23c010900c)
2024-03-11 13:31:19 -05:00
bruvzg
08da55cd0b [Core] Improve CowData and Memory metadata alignment.
(cherry picked from commit b173a4d935)
2024-03-11 13:11:26 -05:00
Fabio Alessandrelli
670c4d0eac [SCons] Split targets.py, apply flags from tools
Split `targets` tool logic, moving all the compiler-specific flags to a
new `common_compiler_flags.py` file, and everything else (CPPDEFINES,
optimize option logic, dev build logic, etc) to the `godotcpp` tool.

The default tools now apply the common compiler flags by importing the
file and explicitly calling `configure`.

(cherry picked from commit 16df4bff30)
2024-03-11 13:06:57 -05:00
Fabio Alessandrelli
bab62a4d72 [SCons] Add support for custom build tools and platforms
Use with:

`scons platform=os2 custom_tools=/path/to/tools`

(assuming you have an `os2.py` inside `/path/to/tools/`)

(cherry picked from commit baaad7ada2)
2024-03-11 13:01:23 -05:00
DaylilyZeleen
1ac8627b2e Fix object return value of builtin types' methods.
(cherry picked from commit 6a3753c076)
2024-03-11 12:59:32 -05:00
bruvzg
6202bf141e Switch to 64-bit ints.
(cherry picked from commit 59a5a8b104)
2024-03-11 12:59:19 -05:00
David Snopek
4b63d795e4 Merge pull request #1373 from dsnopek/4.1-cherrypicks-7
Cherry-picks for the godot-cpp 4.1 branch - 7th batch
2024-02-16 09:37:17 -06:00
MJacred
2cc967787a Update README: fix godot-cpp issue tracker url
(cherry picked from commit 8a535d0ecc)
2024-01-24 08:44:28 -06:00
nightblade9
6884ca9be0 Update README.md with basic pre-requisites
(cherry picked from commit ee169b201b)
2024-01-22 15:53:49 -06:00
Daylily-Zeleen
dde0bbb93d Remove "godot" namespace when binding global constants.
(cherry picked from commit bd40a94424)
2024-01-22 15:53:49 -06:00
A Thousand Ships
1c03aa7746 Add missing OP_POWER operator to Variant
(cherry picked from commit f037a697eb)
2024-01-22 15:53:49 -06:00
ArchLinus
82475b215b Add an error message if android NDK is not installed
(cherry picked from commit 718d0baea3)
2024-01-22 15:53:49 -06:00
Aaron Franke
49098fbdc7 Allow detecting when building as a GDExtension
(cherry picked from commit e17c7bf530)
2024-01-22 15:53:49 -06:00
dependabot[bot]
756190705e Bump actions/setup-python from 4 to 5
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit a7becb43e6)
2024-01-22 15:53:49 -06:00
Rémi Verschelde
2e42c7020e CMake: Remove hardcoded warnings list and forcing -Werror on library builds
The CMake buildsystem should be completely reviewed to properly match
what is done by SCons, instead of making its own arbitrary decisions on
how godot-cpp should be compiled.

Currently the SCons setup doesn't include warning options, so CMake
shouldn't either. Options similar to upstream Godot's SCons setup could
be added, and then replicated for CMake.

(cherry picked from commit 41517eacb1)
2024-01-22 15:53:49 -06:00
David Snopek
e9273e8528 Avoid error from -Werror=type-limits on GCC 11
(cherry picked from commit cad5be53b1)
2024-01-22 15:53:49 -06:00
David Snopek
b1bd58d7da Send NOTIFICATION_POSTINITIALIZE to extension classes
(cherry picked from commit 20c4e843b0)
2024-01-22 15:53:49 -06:00
LAK132
d5a2e8e797 Fix file list issues when trying to build with meson via cmake
(cherry picked from commit 39c139c814)
2024-01-22 15:53:49 -06:00
Bytzo
6bb4b1d321 Prevent CMake from always including debug symbols
(cherry picked from commit db884e9b1d)
2024-01-22 15:53:49 -06:00
DmitriySalnikov
51aeda7437 [Scons] Set the minimum Android API level to 21
(cherry picked from commit 79d2a9c456)
2024-01-22 15:53:49 -06:00
dependabot[bot]
cd904155a8 Bump mymindstorm/setup-emsdk from 12 to 13
Bumps [mymindstorm/setup-emsdk](https://github.com/mymindstorm/setup-emsdk) from 12 to 13.
- [Release notes](https://github.com/mymindstorm/setup-emsdk/releases)
- [Commits](https://github.com/mymindstorm/setup-emsdk/compare/v12...v13)

---
updated-dependencies:
- dependency-name: mymindstorm/setup-emsdk
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 78bf5a42ed)
2024-01-22 15:53:49 -06:00
bruvzg
b622b11df3 [iOS] Fix initialisation/termination of multiple statically linked extensions.
(cherry picked from commit adc9def046)
2024-01-22 15:53:49 -06:00
Rémi Verschelde
92449b46e1 CI: Install Android NDK r23c explicitly
It has just been removed from the Ubuntu 20.04 default install,
breaking our CI setup.

Also, sets Emscripten version to 3.1.39, as done upstream.
Newer versions actually break dynamic library support.

(cherry picked from commit eea33b4133)
2024-01-22 15:53:49 -06:00
Thaddeus Crews
e8b6887b36 Add missing int→Variant conversions
(cherry picked from commit bcac96c8c2)
2024-01-22 15:53:49 -06:00
Rémi Verschelde
631cd5fe37 Merge pull request #1306 from dsnopek/4.1-cherrypicks-6
Cherry-picks for the godot-cpp 4.1 branch - 6th batch
2023-11-13 20:28:26 +01:00
Alex Drozd
731a10a4ea ignoring venv in .gitignore
(cherry picked from commit 92dd34ae96)
2023-11-13 13:00:24 -06:00
Thaddeus Crews
a1ae58448c fix is_msvc and use_hot_reload variables
(cherry picked from commit 648b8c4489)
2023-11-13 13:00:24 -06:00
Thaddeus Crews
805cdde0b7 GDCLASS synced by ending with "private:"
• Matches implementation used by modules and godot itself
• Apply same to GDEXTENSION_CLASS, setup with same diff-friendly spacers as GDCLASS

(cherry picked from commit 6eb5d450bd)
2023-11-13 13:00:24 -06:00
Fredia Huya-Kouadio
29335d8f5c Update the environment variables used to access the Android NDK toolchain
(cherry picked from commit 86dbd5fa0d)
2023-11-13 13:00:24 -06:00
Rémi Verschelde
c5f47b2a4e CI: Workaround upstream issue with .NET editor build not exiting
We force closing the process after 10 s, which should be ample time to generate
the .godot folder.

(cherry picked from commit 306774b5a4)
2023-11-13 19:15:07 +01:00
Rémi Verschelde
df5b1a9a69 gdextension: Sync with upstream commit fc79201851a16215f9554884aa242ed957801b10 (4.1.3-stable) 2023-11-09 13:25:22 +01:00
Rémi Verschelde
04b34077d8 Merge pull request #1281 from dsnopek/4.1-cherrypicks-5
Cherry-picks for the godot-cpp 4.1 branch - 5th batch
2023-10-24 11:38:55 +02:00
David Snopek
9d813310bb Add protections against registering classes that didn't use GDCLASS()
(cherry picked from commit a61cdc8860)
2023-10-23 10:11:04 -05:00
Rémi Verschelde
ef8a499eac SCons: Disable C++ exception handling by default
Counterpart to https://github.com/godotengine/godot/pull/80612.

(cherry picked from commit bf1c03ab5f)
2023-10-23 10:10:59 -05:00
gilzoide
698da13d66 Fix return value and r_valid value in Variant::iter_init and iter_next
(cherry picked from commit 60dfa3445a)
2023-10-22 14:47:10 -05:00
Adam Scott
8295486fdb Refactor compiledb implementation
This comment enables the possibility to build the "compile_commands.json"
file by only using `scons -Q compiledb`. No need to use the argument
`compiledb=yes`.

And when using the `compiledb=yes`, it will create a
"compiled_commands.json" automatically.

(cherry picked from commit 2d5024ac8e)
2023-10-22 14:46:58 -05:00
Thaddeus Crews
7704a9d054 Let gdextension_dir function as only argument
(cherry picked from commit 7a5cbcac21)
2023-10-22 14:46:48 -05:00
David Snopek
f7ffc4fe4d Automatically register only engine classes whose header has been included
(cherry picked from commit b507b3e591)
2023-10-22 14:46:26 -05:00
Fabio Alessandrelli
62cb5eac47 [SCons] Rename javascript tool to web
And clean it up a bit.

(cherry picked from commit 18bfa133ab)
2023-10-22 14:03:20 -05:00
Mikael Hermansson
03ea717742 Declare explicit specializations for CharStringT
(cherry picked from commit 6e05b978b8)
2023-10-22 14:03:10 -05:00
Rémi Verschelde
e389f7a50c Merge pull request #1261 from dsnopek/4.1-cherrypicks-4
Cherry-picks for the godot-cpp 4.1 branch - 4th batch
2023-10-12 18:08:12 +02:00
Nick Maltbie
0b1c8bcac3 Added fix for javascript build for godot 4.x
Added changes to tools/javascript.py to add PFlags to fix SharedArrayBuffer memory error.
Corrected some small errors in tools/javascript.py to support new target names.
Also updated ci to include validation for web build.

(cherry picked from commit 2b4bcbb0ce)
2023-10-09 08:43:36 -05:00
Matthew Murphy
857d8e3a56 Fix variant call compiler error
Co-authored-by: David Snopek <dsnopek@gmail.com>
(cherry picked from commit ca3e25de04)
2023-10-09 08:43:23 -05:00
David Snopek
ec6e51b3a4 Handle missing instance binding callbacks by finding the closest parent
(cherry picked from commit 52ca3ef547)
2023-10-09 08:42:55 -05:00
Adam Scott
f8054cca80 Add support to import custom variables from parent SConstruct (redux)
(cherry picked from commit 982e01ec7f)
2023-10-09 08:42:43 -05:00
A Thousand Ships
59ebcfd744 Fix allocation size overflow check in CowData
(cherry picked from commit 06ffc7e952)
2023-10-09 08:42:31 -05:00
A Thousand Ships
205beacc5b Replace ERR_FAIL_COND with ERR_FAIL_NULL where applicable
(cherry picked from commit 1e5767693e)
2023-10-09 08:42:16 -05:00
Rémi Verschelde
3b3f357de9 CI: Fix MinGW install error by pinning to earlier version
Works around https://github.com/egor-tensin/setup-mingw/issues/14.

(cherry picked from commit 0369f6fea0)
2023-10-04 15:21:01 +02:00
Rémi Verschelde
48b92acf8c gdextension: Sync with upstream commit 399c9dc393f6f84c0b4e4d4117906c70c048ecf2 (4.1.2-stable) 2023-10-04 12:02:54 +02:00
Rémi Verschelde
4eed2d7be0 Merge pull request #1244 from dsnopek/4.1-cherrypicks-3
Cherry-picks for the godot-cpp 4.1 branch - 3rd batch
2023-09-20 23:48:26 +02:00
David Snopek
bc82ae8b0b Add static methods to ClassDB for the methods bound to the ClassDB singleton
(cherry picked from commit 6f913563d8)
2023-09-19 21:30:03 -05:00
David Snopek
590e267902 Load 'print_error_with_message' function
(cherry picked from commit 634ed09ec0)
2023-09-19 21:29:29 -05:00
David Snopek
3be7ec4162 Check that GDExtension is opened by compatible Godot version
(cherry picked from commit fecb2959b4)
2023-09-19 21:27:40 -05:00
DmitriySalnikov
dd8e1def67 [SCons] Fixed crashes in several scripts
(cherry picked from commit 0e5975dd26)
2023-09-19 21:23:20 -05:00
David Snopek
dcd7a69512 Ensure that PtrToArg specializations for native structs are used
(cherry picked from commit 3cd3f24150)
2023-09-19 21:22:54 -05:00
dependabot[bot]
354ed1e79d Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 5d4ff63930)
2023-09-19 21:22:20 -05:00
A Thousand Ships
014132d4c0 Ensure const correctness for wrappers
(cherry picked from commit f651df5e7a)
2023-09-19 21:21:20 -05:00
David Snopek
bc980b59ff Merge pull request #1227 from dsnopek/4.1-cherrypicks-2
Cherry-picks for the godot-cpp 4.1 branch - 2nd batch
2023-09-02 12:38:20 -05:00
A Thousand Ships
c3771fb065 Fix formatting of compatibility_minimum examples
Without quotes the values is parsed as a float, breaking in various
cases.

(cherry picked from commit b3596a18e1)
2023-09-01 17:08:03 -05:00
Fabio Alessandrelli
63755b2a32 [SCons] Move the GodotCPP build to its own tool.
(cherry picked from commit f8b4f60cb9)
2023-09-01 17:07:53 -05:00
David Snopek
ce5dd378d9 Clarify versions and examples in the README
(cherry picked from commit 1588dc8437)
2023-09-01 17:07:41 -05:00
A Thousand Ships
c6fe6533f9 Fix link to test project in readme
Also updated format for library paths

(cherry picked from commit e586e11637)
2023-09-01 17:07:30 -05:00
A Thousand Ships
170a691a7e Add remaining component-wise min/max functions to Vector*
(cherry picked from commit 52eb77efd4)
2023-09-01 17:07:18 -05:00
Rémi Verschelde
738ef9baf8 SCons: Sync targets.py fully with upstream Godot
- Reorders existing code to match Godot.
- Adds `NDEBUG` for non-dev builds.
- Adds `-gdwarf-4` for Clang debug symbols.
- Adds strip link flag for GCC/Clang builds without debug symbols.

(cherry picked from commit 600e749d9b)
2023-09-01 17:07:03 -05:00
Adam Scott
c7afd0f89a Fix forgotten not operator
(cherry picked from commit f5c8e5190f)
2023-09-01 17:06:51 -05:00
Adam Scott
6789b29b72 Fix Clang deprecated builtins
It seems that Clang and GCC have different interpretations of certain
builtins. So this PR uses std <type_traits> functions just as cowdata.h
does in the godot project.

(cherry picked from commit 5c262844ad)
2023-09-01 17:06:39 -05:00
David Snopek
960c906da1 Add automated tests to verify some previous fixes
(cherry picked from commit d5fab0b9f8)
2023-09-01 17:06:20 -05:00
Marc Gilleron
0f2d3652e5 Added generated version header
(cherry picked from commit c6b2c82570)
2023-09-01 17:06:03 -05:00
David Snopek
28494f0bd5 Merge pull request #1205 from dsnopek/4.1-cherrypicks-1
Cherry-picks for the godot-cpp 4.1 branch - 1st batch
2023-08-11 10:35:12 -05:00
Feiyun Wang
4fb9af7fb2 Statically link mingw/msvc runtime libraries on Windows
Co-authored-by: David Snopek <dsnopek@gmail.com>
(cherry picked from commit a745c2ac47)
2023-08-10 09:10:04 -05:00
Fabio Alessandrelli
6fa6b8b178 [SCons] Merge OSXCross tools into platofrm ones
(cherry picked from commit 6d195137fe)
2023-08-10 09:09:49 -05:00
Fabio Alessandrelli
784c3dc012 [SCons] Add option to generate a compilation database.
(cherry picked from commit 2586ad016e)
2023-08-10 09:09:30 -05:00
Adam Scott
7a9b323931 Add platform macros
(cherry picked from commit 9d9f4279ed)
2023-08-10 09:09:14 -05:00
Marc Gilleron
e75ec636db Don't cache null forever if a singleton isn't available yet
# Conflicts:
#	binding_generator.py

(cherry picked from commit 548c758677)
2023-08-10 09:08:56 -05:00
David Snopek
5dda0212f6 In generated methods, only construct the method StringName the first time
(cherry picked from commit efc16b49d9)
2023-08-10 09:06:33 -05:00
David Snopek
011965d864 Attempt to fully implement CharString
(cherry picked from commit 4df112cd95)
2023-08-10 09:06:09 -05:00
76 changed files with 2708 additions and 1441 deletions

View File

@@ -65,7 +65,7 @@ jobs:
platform: android
artifact-name: godot-cpp-android-arm64-release
artifact-path: bin/libgodot-cpp.android.template_release.arm64.a
flags: ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME arch=arm64
flags: arch=arm64
run-tests: false
cache-name: android-arm64
@@ -78,12 +78,22 @@ jobs:
run-tests: false
cache-name: ios-arm64
- name: 🌐 Web (wasm32)
os: ubuntu-20.04
platform: web
artifact-name: godot-cpp-web-wasm32-release
artifact-path: bin/libgodot-cpp.web.template_release.wasm32.a
run-tests: false
cache-name: web-wasm32
env:
SCONS_CACHE: ${{ github.workspace }}/.scons-cache/
EM_VERSION: 3.1.39
EM_CACHE_FOLDER: "emsdk-cache"
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: recursive
@@ -94,42 +104,52 @@ jobs:
continue-on-error: true
- name: Set up Python (for SCons)
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Linux dependencies
if: ${{ matrix.platform == 'linux' }}
run: |
sudo apt-get update -qq
sudo apt-get install -qqq build-essential pkg-config
- name: Android dependencies
if: ${{ matrix.platform == 'android' }}
uses: nttld/setup-ndk@v1
with:
ndk-version: r23c
link-to-sdk: true
- name: Web dependencies
if: ${{ matrix.platform == 'web' }}
uses: mymindstorm/setup-emsdk@v14
with:
version: ${{env.EM_VERSION}}
actions-cache-folder: ${{env.EM_CACHE_FOLDER}}
- name: Setup MinGW for Windows/MinGW build
if: ${{ matrix.platform == 'windows' && matrix.flags == 'use_mingw=yes' }}
uses: egor-tensin/setup-mingw@v2
with:
version: 12.2.0
- name: Install scons
run: |
python -m pip install scons==4.0.0
- name: Setup MinGW for Windows/MinGW build
if: ${{ matrix.platform == 'windows' && matrix.flags == 'use_mingw=yes' }}
uses: egor-tensin/setup-mingw@v2
- name: Generate godot-cpp sources only
run: |
scons platform=${{ matrix.platform }} build_library=no ${{ matrix.flags }}
scons platform=${{ matrix.platform }} verbose=yes build_library=no ${{ matrix.flags }}
scons -c
- name: Build godot-cpp (debug)
run: |
scons platform=${{ matrix.platform }} target=template_debug ${{ matrix.flags }}
scons platform=${{ matrix.platform }} verbose=yes target=template_debug ${{ matrix.flags }}
- name: Build test without rebuilding godot-cpp (debug)
run: |
cd test
scons platform=${{ matrix.platform }} target=template_debug ${{ matrix.flags }} build_library=no
scons platform=${{ matrix.platform }} verbose=yes target=template_debug ${{ matrix.flags }} build_library=no
- name: Build test and godot-cpp (release)
run: |
cd test
scons platform=${{ matrix.platform }} target=template_release ${{ matrix.flags }}
scons platform=${{ matrix.platform }} verbose=yes target=template_release ${{ matrix.flags }}
- name: Download latest Godot artifacts
uses: dsnopek/action-download-artifact@1322f74e2dac9feed2ee76a32d9ae1ca3b4cf4e9
@@ -153,7 +173,7 @@ jobs:
./godot-artifacts/godot.linuxbsd.editor.x86_64.mono --headless --version
cd test
# Need to run the editor so .godot is generated... but it crashes! Ignore that :-)
(cd project && (../../godot-artifacts/godot.linuxbsd.editor.x86_64.mono --editor --headless --quit >/dev/null 2>&1 || true))
(cd project && (timeout 10 ../../godot-artifacts/godot.linuxbsd.editor.x86_64.mono --editor --headless --quit >/dev/null 2>&1 || true))
GODOT=../godot-artifacts/godot.linuxbsd.editor.x86_64.mono ./run-tests.sh
- name: Upload artifact
@@ -168,7 +188,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: recursive
@@ -192,7 +212,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: recursive
@@ -216,7 +236,7 @@ jobs:
runs-on: windows-2019
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: recursive

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
# Azure repositories are not reliable, we need to prevent Azure giving us packages.
- name: Make apt sources.list use the default Ubuntu repositories

4
.gitignore vendored
View File

@@ -191,3 +191,7 @@ godot.creator.*
# compile commands (https://clang.llvm.org/docs/JSONCompilationDatabase.html)
compile_commands.json
# Python development
.venv
venv

View File

@@ -37,7 +37,7 @@
# Todo
# Test build for Windows, Mac and mingw.
cmake_minimum_required(VERSION 3.12)
cmake_minimum_required(VERSION 3.13)
project(godot-cpp LANGUAGES CXX)
option(GENERATE_TEMPLATE_GET_NODE "Generate a template version of the Node class's get_node." ON)
@@ -47,11 +47,6 @@ option(GODOT_CPP_WARNING_AS_ERROR "Treat warnings as errors" OFF)
# Add path to modules
list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" )
# Check if we are building ourself or being included
if(${PROJECT_NAME} STREQUAL ${CMAKE_PROJECT_NAME})
set(GODOT_CPP_BUILDING_SELF ON)
endif()
# Set some helper variables for readability
set( compiler_is_clang "$<OR:$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:Clang>>" )
set( compiler_is_gnu "$<CXX_COMPILER_ID:GNU>" )
@@ -72,21 +67,22 @@ endif()
# Input from user for GDExtension interface header and the API JSON file
set(GODOT_GDEXTENSION_DIR "gdextension" CACHE STRING "")
set(GODOT_CUSTOM_API_FILE "" CACHE STRING "")
set(FLOAT_PRECISION "single" CACHE STRING "")
if ("${FLOAT_PRECISION}" STREQUAL "double")
add_definitions(-DREAL_T_IS_DOUBLE)
endif()
set(GODOT_GDEXTENSION_API_FILE "${GODOT_GDEXTENSION_DIR}/extension_api.json")
if (NOT "${GODOT_CUSTOM_API_FILE}" STREQUAL "") # User-defined override.
set(GODOT_GDEXTENSION_API_FILE "${GODOT_CUSTOM_API_FILE}")
endif()
set(FLOAT_PRECISION "single" CACHE STRING "")
if ("${FLOAT_PRECISION}" STREQUAL "double")
add_definitions(-DREAL_T_IS_DOUBLE)
endif()
set(GODOT_COMPILE_FLAGS )
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# using Visual Studio C++
set(GODOT_COMPILE_FLAGS "/EHsc /utf-8") # /GF /MP
set(GODOT_COMPILE_FLAGS "/utf-8") # /GF /MP
if(CMAKE_BUILD_TYPE MATCHES Debug)
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /MDd") # /Od /RTC1 /Zi
@@ -98,15 +94,28 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
add_definitions(-DNOMINMAX)
else() # GCC/Clang
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -g")
if(CMAKE_BUILD_TYPE MATCHES Debug)
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0 -g")
else()
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -O3")
endif(CMAKE_BUILD_TYPE MATCHES Debug)
endif()
# Disable exception handling. Godot doesn't use exceptions anywhere, and this
# saves around 20% of binary size and very significant build time (GH-80513).
option(GODOT_DISABLE_EXCEPTIONS OFF "Force disabling exception handling code")
if (GODOT_DISABLE_EXCEPTIONS)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -D_HAS_EXCEPTIONS=0")
else()
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-exceptions")
endif()
else()
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /EHsc")
endif()
endif()
# Generate source from the bindings file
find_package(Python3 3.4 REQUIRED) # pathlib should be present
if(GENERATE_TEMPLATE_GET_NODE)
@@ -118,6 +127,7 @@ endif()
execute_process(COMMAND "${Python3_EXECUTABLE}" "-c" "import binding_generator; binding_generator.print_file_list(\"${GODOT_GDEXTENSION_API_FILE}\", \"${CMAKE_CURRENT_BINARY_DIR}\", headers=True, sources=True)"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE GENERATED_FILES_LIST
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_custom_command(OUTPUT ${GENERATED_FILES_LIST}
@@ -143,12 +153,6 @@ add_library(godot::cpp ALIAS ${PROJECT_NAME})
include(GodotCompilerWarnings)
# Treat warnings as errors if we are building ourself
if(GODOT_CPP_BUILDING_SELF)
unset( GODOT_CPP_WARNING_AS_ERROR CACHE )
set_warning_as_error()
endif()
target_compile_features(${PROJECT_NAME}
PRIVATE
cxx_std_17

View File

@@ -2,11 +2,15 @@
> **Warning**
>
> This repository's `master` branch is only usable with the latest version of
> Godot's ([GDExtension](https://godotengine.org/article/introducing-gd-extensions))
> API (Godot 4.1 and later).
> This repository's `master` branch is only usable with
> [GDExtension](https://godotengine.org/article/introducing-gd-extensions)
> from Godot's `master` branch.
>
> For users of Godot 4.0.x, switch to the [`4.0`](https://github.com/godotengine/godot-cpp/tree/4.0) branch.
> For users of stable branches, switch to the branch matching your target Godot version:
> - [`4.0`](https://github.com/godotengine/godot-cpp/tree/4.0)
> - [`4.1`](https://github.com/godotengine/godot-cpp/tree/4.1)
>
> Or check out the Git tag matching your Godot version (e.g. `godot-4.1.1-stable`).
>
> For GDNative users (Godot 3.x), switch to the [`3.x`](https://github.com/godotengine/godot-cpp/tree/3.x)
> or the [`3.5`](https://github.com/godotengine/godot-cpp/tree/3.5) branch.
@@ -52,9 +56,10 @@ first-party `godot-cpp` extension.
Some compatibility breakage is to be expected as GDExtension and `godot-cpp`
get more used, documented, and critical issues get resolved. See the
[issue tracker](https://github.com/godotengine/godot/issues) for a list of known
issues, and be sure to provide feedback on issues and PRs which affect your use
of this extension.
[Godot issue tracker](https://github.com/godotengine/godot/issues?q=is%3Aissue+is%3Aopen+label%3Atopic%3Agdextension)
and the [godot-cpp issue tracker](https://github.com/godotengine/godot-cpp/issues)
for a list of known issues, and be sure to provide feedback on issues and PRs
which affect your use of this extension.
## Contributing
@@ -68,7 +73,10 @@ so formatting is done before your changes are submitted.
## Getting started
It's a bit similar to what it was for 3.x but also a bit different.
You need the same C++ pre-requisites installed that are required for the `godot` repository. Follow the [official build instructions for your target platform](https://docs.godotengine.org/en/latest/contributing/development/compiling/index.html#building-for-target-platforms).
Getting started with GDExtensions is a bit similar to what it was for 3.x but also a bit different.
This new approach is much more akin to how core Godot modules are structured.
Compiling this repository generates a static library to be linked with your shared lib,
@@ -76,22 +84,22 @@ just like before.
To use the shared lib in your Godot project you'll need a `.gdextension`
file, which replaces what was the `.gdnlib` before.
Follow [the example](test/demo/example.gdextension):
See [example.gdextension](test/project/example.gdextension) used in the test project:
```ini
[configuration]
entry_symbol = "example_library_init"
compatibility_minimum = 4.1
compatibility_minimum = "4.1"
[libraries]
macos.debug = "bin/libgdexample.macos.debug.framework"
macos.release = "bin/libgdexample.macos.release.framework"
windows.debug.x86_64 = "bin/libgdexample.windows.debug.x86_64.dll"
windows.release.x86_64 = "bin/libgdexample.windows.release.x86_64.dll"
linux.debug.x86_64 = "bin/libgdexample.linux.debug.x86_64.so"
linux.release.x86_64 = "bin/libgdexample.linux.release.x86_64.so"
macos.debug = "res://bin/libgdexample.macos.debug.framework"
macos.release = "res://bin/libgdexample.macos.release.framework"
windows.debug.x86_64 = "res://bin/libgdexample.windows.debug.x86_64.dll"
windows.release.x86_64 = "res://bin/libgdexample.windows.release.x86_64.dll"
linux.debug.x86_64 = "res://bin/libgdexample.linux.debug.x86_64.so"
linux.release.x86_64 = "res://bin/libgdexample.linux.release.x86_64.so"
# Repeat for other architectures to support arm64, rv64, etc.
```
@@ -123,12 +131,16 @@ void initialize_example_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
ClassDB::register_class<Example>();
GDREGISTER_CLASS(Example);
}
```
Any node and resource you register will be available in the corresponding `Create...` dialog. Any class will be available to scripting as well.
## Included example
## Examples and templates
Check the project in the `test` folder for an example on how to use and register different things.
See the [godot-cpp-template](https://github.com/godotengine/godot-cpp-template) project for a
generic reusable template.
Or checkout the code for the [Summator example](https://github.com/paddy-exe/GDExtensionSummator)
as shown in the [official documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/gdextension/gdextension_cpp_example.html).

View File

@@ -5,52 +5,11 @@ import platform
import sys
import subprocess
from binding_generator import scons_generate_bindings, scons_emit_files
from SCons.Errors import UserError
EnsureSConsVersion(4, 0)
def add_sources(sources, dir, extension):
for f in os.listdir(dir):
if f.endswith("." + extension):
sources.append(dir + "/" + f)
def normalize_path(val):
return val if os.path.isabs(val) else os.path.join(env.Dir("#").abspath, val)
def validate_api_file(key, val, env):
if not os.path.isfile(normalize_path(val)):
raise UserError("GDExtension API file ('%s') does not exist: %s" % (key, val))
def validate_gdextension_dir(key, val, env):
if not os.path.isdir(normalize_path(val)):
raise UserError("GDExtension directory ('%s') does not exist: %s" % (key, val))
def get_gdextension_dir(env):
return normalize_path(env.get("gdextension_dir", env.Dir("gdextension").abspath))
def get_api_file(env):
return normalize_path(env.get("custom_api_file", os.path.join(get_gdextension_dir(env), "extension_api.json")))
# Try to detect the host platform automatically.
# This is used if no `platform` argument is passed
if sys.platform.startswith("linux"):
default_platform = "linux"
elif sys.platform == "darwin":
default_platform = "macos"
elif sys.platform == "win32" or sys.platform == "msys":
default_platform = "windows"
elif ARGUMENTS.get("platform", ""):
default_platform = ARGUMENTS.get("platform")
else:
raise ValueError("Could not detect platform automatically, please specify with platform=<platform>")
try:
Import("env")
except:
@@ -60,26 +19,12 @@ except:
env.PrependENVPath("PATH", os.getenv("PATH"))
# Default num_jobs to local cpu count if not user specified.
# SCons has a peculiarity where user-specified options won't be overridden
# by SetOption, so we can rely on this to know if we should use our default.
initial_num_jobs = env.GetOption("num_jobs")
altered_num_jobs = initial_num_jobs + 1
env.SetOption("num_jobs", altered_num_jobs)
if env.GetOption("num_jobs") == altered_num_jobs:
cpu_count = os.cpu_count()
if cpu_count is None:
print("Couldn't auto-detect CPU count to configure build parallelism. Specify it with the -j argument.")
else:
safer_cpu_count = cpu_count if cpu_count <= 4 else cpu_count - 1
print(
"Auto-detected %d CPU cores available for build parallelism. Using %d cores by default. You can override it with the -j argument."
% (cpu_count, safer_cpu_count)
)
env.SetOption("num_jobs", safer_cpu_count)
# Custom options and profile flags.
customs = ["custom.py"]
try:
customs += Import("customs")
except:
pass
profile = ARGUMENTS.get("profile", "")
if profile:
if os.path.isfile(profile):
@@ -87,143 +32,12 @@ if profile:
elif os.path.isfile(profile + ".py"):
customs.append(profile + ".py")
opts = Variables(customs, ARGUMENTS)
platforms = ("linux", "macos", "windows", "android", "ios", "javascript")
opts.Add(
EnumVariable(
key="platform",
help="Target platform",
default=env.get("platform", default_platform),
allowed_values=platforms,
ignorecase=2,
)
)
# Editor and template_debug are compatible (i.e. you can use the same binary for Godot editor builds and Godot debug templates).
# Godot release templates are only compatible with "template_release" builds.
# For this reason, we default to template_debug builds, unlike Godot which defaults to editor builds.
opts.Add(
EnumVariable(
key="target",
help="Compilation target",
default=env.get("target", "template_debug"),
allowed_values=("editor", "template_release", "template_debug"),
)
)
opts.Add(
PathVariable(
key="gdextension_dir",
help="Path to a custom directory containing GDExtension interface header and API JSON file",
default=env.get("gdextension_dir", None),
validator=validate_gdextension_dir,
)
)
opts.Add(
PathVariable(
key="custom_api_file",
help="Path to a custom GDExtension API JSON file (takes precedence over `gdextension_dir`)",
default=env.get("custom_api_file", None),
validator=validate_api_file,
)
)
opts.Add(
BoolVariable(
key="generate_bindings",
help="Force GDExtension API bindings generation. Auto-detected by default.",
default=env.get("generate_bindings", False),
)
)
opts.Add(
BoolVariable(
key="generate_template_get_node",
help="Generate a template version of the Node class's get_node.",
default=env.get("generate_template_get_node", True),
)
)
opts.Add(BoolVariable(key="build_library", help="Build the godot-cpp library.", default=env.get("build_library", True)))
opts.Add(
EnumVariable(
key="precision",
help="Set the floating-point precision level",
default=env.get("precision", "single"),
allowed_values=("single", "double"),
)
)
# Add platform options
tools = {}
for pl in platforms:
tool = Tool(pl, toolpath=["tools"])
if hasattr(tool, "options"):
tool.options(opts)
tools[pl] = tool
# CPU architecture options.
architecture_array = ["", "universal", "x86_32", "x86_64", "arm32", "arm64", "rv64", "ppc32", "ppc64", "wasm32"]
architecture_aliases = {
"x64": "x86_64",
"amd64": "x86_64",
"armv7": "arm32",
"armv8": "arm64",
"arm64v8": "arm64",
"aarch64": "arm64",
"rv": "rv64",
"riscv": "rv64",
"riscv64": "rv64",
"ppcle": "ppc32",
"ppc": "ppc32",
"ppc64le": "ppc64",
}
opts.Add(
EnumVariable(
key="arch",
help="CPU architecture",
default=env.get("arch", ""),
allowed_values=architecture_array,
map=architecture_aliases,
)
)
# Targets flags tool (optimizations, debug symbols)
target_tool = Tool("targets", toolpath=["tools"])
target_tool.options(opts)
cpp_tool = Tool("godotcpp", toolpath=["tools"])
cpp_tool.options(opts, env)
opts.Update(env)
Help(opts.GenerateHelpText(env))
# Process CPU architecture argument.
if env["arch"] == "":
# No architecture specified. Default to arm64 if building for Android,
# universal if building for macOS or iOS, wasm32 if building for web,
# otherwise default to the host architecture.
if env["platform"] in ["macos", "ios"]:
env["arch"] = "universal"
elif env["platform"] == "android":
env["arch"] = "arm64"
elif env["platform"] == "javascript":
env["arch"] = "wasm32"
else:
host_machine = platform.machine().lower()
if host_machine in architecture_array:
env["arch"] = host_machine
elif host_machine in architecture_aliases.keys():
env["arch"] = architecture_aliases[host_machine]
elif "86" in host_machine:
# Catches x86, i386, i486, i586, i686, etc.
env["arch"] = "x86_32"
else:
print("Unsupported CPU architecture: " + host_machine)
Exit()
tool = Tool(env["platform"], toolpath=["tools"])
if tool is None or not tool.exists(env):
raise ValueError("Required toolchain not found for platform " + env["platform"])
tool.generate(env)
target_tool.generate(env)
# Detect and print a warning listing unknown SCons variables to ease troubleshooting.
unknown = opts.UnknownVariables()
if unknown:
@@ -231,66 +45,12 @@ if unknown:
for item in unknown.items():
print(" " + item[0] + "=" + item[1])
print("Building for architecture " + env["arch"] + " on platform " + env["platform"])
# Require C++17
if env.get("is_msvc", False):
env.Append(CXXFLAGS=["/std:c++17"])
else:
env.Append(CXXFLAGS=["-std=c++17"])
if env["precision"] == "double":
env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"])
# Generate bindings
env.Append(BUILDERS={"GenerateBindings": Builder(action=scons_generate_bindings, emitter=scons_emit_files)})
bindings = env.GenerateBindings(
env.Dir("."),
[get_api_file(env), os.path.join(get_gdextension_dir(env), "gdextension_interface.h"), "binding_generator.py"],
)
scons_cache_path = os.environ.get("SCONS_CACHE")
if scons_cache_path is not None:
CacheDir(scons_cache_path)
Decider("MD5")
# Forces bindings regeneration.
if env["generate_bindings"]:
AlwaysBuild(bindings)
NoCache(bindings)
cpp_tool.generate(env)
library = env.GodotCPP()
# Includes
env.Append(CPPPATH=[[env.Dir(d) for d in [get_gdextension_dir(env), "include", os.path.join("gen", "include")]]])
# Sources to compile
sources = []
add_sources(sources, "src", "cpp")
add_sources(sources, "src/classes", "cpp")
add_sources(sources, "src/core", "cpp")
add_sources(sources, "src/variant", "cpp")
sources.extend([f for f in bindings if str(f).endswith(".cpp")])
suffix = ".{}.{}".format(env["platform"], env["target"])
if env.dev_build:
suffix += ".dev"
if env["precision"] == "double":
suffix += ".double"
suffix += "." + env["arch"]
if env["ios_simulator"]:
suffix += ".simulator"
# Expose it when included from another project
env["suffix"] = suffix
library = None
env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"]
library_name = "libgodot-cpp{}{}".format(suffix, env["LIBSUFFIX"])
if env["build_library"]:
library = env.StaticLibrary(target=env.File("bin/%s" % library_name), source=sources)
Default(library)
env.Append(LIBPATH=[env.Dir("bin")])
env.Append(LIBS=library_name)
Return("env")

View File

@@ -97,9 +97,10 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False):
files.append(str(source_filename.as_posix()))
for engine_class in api["classes"]:
# TODO: Properly setup this singleton since it conflicts with ClassDB in the bindings.
# Generate code for the ClassDB singleton under a different name.
if engine_class["name"] == "ClassDB":
continue
engine_class["name"] = "ClassDBSingleton"
engine_class["alias_for"] = "ClassDB"
header_filename = include_gen_folder / "classes" / (camel_to_snake(engine_class["name"]) + ".hpp")
source_filename = source_gen_folder / "classes" / (camel_to_snake(engine_class["name"]) + ".cpp")
if headers:
@@ -123,21 +124,18 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False):
include_gen_folder / "variant" / "variant_size.hpp",
include_gen_folder / "classes" / "global_constants.hpp",
include_gen_folder / "classes" / "global_constants_binds.hpp",
include_gen_folder / "core" / "version.hpp",
]:
files.append(str(path.as_posix()))
if sources:
utility_functions_source_path = source_gen_folder / "variant" / "utility_functions.cpp"
files.append(str(utility_functions_source_path.as_posix()))
register_engine_classes_source_path = source_gen_folder / "register_engine_classes.cpp"
files.append(str(register_engine_classes_source_path.as_posix()))
return files
def print_file_list(api_filepath, output_dir, headers=False, sources=False):
end = ";"
for f in get_file_list(api_filepath, output_dir, headers, sources):
print(f, end=end)
print(*get_file_list(api_filepath, output_dir, headers, sources), sep=";", end=None)
def scons_emit_files(target, source, env):
@@ -173,6 +171,7 @@ def generate_bindings(api_filepath, use_template_get_node, bits="64", precision=
print("Built-in type config: " + real_t + "_" + bits)
generate_global_constants(api, target_dir)
generate_version_header(api, target_dir)
generate_global_constant_binds(api, target_dir)
generate_builtin_bindings(api, target_dir, real_t + "_" + bits)
generate_engine_classes_bindings(api, target_dir, use_template_get_node)
@@ -376,6 +375,10 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl
if class_name == "PackedVector3Array":
result.append("#include <godot_cpp/variant/vector3.hpp>")
if is_packed_array(class_name):
result.append("#include <godot_cpp/core/error_macros.hpp>")
result.append("#include <initializer_list>")
if class_name == "Array":
result.append("#include <godot_cpp/variant/array_helpers.hpp>")
@@ -414,6 +417,8 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl
result.append("")
result.append("\tstatic struct _MethodBindings {")
result.append("\t\tGDExtensionTypeFromVariantConstructorFunc from_variant_constructor;")
if "constructors" in builtin_api:
for constructor in builtin_api["constructors"]:
result.append(f'\t\tGDExtensionPtrConstructor constructor_{constructor["index"]};')
@@ -454,6 +459,9 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl
result.append("\tstatic void init_bindings();")
result.append("\tstatic void _init_bindings_constructors_destructor();")
result.append("")
result.append(f"\t{class_name}(const Variant *p_variant);")
result.append("")
result.append("public:")
@@ -512,7 +520,7 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl
vararg = method["is_vararg"]
if vararg:
result.append("\ttemplate<class... Args>")
result.append("\ttemplate<typename... Args>")
method_signature = "\t"
if "is_static" in method and method["is_static"]:
@@ -542,10 +550,10 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl
# Special cases.
if class_name == "String":
result.append("\tstatic String utf8(const char *from, int len = -1);")
result.append("\tvoid parse_utf8(const char *from, int len = -1);")
result.append("\tstatic String utf16(const char16_t *from, int len = -1);")
result.append("\tvoid parse_utf16(const char16_t *from, int len = -1);")
result.append("\tstatic String utf8(const char *from, int64_t len = -1);")
result.append("\tvoid parse_utf8(const char *from, int64_t len = -1);")
result.append("\tstatic String utf16(const char16_t *from, int64_t len = -1);")
result.append("\tvoid parse_utf16(const char16_t *from, int64_t len = -1);")
result.append("\tCharString utf8() const;")
result.append("\tCharString ascii() const;")
result.append("\tChar16String utf16() const;")
@@ -604,13 +612,13 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl
result.append("\tString &operator+=(const wchar_t *p_str);")
result.append("\tString &operator+=(const char32_t *p_str);")
result.append("\tconst char32_t &operator[](int p_index) const;")
result.append("\tchar32_t &operator[](int p_index);")
result.append("\tconst char32_t &operator[](int64_t p_index) const;")
result.append("\tchar32_t &operator[](int64_t p_index);")
result.append("\tconst char32_t *ptr() const;")
result.append("\tchar32_t *ptrw();")
if class_name == "Array":
result.append("\ttemplate <class... Args>")
result.append("\ttemplate <typename... Args>")
result.append("\tstatic Array make(Args... args) {")
result.append("\t\treturn helpers::append_all(Array(), args...);")
result.append("\t}")
@@ -623,8 +631,8 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl
return_type = "int32_t"
elif class_name == "PackedFloat32Array":
return_type = "float"
result.append(f"\tconst {return_type} &operator[](int p_index) const;")
result.append(f"\t{return_type} &operator[](int p_index);")
result.append(f"\tconst {return_type} &operator[](int64_t p_index) const;")
result.append(f"\t{return_type} &operator[](int64_t p_index);")
result.append(f"\tconst {return_type} *ptr() const;")
result.append(f"\t{return_type} *ptrw();")
iterators = """
@@ -693,10 +701,21 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl
}
"""
result.append(iterators.replace("$TYPE", return_type))
init_list = """
_FORCE_INLINE_ $CLASS(std::initializer_list<$TYPE> p_init) {
ERR_FAIL_COND(resize(p_init.size()) != 0);
size_t i = 0;
for (const $TYPE &element : p_init) {
set(i++, element);
}
}
"""
result.append(init_list.replace("$TYPE", return_type).replace("$CLASS", class_name))
if class_name == "Array":
result.append("\tconst Variant &operator[](int p_index) const;")
result.append("\tVariant &operator[](int p_index);")
result.append("\tconst Variant &operator[](int64_t p_index) const;")
result.append("\tVariant &operator[](int64_t p_index);")
result.append("\tvoid set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script);")
result.append("\tvoid _ref(const Array &p_from) const;")
@@ -771,6 +790,10 @@ def generate_builtin_class_source(builtin_api, size, used_classes, fully_used_cl
result.append(f"void {class_name}::_init_bindings_constructors_destructor() {{")
result.append(
f"\t_method_bindings.from_variant_constructor = internal::gdextension_interface_get_variant_to_type_constructor({enum_type_name});"
)
if "constructors" in builtin_api:
for constructor in builtin_api["constructors"]:
result.append(
@@ -852,6 +875,11 @@ def generate_builtin_class_source(builtin_api, size, used_classes, fully_used_cl
copy_constructor_index = -1
result.append(f"{class_name}::{class_name}(const Variant *p_variant) {{")
result.append("\t_method_bindings.from_variant_constructor(&opaque, p_variant->_native_ptr());")
result.append("}")
result.append("")
if "constructors" in builtin_api:
for constructor in builtin_api["constructors"]:
method_signature = f"{class_name}::{class_name}("
@@ -917,8 +945,19 @@ def generate_builtin_class_source(builtin_api, size, used_classes, fully_used_cl
result.append(method_signature + "{")
method_call = "\t"
is_ref = False
if "return_type" in method:
method_call += f'return internal::_call_builtin_method_ptr_ret<{correct_type(method["return_type"])}>('
return_type = method["return_type"]
if is_enum(return_type):
method_call += f"return ({get_gdextension_type(correct_type(return_type))})internal::_call_builtin_method_ptr_ret<int64_t>("
elif is_pod_type(return_type) or is_variant(return_type):
method_call += f"return internal::_call_builtin_method_ptr_ret<{get_gdextension_type(correct_type(return_type))}>("
elif is_refcounted(return_type):
method_call += f"return Ref<{return_type}>::_gde_internal_constructor(internal::_call_builtin_method_ptr_ret_obj<{return_type}>("
is_ref = True
else:
method_call += f"return internal::_call_builtin_method_ptr_ret_obj<{return_type}>("
else:
method_call += "internal::_call_builtin_method_ptr_no_ret("
method_call += f'_method_bindings.method_{method["name"]}, '
@@ -939,6 +978,9 @@ def generate_builtin_class_source(builtin_api, size, used_classes, fully_used_cl
result += encode
arguments.append(arg_name)
method_call += ", ".join(arguments)
if is_ref:
method_call += ")" # Close Ref<> constructor.
method_call += ");"
result.append(method_call)
@@ -1036,21 +1078,23 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
# First create map of classes and singletons.
for class_api in api["classes"]:
# TODO: Properly setup this singleton since it conflicts with ClassDB in the bindings.
# Generate code for the ClassDB singleton under a different name.
if class_api["name"] == "ClassDB":
continue
class_api["name"] = "ClassDBSingleton"
class_api["alias_for"] = "ClassDB"
engine_classes[class_api["name"]] = class_api["is_refcounted"]
for native_struct in api["native_structures"]:
engine_classes[native_struct["name"]] = False
native_structures.append(native_struct["name"])
for singleton in api["singletons"]:
# Generate code for the ClassDB singleton under a different name.
if singleton["name"] == "ClassDB":
singleton["name"] = "ClassDBSingleton"
singleton["alias_for"] = "ClassDB"
singletons.append(singleton["name"])
for class_api in api["classes"]:
# TODO: Properly setup this singleton since it conflicts with ClassDB in the bindings.
if class_api["name"] == "ClassDB":
continue
# Check used classes for header include.
used_classes = set()
fully_used_classes = set()
@@ -1140,6 +1184,12 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
else:
fully_used_classes.add("Wrapped")
# In order to ensure that PtrToArg specializations for native structs are
# always used, let's move any of them into 'fully_used_classes'.
for type_name in used_classes:
if is_struct_type(type_name) and not is_included_struct_type(type_name):
fully_used_classes.add(type_name)
for type_name in fully_used_classes:
if type_name in used_classes:
used_classes.remove(type_name)
@@ -1159,10 +1209,6 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
generate_engine_class_source(class_api, used_classes, fully_used_classes, use_template_get_node)
)
register_engine_classes_filename = Path(output_dir) / "src" / "register_engine_classes.cpp"
with register_engine_classes_filename.open("w+", encoding="utf-8") as source_file:
source_file.write(generate_register_engine_classes_source(api))
for native_struct in api["native_structures"]:
struct_name = native_struct["name"]
snake_struct_name = camel_to_snake(struct_name)
@@ -1237,12 +1283,12 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append(f"#include <godot_cpp/{get_include_path(included)}>")
if class_name == "EditorPlugin":
result.append("#include <godot_cpp/templates/vector.hpp>")
result.append("#include <godot_cpp/classes/editor_plugin_registration.hpp>")
if len(fully_used_classes) > 0:
result.append("")
if class_name != "Object":
if class_name != "Object" and class_name != "ClassDBSingleton":
result.append("#include <godot_cpp/core/class_db.hpp>")
result.append("")
result.append("#include <type_traits>")
@@ -1263,7 +1309,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
inherits = class_api["inherits"] if "inherits" in class_api else "Wrapped"
result.append(f"class {class_name} : public {inherits} {{")
result.append(f"\tGDEXTENSION_CLASS({class_name}, {inherits})")
if "alias_for" in class_api:
result.append(f"\tGDEXTENSION_CLASS_ALIAS({class_name}, {class_api['alias_for']}, {inherits})")
else:
result.append(f"\tGDEXTENSION_CLASS({class_name}, {inherits})")
result.append("")
result.append("public:")
@@ -1322,7 +1371,7 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append("protected:")
# T is the custom class we want to register (from which the call initiates, going up the inheritance chain),
# B is its base class (can be a custom class too, that's why we pass it).
result.append("\ttemplate <class T, class B>")
result.append("\ttemplate <typename T, typename B>")
result.append("\tstatic void register_virtuals() {")
if class_name != "Object":
result.append(f"\t\t{inherits}::register_virtuals<T, B>();")
@@ -1368,16 +1417,16 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
if class_name == "Object":
result.append("")
result.append("\ttemplate<class T>")
result.append("\ttemplate<typename T>")
result.append("\tstatic T *cast_to(Object *p_object);")
result.append("\ttemplate<class T>")
result.append("\ttemplate<typename T>")
result.append("\tstatic const T *cast_to(const Object *p_object);")
result.append("\tvirtual ~Object() = default;")
elif use_template_get_node and class_name == "Node":
result.append("\ttemplate<class T>")
result.append("\ttemplate<typename T>")
result.append(
"\tT *get_node(const NodePath &p_path) const { return Object::cast_to<T>(get_node_internal(p_path)); }"
)
@@ -1386,30 +1435,6 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append("};")
result.append("")
if class_name == "EditorPlugin":
result.append("class EditorPlugins {")
result.append("private:")
result.append("\tstatic Vector<StringName> plugin_classes;")
result.append("")
result.append("public:")
result.append("\tstatic void add_plugin_class(const StringName &p_class_name);")
result.append("\tstatic void remove_plugin_class(const StringName &p_class_name);")
result.append("\tstatic void deinitialize(GDExtensionInitializationLevel p_level);")
result.append("")
result.append("\ttemplate <class T>")
result.append("\tstatic void add_by_type() {")
result.append("\t\tadd_plugin_class(T::get_class_static());")
result.append("\t}")
result.append("\ttemplate <class T>")
result.append("\tstatic void remove_by_type() {")
result.append("\t\tremove_plugin_class(T::get_class_static());")
result.append("\t}")
result.append("};")
result.append("")
result.append("} // namespace godot")
result.append("")
@@ -1421,6 +1446,51 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append(f'VARIANT_ENUM_CAST({class_name}::{enum_api["name"]});')
result.append("")
if class_name == "ClassDBSingleton":
result.append("#define CLASSDB_SINGLETON_FORWARD_METHODS \\")
for method in class_api["methods"]:
# ClassDBSingleton shouldn't have any static or vararg methods, but if some appear later, lets skip them.
if vararg:
continue
if "is_static" in method and method["is_static"]:
continue
method_signature = "\tstatic "
if "return_type" in method:
method_signature += f'{correct_type(method["return_type"])} '
elif "return_value" in method:
method_signature += (
correct_type(method["return_value"]["type"], method["return_value"].get("meta", None)) + " "
)
else:
method_signature += "void "
method_signature += f'{method["name"]}('
method_arguments = []
if "arguments" in method:
method_arguments = method["arguments"]
method_signature += make_function_parameters(
method_arguments, include_default=True, for_builtin=True, is_vararg=False
)
method_signature += ") { \\"
result.append(method_signature)
method_body = "\t\t"
if "return_type" in method or "return_value" in method:
method_body += "return "
method_body += f'ClassDBSingleton::get_singleton()->{method["name"]}('
method_body += ", ".join(map(lambda x: escape_identifier(x["name"]), method_arguments))
method_body += "); \\"
result.append(method_body)
result.append("\t} \\")
result.append("\t;")
result.append("")
result.append(f"#endif // ! {header_guard}")
return "\n".join(result)
@@ -1453,16 +1523,22 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
if is_singleton:
result.append(f"{class_name} *{class_name}::get_singleton() {{")
result.append(f"\tconst StringName _gde_class_name = {class_name}::get_class_static();")
# We assume multi-threaded access is OK because each assignment will assign the same value every time
result.append(f"\tstatic {class_name} *singleton = nullptr;")
result.append("\tif (unlikely(singleton == nullptr)) {")
result.append(
"\tstatic GDExtensionObjectPtr singleton_obj = internal::gdextension_interface_global_get_singleton(_gde_class_name._native_ptr());"
f"\t\tGDExtensionObjectPtr singleton_obj = internal::gdextension_interface_global_get_singleton({class_name}::get_class_static()._native_ptr());"
)
result.append("#ifdef DEBUG_ENABLED")
result.append("\tERR_FAIL_COND_V(singleton_obj == nullptr, nullptr);")
result.append("\t\tERR_FAIL_NULL_V(singleton_obj, nullptr);")
result.append("#endif // DEBUG_ENABLED")
result.append(
f"\tstatic {class_name} *singleton = reinterpret_cast<{class_name} *>(internal::gdextension_interface_object_get_instance_binding(singleton_obj, internal::token, &{class_name}::_gde_binding_callbacks));"
f"\t\tsingleton = reinterpret_cast<{class_name} *>(internal::gdextension_interface_object_get_instance_binding(singleton_obj, internal::token, &{class_name}::_gde_binding_callbacks));"
)
result.append("#ifdef DEBUG_ENABLED")
result.append("\t\tERR_FAIL_NULL_V(singleton, nullptr);")
result.append("#endif // DEBUG_ENABLED")
result.append("\t}")
result.append("\treturn singleton;")
result.append("}")
result.append("")
@@ -1480,10 +1556,8 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
result.append(method_signature + " {")
# Method body.
result.append(f"\tconst StringName _gde_class_name = {class_name}::get_class_static();")
result.append(f'\tconst StringName _gde_method_name = "{method["name"]}";')
result.append(
f'\tstatic GDExtensionMethodBindPtr _gde_method_bind = internal::gdextension_interface_classdb_get_method_bind(_gde_class_name._native_ptr(), _gde_method_name._native_ptr(), {method["hash"]});'
f'\tstatic GDExtensionMethodBindPtr _gde_method_bind = internal::gdextension_interface_classdb_get_method_bind({class_name}::get_class_static()._native_ptr(), StringName("{method["name"]}")._native_ptr(), {method["hash"]});'
)
method_call = "\t"
has_return = "return_value" in method and method["return_value"]["type"] != "void"
@@ -1585,38 +1659,6 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
return "\n".join(result)
def generate_register_engine_classes_source(api):
includes = []
registrations = []
for class_api in api["classes"]:
if class_api["name"] == "ClassDB":
continue
class_name = class_api["name"]
snake_class_name = camel_to_snake(class_name)
includes.append(f"#include <godot_cpp/classes/{snake_class_name}.hpp>")
registrations.append(f"\tClassDB::register_engine_class<{class_name}>();")
result = []
add_header(f"register_engine_classes.cpp", result)
result.append("#include <godot_cpp/godot.hpp>")
result.append("")
result = result + includes
result.append("")
result.append("namespace godot {")
result.append("")
result.append("void GDExtensionBinding::register_engine_classes() {")
result = result + registrations
result.append("}")
result.append("")
result.append("} // namespace godot ")
return "\n".join(result)
def generate_global_constants(api, output_dir):
include_gen_folder = Path(output_dir) / "include" / "godot_cpp" / "classes"
source_gen_folder = Path(output_dir) / "src" / "classes"
@@ -1639,7 +1681,7 @@ def generate_global_constants(api, output_dir):
header.append("")
for constant in api["global_constants"]:
header.append(f'\tconst int {escape_identifier(constant["name"])} = {constant["value"]};')
header.append(f'\tconst int64_t {escape_identifier(constant["name"])} = {constant["value"]};')
header.append("")
@@ -1662,6 +1704,35 @@ def generate_global_constants(api, output_dir):
header_file.write("\n".join(header))
def generate_version_header(api, output_dir):
header = []
header_filename = "version.hpp"
add_header(header_filename, header)
include_gen_folder = Path(output_dir) / "include" / "godot_cpp" / "core"
include_gen_folder.mkdir(parents=True, exist_ok=True)
header_file_path = include_gen_folder / header_filename
header_guard = "GODOT_CPP_VERSION_HPP"
header.append(f"#ifndef {header_guard}")
header.append(f"#define {header_guard}")
header.append("")
header.append(f"#define GODOT_VERSION_MAJOR {api['header']['version_major']}")
header.append(f"#define GODOT_VERSION_MINOR {api['header']['version_minor']}")
header.append(f"#define GODOT_VERSION_PATCH {api['header']['version_patch']}")
header.append(f"#define GODOT_VERSION_STATUS \"{api['header']['version_status']}\"")
header.append(f"#define GODOT_VERSION_BUILD \"{api['header']['version_build']}\"")
header.append("")
header.append(f"#endif // {header_guard}")
header.append("")
with header_file_path.open("w+", encoding="utf-8") as header_file:
header_file.write("\n".join(header))
def generate_global_constant_binds(api, output_dir):
include_gen_folder = Path(output_dir) / "include" / "godot_cpp" / "classes"
source_gen_folder = Path(output_dir) / "src" / "classes"
@@ -1688,9 +1759,9 @@ def generate_global_constant_binds(api, output_dir):
continue
if enum_def["is_bitfield"]:
header.append(f'VARIANT_BITFIELD_CAST(godot::{enum_def["name"]});')
header.append(f'VARIANT_BITFIELD_CAST({enum_def["name"]});')
else:
header.append(f'VARIANT_ENUM_CAST(godot::{enum_def["name"]});')
header.append(f'VARIANT_ENUM_CAST({enum_def["name"]});')
# Variant::Type is not a global enum, but only one line, it is worth to place in this file instead of creating new file.
header.append(f"VARIANT_ENUM_CAST(godot::Variant::Type);")
@@ -1773,9 +1844,8 @@ def generate_utility_functions(api, output_dir):
# Function body.
source.append(f'\tconst StringName _gde_function_name = "{function["name"]}";')
source.append(
f'\tstatic GDExtensionPtrUtilityFunction _gde_function = internal::gdextension_interface_variant_get_ptr_utility_function(_gde_function_name._native_ptr(), {function["hash"]});'
f'\tstatic GDExtensionPtrUtilityFunction _gde_function = internal::gdextension_interface_variant_get_ptr_utility_function(StringName("{function["name"]}")._native_ptr(), {function["hash"]});'
)
has_return = "return_type" in function and function["return_type"] != "void"
if has_return:
@@ -1972,7 +2042,7 @@ def make_signature(
def make_varargs_template(function_data, static=False):
result = []
function_signature = "\tpublic: template<class... Args> "
function_signature = "\tpublic: template<typename... Args> "
if static:
function_signature += "static "
@@ -2285,6 +2355,7 @@ def escape_identifier(id):
"operator": "_operator",
"typeof": "type_of",
"typename": "type_name",
"enum": "_enum",
}
if id in cpp_keywords_map:
return cpp_keywords_map[id]
@@ -2306,6 +2377,7 @@ def get_operator_id_name(op):
"unary-": "negate",
"unary+": "positive",
"%": "module",
"**": "power",
"<<": "shift_left",
">>": "shift_right",
"&": "bit_and",

View File

@@ -2,10 +2,10 @@
"header": {
"version_major": 4,
"version_minor": 1,
"version_patch": 1,
"version_patch": 4,
"version_status": "stable",
"version_build": "official",
"version_full_name": "Godot Engine v4.1.1.stable.official"
"version_full_name": "Godot Engine v4.1.4.stable.official"
},
"builtin_class_sizes": [
{
@@ -2593,6 +2593,22 @@
"name": "KEY_LAUNCHF",
"value": 4194415
},
{
"name": "KEY_GLOBE",
"value": 4194416
},
{
"name": "KEY_KEYBOARD",
"value": 4194417
},
{
"name": "KEY_JIS_EISU",
"value": 4194418
},
{
"name": "KEY_JIS_KANA",
"value": 4194419
},
{
"name": "KEY_UNKNOWN",
"value": 8388607
@@ -2880,22 +2896,6 @@
{
"name": "KEY_SECTION",
"value": 167
},
{
"name": "KEY_GLOBE",
"value": 4194416
},
{
"name": "KEY_KEYBOARD",
"value": 4194417
},
{
"name": "KEY_JIS_EISU",
"value": 4194418
},
{
"name": "KEY_JIS_KANA",
"value": 4194419
}
]
},
@@ -95246,13 +95246,6 @@
}
]
},
{
"name": "FramebufferCacheRD",
"is_refcounted": false,
"is_instantiable": false,
"inherits": "Object",
"api_type": "core"
},
{
"name": "GDExtension",
"is_refcounted": true,
@@ -113089,6 +113082,29 @@
}
]
},
{
"name": "should_ignore_device",
"is_const": true,
"is_vararg": false,
"is_static": false,
"is_virtual": false,
"hash": 2522259332,
"return_value": {
"type": "bool"
},
"arguments": [
{
"name": "vendor_id",
"type": "int",
"meta": "int32"
},
{
"name": "product_id",
"type": "int",
"meta": "int32"
}
]
},
{
"name": "get_connected_joypads",
"is_const": false,
@@ -122009,6 +122025,10 @@
{
"name": "BAKE_ERROR_USER_ABORTED",
"value": 8
},
{
"name": "BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL",
"value": 9
}
]
},
@@ -149258,6 +149278,93 @@
}
]
},
{
"name": "OpenXRInteractionProfileMetadata",
"is_refcounted": false,
"is_instantiable": true,
"inherits": "Object",
"api_type": "core",
"methods": [
{
"name": "register_top_level_path",
"is_const": false,
"is_vararg": false,
"is_static": false,
"is_virtual": false,
"hash": 254767734,
"arguments": [
{
"name": "display_name",
"type": "String"
},
{
"name": "openxr_path",
"type": "String"
},
{
"name": "openxr_extension_name",
"type": "String"
}
]
},
{
"name": "register_interaction_profile",
"is_const": false,
"is_vararg": false,
"is_static": false,
"is_virtual": false,
"hash": 254767734,
"arguments": [
{
"name": "display_name",
"type": "String"
},
{
"name": "openxr_path",
"type": "String"
},
{
"name": "openxr_extension_name",
"type": "String"
}
]
},
{
"name": "register_io_path",
"is_const": false,
"is_vararg": false,
"is_static": false,
"is_virtual": false,
"hash": 3443511926,
"arguments": [
{
"name": "interaction_profile",
"type": "String"
},
{
"name": "display_name",
"type": "String"
},
{
"name": "toplevel_path",
"type": "String"
},
{
"name": "openxr_path",
"type": "String"
},
{
"name": "openxr_extension_name",
"type": "String"
},
{
"name": "action_type",
"type": "enum::OpenXRAction.ActionType"
}
]
}
]
},
{
"name": "OpenXRInterface",
"is_refcounted": true,
@@ -198879,6 +198986,23 @@
}
]
},
{
"name": "debug_canvas_item_get_rect",
"is_const": false,
"is_vararg": false,
"is_static": false,
"is_virtual": false,
"hash": 624227424,
"return_value": {
"type": "Rect2"
},
"arguments": [
{
"name": "item",
"type": "RID"
}
]
},
{
"name": "canvas_light_create",
"is_const": false,
@@ -251426,6 +251550,43 @@
}
]
},
{
"name": "set_text_overrun_behavior",
"is_const": false,
"is_vararg": false,
"is_static": false,
"is_virtual": false,
"hash": 1940772195,
"arguments": [
{
"name": "column",
"type": "int",
"meta": "int32"
},
{
"name": "overrun_behavior",
"type": "enum::TextServer.OverrunBehavior"
}
]
},
{
"name": "get_text_overrun_behavior",
"is_const": true,
"is_vararg": false,
"is_static": false,
"is_virtual": false,
"hash": 3782727860,
"return_value": {
"type": "enum::TextServer.OverrunBehavior"
},
"arguments": [
{
"name": "column",
"type": "int",
"meta": "int32"
}
]
},
{
"name": "set_structured_text_bidi_override",
"is_const": false,
@@ -255076,13 +255237,6 @@
}
]
},
{
"name": "UniformSetCacheRD",
"is_refcounted": false,
"is_instantiable": false,
"inherits": "Object",
"api_type": "core"
},
{
"name": "VBoxContainer",
"is_refcounted": false,

View File

@@ -0,0 +1,62 @@
/**************************************************************************/
/* editor_plugin_registration.hpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef GODOT_EDITOR_PLUGIN_REGISTRATION_HPP
#define GODOT_EDITOR_PLUGIN_REGISTRATION_HPP
#include <godot_cpp/templates/vector.hpp>
namespace godot {
class EditorPlugin;
class StringName;
class EditorPlugins {
private:
static Vector<StringName> plugin_classes;
public:
static void add_plugin_class(const StringName &p_class_name);
static void remove_plugin_class(const StringName &p_class_name);
static void deinitialize(GDExtensionInitializationLevel p_level);
template <typename T>
static void add_by_type() {
add_plugin_class(T::get_class_static());
}
template <typename T>
static void remove_by_type() {
remove_plugin_class(T::get_class_static());
}
};
} // namespace godot
#endif // GODOT_EDITOR_PLUGIN_REGISTRATION_HPP

View File

@@ -45,7 +45,7 @@ namespace godot {
class RefCounted;
template <class T>
template <typename T>
class Ref {
T *reference = nullptr;
@@ -63,7 +63,7 @@ class Ref {
}
void ref_pointer(T *p_ref) {
ERR_FAIL_COND(!p_ref);
ERR_FAIL_NULL(p_ref);
if (p_ref->init_ref()) {
reference = p_ref;
@@ -108,7 +108,7 @@ public:
ref(p_from);
}
template <class T_Other>
template <typename T_Other>
void operator=(const Ref<T_Other> &p_from) {
RefCounted *refb = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_from.ptr()));
if (!refb) {
@@ -144,7 +144,7 @@ public:
}
}
template <class T_Other>
template <typename T_Other>
void reference_ptr(T_Other *p_ptr) {
if (reference == p_ptr) {
return;
@@ -161,7 +161,7 @@ public:
ref(p_from);
}
template <class T_Other>
template <typename T_Other>
Ref(const Ref<T_Other> &p_from) {
RefCounted *refb = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_from.ptr()));
if (!refb) {
@@ -226,7 +226,7 @@ public:
}
};
template <class T>
template <typename T>
struct PtrToArg<Ref<T>> {
_FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) {
GDExtensionRefPtr ref = (GDExtensionRefPtr)p_ptr;
@@ -248,7 +248,7 @@ struct PtrToArg<Ref<T>> {
}
};
template <class T>
template <typename T>
struct PtrToArg<const Ref<T> &> {
typedef Ref<T> EncodeT;
@@ -259,7 +259,7 @@ struct PtrToArg<const Ref<T> &> {
}
};
template <class T>
template <typename T>
struct GetTypeInfo<Ref<T>, typename EnableIf<TypeInherits<RefCounted, T>::value>::type> {
static const GDExtensionVariantType VARIANT_TYPE = GDEXTENSION_VARIANT_TYPE_OBJECT;
static const GDExtensionClassMethodArgumentMetadata METADATA = GDEXTENSION_METHOD_ARGUMENT_METADATA_NONE;
@@ -269,7 +269,7 @@ struct GetTypeInfo<Ref<T>, typename EnableIf<TypeInherits<RefCounted, T>::value>
}
};
template <class T>
template <typename T>
struct GetTypeInfo<const Ref<T> &, typename EnableIf<TypeInherits<RefCounted, T>::value>::type> {
static const GDExtensionVariantType VARIANT_TYPE = GDEXTENSION_VARIANT_TYPE_OBJECT;
static const GDExtensionClassMethodArgumentMetadata METADATA = GDEXTENSION_METHOD_ARGUMENT_METADATA_NONE;

View File

@@ -95,6 +95,26 @@ public:
GodotObject *_owner = nullptr;
};
namespace internal {
typedef void (*EngineClassRegistrationCallback)();
void add_engine_class_registration_callback(EngineClassRegistrationCallback p_callback);
void register_engine_class(const StringName &p_name, const GDExtensionInstanceBindingCallbacks *p_callbacks);
void register_engine_classes();
template <typename T>
struct EngineClassRegistration {
EngineClassRegistration() {
add_engine_class_registration_callback(&EngineClassRegistration<T>::callback);
}
static void callback() {
register_engine_class(T::get_class_static(), &T::_gde_binding_callbacks);
}
};
} // namespace internal
} // namespace godot
#define GDCLASS(m_class, m_inherits) \
@@ -132,24 +152,26 @@ protected:
return (void(::godot::Wrapped::*)(::godot::List<::godot::PropertyInfo> * p_list) const) & m_class::_get_property_list; \
} \
\
static bool (::godot::Wrapped::*_get_property_can_revert())(const ::godot::StringName &p_name) { \
return (bool(::godot::Wrapped::*)(const ::godot::StringName &p_name)) & m_class::_property_can_revert; \
static bool (::godot::Wrapped::*_get_property_can_revert())(const ::godot::StringName &p_name) const { \
return (bool(::godot::Wrapped::*)(const ::godot::StringName &p_name) const) & m_class::_property_can_revert; \
} \
\
static bool (::godot::Wrapped::*_get_property_get_revert())(const ::godot::StringName &p_name, ::godot::Variant &) { \
return (bool(::godot::Wrapped::*)(const ::godot::StringName &p_name, ::godot::Variant &)) & m_class::_property_get_revert; \
static bool (::godot::Wrapped::*_get_property_get_revert())(const ::godot::StringName &p_name, ::godot::Variant &) const { \
return (bool(::godot::Wrapped::*)(const ::godot::StringName &p_name, ::godot::Variant &) const) & m_class::_property_get_revert; \
} \
\
static ::godot::String (::godot::Wrapped::*_get_to_string())() { \
return (::godot::String(::godot::Wrapped::*)()) & m_class::_to_string; \
static ::godot::String (::godot::Wrapped::*_get_to_string())() const { \
return (::godot::String(::godot::Wrapped::*)() const) & m_class::_to_string; \
} \
\
template <class T, class B> \
template <typename T, typename B> \
static void register_virtuals() { \
m_inherits::register_virtuals<T, B>(); \
} \
\
public: \
typedef m_class self_type; \
\
static void initialize_class() { \
static bool initialized = false; \
if (initialized) { \
@@ -179,11 +201,11 @@ public:
\
static void notification_bind(GDExtensionClassInstancePtr p_instance, int32_t p_what) { \
if (p_instance && m_class::_get_notification()) { \
m_inherits::notification_bind(p_instance, p_what); \
if (m_class::_get_notification() != m_inherits::_get_notification()) { \
m_class *cls = reinterpret_cast<m_class *>(p_instance); \
return cls->_notification(p_what); \
cls->_notification(p_what); \
} \
m_inherits::notification_bind(p_instance, p_what); \
} \
} \
\
@@ -303,82 +325,107 @@ public:
_gde_binding_create_callback, \
_gde_binding_free_callback, \
_gde_binding_reference_callback, \
};
}; \
\
private:
// Don't use this for your classes, use GDCLASS() instead.
#define GDEXTENSION_CLASS(m_class, m_inherits) \
private: \
void operator=(const m_class &p_rval) {} \
\
protected: \
virtual const GDExtensionInstanceBindingCallbacks *_get_bindings_callbacks() const override { \
return &_gde_binding_callbacks; \
} \
\
m_class(const char *p_godot_class) : m_inherits(p_godot_class) {} \
m_class(GodotObject *p_godot_object) : m_inherits(p_godot_object) {} \
\
static void (*_get_bind_methods())() { \
return nullptr; \
} \
\
static void (Wrapped::*_get_notification())(int) { \
return nullptr; \
} \
\
static bool (Wrapped::*_get_set())(const ::godot::StringName &p_name, const Variant &p_property) { \
return nullptr; \
} \
\
static bool (Wrapped::*_get_get())(const ::godot::StringName &p_name, Variant &r_ret) const { \
return nullptr; \
} \
\
static void (Wrapped::*_get_get_property_list())(List<PropertyInfo> * p_list) const { \
return nullptr; \
} \
\
static bool (Wrapped::*_get_property_can_revert())(const ::godot::StringName &p_name) { \
return nullptr; \
} \
\
static bool (Wrapped::*_get_property_get_revert())(const ::godot::StringName &p_name, Variant &) { \
return nullptr; \
} \
\
static String (Wrapped::*_get_to_string())() { \
return nullptr; \
} \
\
public: \
static void initialize_class() {} \
\
static ::godot::StringName &get_class_static() { \
static ::godot::StringName string_name = ::godot::StringName(#m_class); \
return string_name; \
} \
\
static ::godot::StringName &get_parent_class_static() { \
return m_inherits::get_class_static(); \
} \
\
static void *_gde_binding_create_callback(void *p_token, void *p_instance) { \
/* Do not call memnew here, we don't want the post-initializer to be called */ \
return new ("") m_class((GodotObject *)p_instance); \
} \
static void _gde_binding_free_callback(void *p_token, void *p_instance, void *p_binding) { \
/* Explicitly call the deconstructor to ensure proper lifecycle for non-trivial members */ \
reinterpret_cast<m_class *>(p_binding)->~m_class(); \
Memory::free_static(reinterpret_cast<m_class *>(p_binding)); \
} \
static GDExtensionBool _gde_binding_reference_callback(void *p_token, void *p_instance, GDExtensionBool p_reference) { \
return true; \
} \
static constexpr GDExtensionInstanceBindingCallbacks _gde_binding_callbacks = { \
_gde_binding_create_callback, \
_gde_binding_free_callback, \
_gde_binding_reference_callback, \
}; \
m_class() : m_class(#m_class) {}
#define GDEXTENSION_CLASS_ALIAS(m_class, m_alias_for, m_inherits) /******************************************************************************************************************/ \
private: \
inline static ::godot::internal::EngineClassRegistration<m_class> _gde_engine_class_registration_helper; \
void operator=(const m_class &p_rval) {} \
\
protected: \
virtual const GDExtensionInstanceBindingCallbacks *_get_bindings_callbacks() const override { \
return &_gde_binding_callbacks; \
} \
\
m_class(const char *p_godot_class) : m_inherits(p_godot_class) {} \
m_class(GodotObject *p_godot_object) : m_inherits(p_godot_object) {} \
\
static void (*_get_bind_methods())() { \
return nullptr; \
} \
\
static void (Wrapped::*_get_notification())(int) { \
return nullptr; \
} \
\
static bool (Wrapped::*_get_set())(const ::godot::StringName &p_name, const Variant &p_property) { \
return nullptr; \
} \
\
static bool (Wrapped::*_get_get())(const ::godot::StringName &p_name, Variant &r_ret) const { \
return nullptr; \
} \
\
static inline bool has_get_property_list() { \
return false; \
} \
\
static void (Wrapped::*_get_get_property_list())(List<PropertyInfo> * p_list) const { \
return nullptr; \
} \
\
static bool (Wrapped::*_get_property_can_revert())(const ::godot::StringName &p_name) const { \
return nullptr; \
} \
\
static bool (Wrapped::*_get_property_get_revert())(const ::godot::StringName &p_name, Variant &) const { \
return nullptr; \
} \
\
static void (Wrapped::*_get_validate_property())(::godot::PropertyInfo & p_property) const { \
return nullptr; \
} \
\
static String (Wrapped::*_get_to_string())() const { \
return nullptr; \
} \
\
public: \
typedef m_class self_type; \
\
static void initialize_class() {} \
\
static ::godot::StringName &get_class_static() { \
static ::godot::StringName string_name = ::godot::StringName(#m_alias_for); \
return string_name; \
} \
\
static ::godot::StringName &get_parent_class_static() { \
return m_inherits::get_class_static(); \
} \
\
static GDExtensionObjectPtr create(void *data) { \
return nullptr; \
} \
\
static void free(void *data, GDExtensionClassInstancePtr ptr) { \
} \
\
static void *_gde_binding_create_callback(void *p_token, void *p_instance) { \
/* Do not call memnew here, we don't want the post-initializer to be called */ \
return new ("") m_class((GodotObject *)p_instance); \
} \
static void _gde_binding_free_callback(void *p_token, void *p_instance, void *p_binding) { \
/* Explicitly call the deconstructor to ensure proper lifecycle for non-trivial members */ \
reinterpret_cast<m_class *>(p_binding)->~m_class(); \
Memory::free_static(reinterpret_cast<m_class *>(p_binding)); \
} \
static GDExtensionBool _gde_binding_reference_callback(void *p_token, void *p_instance, GDExtensionBool p_reference) { \
return true; \
} \
static constexpr GDExtensionInstanceBindingCallbacks _gde_binding_callbacks = { \
_gde_binding_create_callback, \
_gde_binding_free_callback, \
_gde_binding_reference_callback, \
}; \
m_class() : m_class(#m_alias_for) {} \
\
private:
// Don't use this for your classes, use GDCLASS() instead.
#define GDEXTENSION_CLASS(m_class, m_inherits) GDEXTENSION_CLASS_ALIAS(m_class, m_class, m_inherits)
#endif // GODOT_WRAPPED_HPP

View File

@@ -83,7 +83,7 @@ namespace godot {
}; \
}
template <class T>
template <typename T>
struct VariantCaster {
static _FORCE_INLINE_ T cast(const Variant &p_variant) {
using TStripped = std::remove_pointer_t<T>;
@@ -95,7 +95,7 @@ struct VariantCaster {
}
};
template <class T>
template <typename T>
struct VariantCaster<T &> {
static _FORCE_INLINE_ T cast(const Variant &p_variant) {
using TStripped = std::remove_pointer_t<T>;
@@ -107,7 +107,7 @@ struct VariantCaster<T &> {
}
};
template <class T>
template <typename T>
struct VariantCaster<const T &> {
static _FORCE_INLINE_ T cast(const Variant &p_variant) {
using TStripped = std::remove_pointer_t<T>;
@@ -144,7 +144,7 @@ struct VariantObjectClassChecker<const Ref<T> &> {
}
};
template <class T>
template <typename T>
struct VariantCasterAndValidate {
static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, GDExtensionCallError &r_error) {
GDExtensionVariantType argtype = GDExtensionVariantType(GetTypeInfo<T>::VARIANT_TYPE);
@@ -159,7 +159,7 @@ struct VariantCasterAndValidate {
}
};
template <class T>
template <typename T>
struct VariantCasterAndValidate<T &> {
static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, GDExtensionCallError &r_error) {
GDExtensionVariantType argtype = GDExtensionVariantType(GetTypeInfo<T>::VARIANT_TYPE);
@@ -174,7 +174,7 @@ struct VariantCasterAndValidate<T &> {
}
};
template <class T>
template <typename T>
struct VariantCasterAndValidate<const T &> {
static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, GDExtensionCallError &r_error) {
GDExtensionVariantType argtype = GDExtensionVariantType(GetTypeInfo<T>::VARIANT_TYPE);
@@ -189,47 +189,47 @@ struct VariantCasterAndValidate<const T &> {
}
};
template <class T, class... P, size_t... Is>
template <typename T, typename... P, size_t... Is>
void call_with_ptr_args_helper(T *p_instance, void (T::*p_method)(P...), const GDExtensionConstTypePtr *p_args, IndexSequence<Is...>) {
(p_instance->*p_method)(PtrToArg<P>::convert(p_args[Is])...);
}
template <class T, class... P, size_t... Is>
template <typename T, typename... P, size_t... Is>
void call_with_ptr_argsc_helper(T *p_instance, void (T::*p_method)(P...) const, const GDExtensionConstTypePtr *p_args, IndexSequence<Is...>) {
(p_instance->*p_method)(PtrToArg<P>::convert(p_args[Is])...);
}
template <class T, class R, class... P, size_t... Is>
template <typename T, typename R, typename... P, size_t... Is>
void call_with_ptr_args_ret_helper(T *p_instance, R (T::*p_method)(P...), const GDExtensionConstTypePtr *p_args, void *r_ret, IndexSequence<Is...>) {
PtrToArg<R>::encode((p_instance->*p_method)(PtrToArg<P>::convert(p_args[Is])...), r_ret);
}
template <class T, class R, class... P, size_t... Is>
template <typename T, typename R, typename... P, size_t... Is>
void call_with_ptr_args_retc_helper(T *p_instance, R (T::*p_method)(P...) const, const GDExtensionConstTypePtr *p_args, void *r_ret, IndexSequence<Is...>) {
PtrToArg<R>::encode((p_instance->*p_method)(PtrToArg<P>::convert(p_args[Is])...), r_ret);
}
template <class T, class... P>
template <typename T, typename... P>
void call_with_ptr_args(T *p_instance, void (T::*p_method)(P...), const GDExtensionConstTypePtr *p_args, void * /*ret*/) {
call_with_ptr_args_helper<T, P...>(p_instance, p_method, p_args, BuildIndexSequence<sizeof...(P)>{});
}
template <class T, class... P>
template <typename T, typename... P>
void call_with_ptr_args(T *p_instance, void (T::*p_method)(P...) const, const GDExtensionConstTypePtr *p_args, void * /*ret*/) {
call_with_ptr_argsc_helper<T, P...>(p_instance, p_method, p_args, BuildIndexSequence<sizeof...(P)>{});
}
template <class T, class R, class... P>
template <typename T, typename R, typename... P>
void call_with_ptr_args(T *p_instance, R (T::*p_method)(P...), const GDExtensionConstTypePtr *p_args, void *r_ret) {
call_with_ptr_args_ret_helper<T, R, P...>(p_instance, p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{});
}
template <class T, class R, class... P>
template <typename T, typename R, typename... P>
void call_with_ptr_args(T *p_instance, R (T::*p_method)(P...) const, const GDExtensionConstTypePtr *p_args, void *r_ret) {
call_with_ptr_args_retc_helper<T, R, P...>(p_instance, p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{});
}
template <class T, class... P, size_t... Is>
template <typename T, typename... P, size_t... Is>
void call_with_variant_args_helper(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, GDExtensionCallError &r_error, IndexSequence<Is...>) {
r_error.error = GDEXTENSION_CALL_OK;
@@ -241,7 +241,7 @@ void call_with_variant_args_helper(T *p_instance, void (T::*p_method)(P...), con
(void)(p_args); // Avoid warning.
}
template <class T, class... P, size_t... Is>
template <typename T, typename... P, size_t... Is>
void call_with_variant_argsc_helper(T *p_instance, void (T::*p_method)(P...) const, const Variant **p_args, GDExtensionCallError &r_error, IndexSequence<Is...>) {
r_error.error = GDEXTENSION_CALL_OK;
@@ -253,7 +253,7 @@ void call_with_variant_argsc_helper(T *p_instance, void (T::*p_method)(P...) con
(void)(p_args); // Avoid warning.
}
template <class T, class R, class... P, size_t... Is>
template <typename T, typename R, typename... P, size_t... Is>
void call_with_variant_args_ret_helper(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, Variant &r_ret, GDExtensionCallError &r_error, IndexSequence<Is...>) {
r_error.error = GDEXTENSION_CALL_OK;
@@ -264,7 +264,7 @@ void call_with_variant_args_ret_helper(T *p_instance, R (T::*p_method)(P...), co
#endif
}
template <class T, class R, class... P, size_t... Is>
template <typename T, typename R, typename... P, size_t... Is>
void call_with_variant_args_retc_helper(T *p_instance, R (T::*p_method)(P...) const, const Variant **p_args, Variant &r_ret, GDExtensionCallError &r_error, IndexSequence<Is...>) {
r_error.error = GDEXTENSION_CALL_OK;
@@ -276,7 +276,7 @@ void call_with_variant_args_retc_helper(T *p_instance, R (T::*p_method)(P...) co
(void)p_args;
}
template <class T, class... P>
template <typename T, typename... P>
void call_with_variant_args_dv(T *p_instance, void (T::*p_method)(P...), const GDExtensionConstVariantPtr *p_args, int p_argcount, GDExtensionCallError &r_error, const std::vector<Variant> &default_values) {
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
@@ -311,7 +311,7 @@ void call_with_variant_args_dv(T *p_instance, void (T::*p_method)(P...), const G
call_with_variant_args_helper(p_instance, p_method, argsp.data(), r_error, BuildIndexSequence<sizeof...(P)>{});
}
template <class T, class... P>
template <typename T, typename... P>
void call_with_variant_argsc_dv(T *p_instance, void (T::*p_method)(P...) const, const GDExtensionConstVariantPtr *p_args, int p_argcount, GDExtensionCallError &r_error, const std::vector<Variant> &default_values) {
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
@@ -346,7 +346,7 @@ void call_with_variant_argsc_dv(T *p_instance, void (T::*p_method)(P...) const,
call_with_variant_argsc_helper(p_instance, p_method, argsp.data(), r_error, BuildIndexSequence<sizeof...(P)>{});
}
template <class T, class R, class... P>
template <typename T, typename R, typename... P>
void call_with_variant_args_ret_dv(T *p_instance, R (T::*p_method)(P...), const GDExtensionConstVariantPtr *p_args, int p_argcount, Variant &r_ret, GDExtensionCallError &r_error, const std::vector<Variant> &default_values) {
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
@@ -381,7 +381,7 @@ void call_with_variant_args_ret_dv(T *p_instance, R (T::*p_method)(P...), const
call_with_variant_args_ret_helper(p_instance, p_method, argsp.data(), r_ret, r_error, BuildIndexSequence<sizeof...(P)>{});
}
template <class T, class R, class... P>
template <typename T, typename R, typename... P>
void call_with_variant_args_retc_dv(T *p_instance, R (T::*p_method)(P...) const, const GDExtensionConstVariantPtr *p_args, int p_argcount, Variant &r_ret, GDExtensionCallError &r_error, const std::vector<Variant> &default_values) {
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
@@ -423,7 +423,7 @@ void call_with_variant_args_retc_dv(T *p_instance, R (T::*p_method)(P...) const,
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#endif
template <class Q>
template <typename Q>
void call_get_argument_type_helper(int p_arg, int &index, GDExtensionVariantType &type) {
if (p_arg == index) {
type = GDExtensionVariantType(GetTypeInfo<Q>::VARIANT_TYPE);
@@ -431,7 +431,7 @@ void call_get_argument_type_helper(int p_arg, int &index, GDExtensionVariantType
index++;
}
template <class... P>
template <typename... P>
GDExtensionVariantType call_get_argument_type(int p_arg) {
GDExtensionVariantType type = GDEXTENSION_VARIANT_TYPE_NIL;
int index = 0;
@@ -443,7 +443,7 @@ GDExtensionVariantType call_get_argument_type(int p_arg) {
return type;
}
template <class Q>
template <typename Q>
void call_get_argument_type_info_helper(int p_arg, int &index, PropertyInfo &info) {
if (p_arg == index) {
info = GetTypeInfo<Q>::get_class_info();
@@ -451,7 +451,7 @@ void call_get_argument_type_info_helper(int p_arg, int &index, PropertyInfo &inf
index++;
}
template <class... P>
template <typename... P>
void call_get_argument_type_info(int p_arg, PropertyInfo &info) {
int index = 0;
// I think rocket science is simpler than modern C++.
@@ -461,7 +461,7 @@ void call_get_argument_type_info(int p_arg, PropertyInfo &info) {
(void)index; // Suppress GCC warning.
}
template <class Q>
template <typename Q>
void call_get_argument_metadata_helper(int p_arg, int &index, GDExtensionClassMethodArgumentMetadata &md) {
if (p_arg == index) {
md = GetTypeInfo<Q>::METADATA;
@@ -469,7 +469,7 @@ void call_get_argument_metadata_helper(int p_arg, int &index, GDExtensionClassMe
index++;
}
template <class... P>
template <typename... P>
GDExtensionClassMethodArgumentMetadata call_get_argument_metadata(int p_arg) {
GDExtensionClassMethodArgumentMetadata md = GDEXTENSION_METHOD_ARGUMENT_METADATA_NONE;
@@ -482,7 +482,7 @@ GDExtensionClassMethodArgumentMetadata call_get_argument_metadata(int p_arg) {
return md;
}
template <class... P, size_t... Is>
template <typename... P, size_t... Is>
void call_with_variant_args_static(void (*p_method)(P...), const Variant **p_args, GDExtensionCallError &r_error, IndexSequence<Is...>) {
r_error.error = GDEXTENSION_CALL_OK;
@@ -493,7 +493,7 @@ void call_with_variant_args_static(void (*p_method)(P...), const Variant **p_arg
#endif
}
template <class... P>
template <typename... P>
void call_with_variant_args_static_dv(void (*p_method)(P...), const GDExtensionConstVariantPtr *p_args, int p_argcount, GDExtensionCallError &r_error, const std::vector<Variant> &default_values) {
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
@@ -528,17 +528,17 @@ void call_with_variant_args_static_dv(void (*p_method)(P...), const GDExtensionC
call_with_variant_args_static(p_method, argsp.data(), r_error, BuildIndexSequence<sizeof...(P)>{});
}
template <class... P, size_t... Is>
template <typename... P, size_t... Is>
void call_with_ptr_args_static_method_helper(void (*p_method)(P...), const GDExtensionConstTypePtr *p_args, IndexSequence<Is...>) {
p_method(PtrToArg<P>::convert(p_args[Is])...);
}
template <class... P>
template <typename... P>
void call_with_ptr_args_static_method(void (*p_method)(P...), const GDExtensionConstTypePtr *p_args) {
call_with_ptr_args_static_method_helper<P...>(p_method, p_args, BuildIndexSequence<sizeof...(P)>{});
}
template <class R, class... P, size_t... Is>
template <typename R, typename... P, size_t... Is>
void call_with_variant_args_static_ret(R (*p_method)(P...), const Variant **p_args, Variant &r_ret, GDExtensionCallError &r_error, IndexSequence<Is...>) {
r_error.error = GDEXTENSION_CALL_OK;
@@ -549,7 +549,7 @@ void call_with_variant_args_static_ret(R (*p_method)(P...), const Variant **p_ar
#endif
}
template <class R, class... P>
template <typename R, typename... P>
void call_with_variant_args_static_ret_dv(R (*p_method)(P...), const GDExtensionConstVariantPtr *p_args, int p_argcount, Variant &r_ret, GDExtensionCallError &r_error, const std::vector<Variant> &default_values) {
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
@@ -584,12 +584,12 @@ void call_with_variant_args_static_ret_dv(R (*p_method)(P...), const GDExtension
call_with_variant_args_static_ret(p_method, argsp.data(), r_ret, r_error, BuildIndexSequence<sizeof...(P)>{});
}
template <class R, class... P, size_t... Is>
template <typename R, typename... P, size_t... Is>
void call_with_ptr_args_static_method_ret_helper(R (*p_method)(P...), const GDExtensionConstTypePtr *p_args, void *r_ret, IndexSequence<Is...>) {
PtrToArg<R>::encode(p_method(PtrToArg<P>::convert(p_args[Is])...), r_ret);
}
template <class R, class... P>
template <typename R, typename... P>
void call_with_ptr_args_static_method_ret(R (*p_method)(P...), const GDExtensionConstTypePtr *p_args, void *r_ret) {
call_with_ptr_args_static_method_ret_helper<R, P...>(p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{});
}

View File

@@ -32,6 +32,7 @@
#define GODOT_BUILTIN_PTRCALL_HPP
#include <gdextension_interface.h>
#include <godot_cpp/core/object.hpp>
#include <array>
@@ -39,13 +40,24 @@ namespace godot {
namespace internal {
template <class... Args>
template <typename O, typename... Args>
O *_call_builtin_method_ptr_ret_obj(const GDExtensionPtrBuiltInMethod method, GDExtensionTypePtr base, const Args &...args) {
GodotObject *ret = nullptr;
std::array<GDExtensionConstTypePtr, sizeof...(Args)> call_args = { { (GDExtensionConstTypePtr)args... } };
method(base, call_args.data(), &ret, sizeof...(Args));
if (ret == nullptr) {
return nullptr;
}
return reinterpret_cast<O *>(internal::get_object_instance_binding(ret));
}
template <typename... Args>
void _call_builtin_constructor(const GDExtensionPtrConstructor constructor, GDExtensionTypePtr base, Args... args) {
std::array<GDExtensionConstTypePtr, sizeof...(Args)> call_args = { { (GDExtensionConstTypePtr)args... } };
constructor(base, call_args.data());
}
template <class T, class... Args>
template <typename T, typename... Args>
T _call_builtin_method_ptr_ret(const GDExtensionPtrBuiltInMethod method, GDExtensionTypePtr base, Args... args) {
T ret;
std::array<GDExtensionConstTypePtr, sizeof...(Args)> call_args = { { (GDExtensionConstTypePtr)args... } };
@@ -53,20 +65,20 @@ T _call_builtin_method_ptr_ret(const GDExtensionPtrBuiltInMethod method, GDExten
return ret;
}
template <class... Args>
template <typename... Args>
void _call_builtin_method_ptr_no_ret(const GDExtensionPtrBuiltInMethod method, GDExtensionTypePtr base, Args... args) {
std::array<GDExtensionConstTypePtr, sizeof...(Args)> call_args = { { (GDExtensionConstTypePtr)args... } };
method(base, call_args.data(), nullptr, sizeof...(Args));
}
template <class T>
template <typename T>
T _call_builtin_operator_ptr(const GDExtensionPtrOperatorEvaluator op, GDExtensionConstTypePtr left, GDExtensionConstTypePtr right) {
T ret;
op(left, right, &ret);
return ret;
}
template <class T>
template <typename T>
T _call_builtin_ptr_getter(const GDExtensionPtrGetter getter, GDExtensionConstTypePtr base) {
T ret;
getter(base, &ret);

View File

@@ -38,6 +38,8 @@
#include <godot_cpp/core/method_bind.hpp>
#include <godot_cpp/core/object.hpp>
#include <godot_cpp/classes/class_db_singleton.hpp>
#include <list>
#include <set>
#include <string>
@@ -112,24 +114,26 @@ private:
static void initialize_class(const ClassInfo &cl);
static void bind_method_godot(const StringName &p_class_name, MethodBind *p_method);
template <class T, bool is_abstract>
template <typename T, bool is_abstract>
static void _register_class(bool p_virtual = false);
public:
template <class T>
template <typename T>
static void register_class(bool p_virtual = false);
template <class T>
template <typename T>
static void register_abstract_class();
template <class T>
static void register_engine_class();
template <class N, class M, typename... VarArgs>
_FORCE_INLINE_ static void _register_engine_class(const StringName &p_name, const GDExtensionInstanceBindingCallbacks *p_callbacks) {
instance_binding_callbacks[p_name] = p_callbacks;
}
template <typename N, typename M, typename... VarArgs>
static MethodBind *bind_method(N p_method_name, M p_method, VarArgs... p_args);
template <class N, class M, typename... VarArgs>
template <typename N, typename M, typename... VarArgs>
static MethodBind *bind_static_method(StringName p_class, N p_method_name, M p_method, VarArgs... p_args);
template <class M>
template <typename M>
static MethodBind *bind_vararg_method(uint32_t p_flags, StringName p_name, M p_method, const MethodInfo &p_info = MethodInfo(), const std::vector<Variant> &p_default_args = std::vector<Variant>{}, bool p_return_nil_is_variant = true);
static void add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix);
@@ -146,27 +150,30 @@ public:
static void initialize(GDExtensionInitializationLevel p_level);
static void deinitialize(GDExtensionInitializationLevel p_level);
CLASSDB_SINGLETON_FORWARD_METHODS;
};
#define BIND_CONSTANT(m_constant) \
godot::ClassDB::bind_integer_constant(get_class_static(), "", #m_constant, m_constant);
::godot::ClassDB::bind_integer_constant(get_class_static(), "", #m_constant, m_constant);
#define BIND_ENUM_CONSTANT(m_constant) \
godot::ClassDB::bind_integer_constant(get_class_static(), godot::_gde_constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant);
::godot::ClassDB::bind_integer_constant(get_class_static(), ::godot::_gde_constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant);
#define BIND_BITFIELD_FLAG(m_constant) \
godot::ClassDB::bind_integer_constant(get_class_static(), godot::_gde_constant_get_bitfield_name(m_constant, #m_constant), #m_constant, m_constant, true);
::godot::ClassDB::bind_integer_constant(get_class_static(), ::godot::_gde_constant_get_bitfield_name(m_constant, #m_constant), #m_constant, m_constant, true);
#define BIND_VIRTUAL_METHOD(m_class, m_method) \
{ \
auto _call##m_method = [](GDExtensionObjectPtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr p_ret) -> void { \
call_with_ptr_args(reinterpret_cast<m_class *>(p_instance), &m_class::m_method, p_args, p_ret); \
}; \
godot::ClassDB::bind_virtual_method(m_class::get_class_static(), #m_method, _call##m_method); \
::godot::ClassDB::bind_virtual_method(m_class::get_class_static(), #m_method, _call##m_method); \
}
template <class T, bool is_abstract>
template <typename T, bool is_abstract>
void ClassDB::_register_class(bool p_virtual) {
static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
instance_binding_callbacks[T::get_class_static()] = &T::_gde_binding_callbacks;
// Register this class within our plugin
@@ -212,22 +219,17 @@ void ClassDB::_register_class(bool p_virtual) {
initialize_class(classes[cl.name]);
}
template <class T>
template <typename T>
void ClassDB::register_class(bool p_virtual) {
ClassDB::_register_class<T, false>(p_virtual);
}
template <class T>
template <typename T>
void ClassDB::register_abstract_class() {
ClassDB::_register_class<T, true>();
}
template <class T>
void ClassDB::register_engine_class() {
instance_binding_callbacks[T::get_class_static()] = &T::_gde_binding_callbacks;
}
template <class N, class M, typename... VarArgs>
template <typename N, typename M, typename... VarArgs>
MethodBind *ClassDB::bind_method(N p_method_name, M p_method, VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
const Variant *argptrs[sizeof...(p_args) + 1];
@@ -238,7 +240,7 @@ MethodBind *ClassDB::bind_method(N p_method_name, M p_method, VarArgs... p_args)
return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const void **)argptrs, sizeof...(p_args));
}
template <class N, class M, typename... VarArgs>
template <typename N, typename M, typename... VarArgs>
MethodBind *ClassDB::bind_static_method(StringName p_class, N p_method_name, M p_method, VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
const Variant *argptrs[sizeof...(p_args) + 1];
@@ -250,10 +252,10 @@ MethodBind *ClassDB::bind_static_method(StringName p_class, N p_method_name, M p
return bind_methodfi(0, bind, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const void **)argptrs, sizeof...(p_args));
}
template <class M>
template <typename M>
MethodBind *ClassDB::bind_vararg_method(uint32_t p_flags, StringName p_name, M p_method, const MethodInfo &p_info, const std::vector<Variant> &p_default_args, bool p_return_nil_is_variant) {
MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant);
ERR_FAIL_COND_V(!bind, nullptr);
ERR_FAIL_NULL_V(bind, nullptr);
bind->set_name(p_name);
bind->set_default_arguments(p_default_args);

View File

@@ -108,7 +108,7 @@ typedef float real_t;
// Generic swap template.
#ifndef SWAP
#define SWAP(m_x, m_y) __swap_tmpl((m_x), (m_y))
template <class T>
template <typename T>
inline void __swap_tmpl(T &x, T &y) {
T aux = x;
x = y;

View File

@@ -43,7 +43,7 @@ namespace godot {
namespace internal {
template <class O, class... Args>
template <typename O, typename... Args>
O *_call_native_mb_ret_obj(const GDExtensionMethodBindPtr mb, void *instance, const Args &...args) {
GodotObject *ret = nullptr;
std::array<GDExtensionConstTypePtr, sizeof...(Args)> mb_args = { { (GDExtensionConstTypePtr)args... } };
@@ -54,7 +54,7 @@ O *_call_native_mb_ret_obj(const GDExtensionMethodBindPtr mb, void *instance, co
return reinterpret_cast<O *>(internal::get_object_instance_binding(ret));
}
template <class R, class... Args>
template <typename R, typename... Args>
R _call_native_mb_ret(const GDExtensionMethodBindPtr mb, void *instance, const Args &...args) {
R ret;
std::array<GDExtensionConstTypePtr, sizeof...(Args)> mb_args = { { (GDExtensionConstTypePtr)args... } };
@@ -62,13 +62,13 @@ R _call_native_mb_ret(const GDExtensionMethodBindPtr mb, void *instance, const A
return ret;
}
template <class... Args>
template <typename... Args>
void _call_native_mb_no_ret(const GDExtensionMethodBindPtr mb, void *instance, const Args &...args) {
std::array<GDExtensionConstTypePtr, sizeof...(Args)> mb_args = { { (GDExtensionConstTypePtr)args... } };
internal::gdextension_interface_object_method_bind_ptrcall(mb, instance, mb_args.data(), nullptr);
}
template <class R, class... Args>
template <typename R, typename... Args>
R _call_utility_ret(GDExtensionPtrUtilityFunction func, const Args &...args) {
R ret;
std::array<GDExtensionConstTypePtr, sizeof...(Args)> mb_args = { { (GDExtensionConstTypePtr)args... } };
@@ -76,15 +76,15 @@ R _call_utility_ret(GDExtensionPtrUtilityFunction func, const Args &...args) {
return ret;
}
template <class... Args>
Object *_call_utility_ret_obj(const GDExtensionPtrUtilityFunction func, void *instance, const Args &...args) {
template <typename... Args>
Object *_call_utility_ret_obj(const GDExtensionPtrUtilityFunction func, const Args &...args) {
GodotObject *ret = nullptr;
std::array<GDExtensionConstTypePtr, sizeof...(Args)> mb_args = { { (GDExtensionConstTypePtr)args... } };
func(&ret, mb_args.data(), mb_args.size());
return (Object *)internal::get_object_instance_binding(ret);
}
template <class... Args>
template <typename... Args>
void _call_utility_no_ret(const GDExtensionPtrUtilityFunction func, const Args &...args) {
std::array<GDExtensionConstTypePtr, sizeof...(Args)> mb_args = { { (GDExtensionConstTypePtr)args... } };
func(nullptr, mb_args.data(), mb_args.size());

View File

@@ -84,7 +84,7 @@ constexpr auto CLAMP(const T m_a, const T2 m_min, const T3 m_max) {
// Generic swap template.
#ifndef SWAP
#define SWAP(m_x, m_y) __swap_tmpl((m_x), (m_y))
template <class T>
template <typename T>
inline void __swap_tmpl(T &x, T &y) {
T aux = x;
x = y;
@@ -138,7 +138,7 @@ static inline int get_shift_from_power_of_2(unsigned int p_bits) {
return -1;
}
template <class T>
template <typename T>
static _FORCE_INLINE_ T nearest_power_of_2_templated(T x) {
--x;

View File

@@ -40,10 +40,6 @@
#include <type_traits>
#ifndef PAD_ALIGN
#define PAD_ALIGN 16 //must always be greater than this at much
#endif
void *operator new(size_t p_size, const char *p_description); ///< operator new that takes a description and uses MemoryStaticPool
void *operator new(size_t p_size, void *(*p_allocfunc)(size_t p_size)); ///< operator new that takes a description and uses MemoryStaticPool
void *operator new(size_t p_size, void *p_pointer, size_t check, const char *p_description); ///< operator new that takes a description and uses a pointer to the preallocated memory
@@ -68,6 +64,18 @@ class Memory {
Memory();
public:
// Alignment: ↓ max_align_t ↓ uint64_t ↓ max_align_t
// ┌─────────────────┬──┬────────────────┬──┬───────────...
// │ uint64_t │░░│ uint64_t │░░│ T[]
// │ alloc size │░░│ element count │░░│ data
// └─────────────────┴──┴────────────────┴──┴───────────...
// Offset: ↑ SIZE_OFFSET ↑ ELEMENT_OFFSET ↑ DATA_OFFSET
// Note: "alloc size" is used and set by the engine and is never accessed or changed for the extension.
static constexpr size_t SIZE_OFFSET = 0;
static constexpr size_t ELEMENT_OFFSET = ((SIZE_OFFSET + sizeof(uint64_t)) % alignof(uint64_t) == 0) ? (SIZE_OFFSET + sizeof(uint64_t)) : ((SIZE_OFFSET + sizeof(uint64_t)) + alignof(uint64_t) - ((SIZE_OFFSET + sizeof(uint64_t)) % alignof(uint64_t)));
static constexpr size_t DATA_OFFSET = ((ELEMENT_OFFSET + sizeof(uint64_t)) % alignof(max_align_t) == 0) ? (ELEMENT_OFFSET + sizeof(uint64_t)) : ((ELEMENT_OFFSET + sizeof(uint64_t)) + alignof(max_align_t) - ((ELEMENT_OFFSET + sizeof(uint64_t)) % alignof(max_align_t)));
static void *alloc_static(size_t p_bytes, bool p_pad_align = false);
static void *realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align = false);
static void free_static(void *p_ptr, bool p_pad_align = false);
@@ -75,7 +83,7 @@ public:
_ALWAYS_INLINE_ void postinitialize_handler(void *) {}
template <class T>
template <typename T>
_ALWAYS_INLINE_ T *_post_initialize(T *p_obj) {
postinitialize_handler(p_obj);
return p_obj;
@@ -91,28 +99,28 @@ _ALWAYS_INLINE_ T *_post_initialize(T *p_obj) {
#define memnew_placement(m_placement, m_class) ::godot::_post_initialize(new (m_placement, sizeof(m_class), "") m_class)
// Generic comparator used in Map, List, etc.
template <class T>
template <typename T>
struct Comparator {
_ALWAYS_INLINE_ bool operator()(const T &p_a, const T &p_b) const { return (p_a < p_b); }
};
template <class T>
template <typename T>
void memdelete(T *p_class, typename std::enable_if<!std::is_base_of_v<godot::Wrapped, T>>::type * = nullptr) {
if (!std::is_trivially_destructible<T>::value) {
if constexpr (!std::is_trivially_destructible_v<T>) {
p_class->~T();
}
Memory::free_static(p_class);
}
template <class T, std::enable_if_t<std::is_base_of_v<godot::Wrapped, T>, bool> = true>
template <typename T, std::enable_if_t<std::is_base_of_v<godot::Wrapped, T>, bool> = true>
void memdelete(T *p_class) {
godot::internal::gdextension_interface_object_destroy(p_class->_owner);
}
template <class T, class A>
template <typename T, typename A>
void memdelete_allocator(T *p_class) {
if (!std::is_trivially_destructible<T>::value) {
if constexpr (!std::is_trivially_destructible_v<T>) {
p_class->~T();
}
@@ -125,16 +133,20 @@ public:
_ALWAYS_INLINE_ static void free(void *p_ptr) { Memory::free_static(p_ptr); }
};
template <class T>
template <typename T>
class DefaultTypedAllocator {
public:
template <class... Args>
template <typename... Args>
_ALWAYS_INLINE_ T *new_allocation(const Args &&...p_args) { return memnew(T(p_args...)); }
_ALWAYS_INLINE_ void delete_allocation(T *p_allocation) { memdelete(p_allocation); }
};
#define memnew_arr(m_class, m_count) memnew_arr_template<m_class>(m_count)
_FORCE_INLINE_ uint64_t *_get_element_count_ptr(uint8_t *p_ptr) {
return (uint64_t *)(p_ptr - Memory::DATA_OFFSET + Memory::ELEMENT_OFFSET);
}
template <typename T>
T *memnew_arr_template(size_t p_elements, const char *p_descr = "") {
if (p_elements == 0) {
@@ -144,12 +156,14 @@ T *memnew_arr_template(size_t p_elements, const char *p_descr = "") {
same strategy used by std::vector, and the Vector class, so it should be safe.*/
size_t len = sizeof(T) * p_elements;
uint64_t *mem = (uint64_t *)Memory::alloc_static(len, true);
uint8_t *mem = (uint8_t *)Memory::alloc_static(len, true);
T *failptr = nullptr; // Get rid of a warning.
ERR_FAIL_COND_V(!mem, failptr);
*(mem - 1) = p_elements;
ERR_FAIL_NULL_V(mem, failptr);
if (!std::is_trivially_destructible<T>::value) {
uint64_t *_elem_count_ptr = _get_element_count_ptr(mem);
*(_elem_count_ptr) = p_elements;
if constexpr (!std::is_trivially_destructible_v<T>) {
T *elems = (T *)mem;
/* call operator new */
@@ -162,11 +176,19 @@ T *memnew_arr_template(size_t p_elements, const char *p_descr = "") {
}
template <typename T>
void memdelete_arr(T *p_class) {
uint64_t *ptr = (uint64_t *)p_class;
size_t memarr_len(const T *p_class) {
uint8_t *ptr = (uint8_t *)p_class;
uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr);
return *(_elem_count_ptr);
}
if (!std::is_trivially_destructible<T>::value) {
uint64_t elem_count = *(ptr - 1);
template <typename T>
void memdelete_arr(T *p_class) {
uint8_t *ptr = (uint8_t *)p_class;
if constexpr (!std::is_trivially_destructible_v<T>) {
uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr);
uint64_t elem_count = *(_elem_count_ptr);
for (uint64_t i = 0; i < elem_count; i++) {
p_class[i].~T();

View File

@@ -147,7 +147,7 @@ public:
virtual ~MethodBind();
};
template <class Derived, class T, class R, bool should_returns>
template <typename Derived, typename T, typename R, bool should_returns>
class MethodBindVarArgBase : public MethodBind {
protected:
R(T::*method)
@@ -208,7 +208,7 @@ private:
}
};
template <class T>
template <typename T>
class MethodBindVarArgT : public MethodBindVarArgBase<MethodBindVarArgT<T>, T, void, false> {
friend class MethodBindVarArgBase<MethodBindVarArgT<T>, T, void, false>;
@@ -231,14 +231,14 @@ private:
}
};
template <class T>
template <typename T>
MethodBind *create_vararg_method_bind(void (T::*p_method)(const Variant **, GDExtensionInt, GDExtensionCallError &), const MethodInfo &p_info, bool p_return_nil_is_variant) {
MethodBind *a = memnew((MethodBindVarArgT<T>)(p_method, p_info, p_return_nil_is_variant));
a->set_instance_class(T::get_class_static());
return a;
}
template <class T, class R>
template <typename T, typename R>
class MethodBindVarArgTR : public MethodBindVarArgBase<MethodBindVarArgTR<T, R>, T, R, true> {
friend class MethodBindVarArgBase<MethodBindVarArgTR<T, R>, T, R, true>;
@@ -260,7 +260,7 @@ private:
}
};
template <class T, class R>
template <typename T, typename R>
MethodBind *create_vararg_method_bind(R (T::*p_method)(const Variant **, GDExtensionInt, GDExtensionCallError &), const MethodInfo &p_info, bool p_return_nil_is_variant) {
MethodBind *a = memnew((MethodBindVarArgTR<T, R>)(p_method, p_info, p_return_nil_is_variant));
a->set_instance_class(T::get_class_static());
@@ -277,9 +277,9 @@ class _gde_UnexistingClass;
// No return, not const.
#ifdef TYPED_METHOD_BIND
template <class T, class... P>
template <typename T, typename... P>
#else
template <class... P>
template <typename... P>
#endif // TYPED_METHOD_BIND
class MethodBindT : public MethodBind {
void (MB_T::*method)(P...);
@@ -339,7 +339,7 @@ public:
}
};
template <class T, class... P>
template <typename T, typename... P>
MethodBind *create_method_bind(void (T::*p_method)(P...)) {
#ifdef TYPED_METHOD_BIND
MethodBind *a = memnew((MethodBindT<T, P...>)(p_method));
@@ -353,9 +353,9 @@ MethodBind *create_method_bind(void (T::*p_method)(P...)) {
// No return, const.
#ifdef TYPED_METHOD_BIND
template <class T, class... P>
template <typename T, typename... P>
#else
template <class... P>
template <typename... P>
#endif // TYPED_METHOD_BIND
class MethodBindTC : public MethodBind {
void (MB_T::*method)(P...) const;
@@ -415,7 +415,7 @@ public:
}
};
template <class T, class... P>
template <typename T, typename... P>
MethodBind *create_method_bind(void (T::*p_method)(P...) const) {
#ifdef TYPED_METHOD_BIND
MethodBind *a = memnew((MethodBindTC<T, P...>)(p_method));
@@ -429,9 +429,9 @@ MethodBind *create_method_bind(void (T::*p_method)(P...) const) {
// Return, not const.
#ifdef TYPED_METHOD_BIND
template <class T, class R, class... P>
template <typename T, typename R, typename... P>
#else
template <class R, class... P>
template <typename R, typename... P>
#endif // TYPED_METHOD_BIND
class MethodBindTR : public MethodBind {
R(MB_T::*method)
@@ -498,7 +498,7 @@ public:
}
};
template <class T, class R, class... P>
template <typename T, typename R, typename... P>
MethodBind *create_method_bind(R (T::*p_method)(P...)) {
#ifdef TYPED_METHOD_BIND
MethodBind *a = memnew((MethodBindTR<T, R, P...>)(p_method));
@@ -512,9 +512,9 @@ MethodBind *create_method_bind(R (T::*p_method)(P...)) {
// Return, const.
#ifdef TYPED_METHOD_BIND
template <class T, class R, class... P>
template <typename T, typename R, typename... P>
#else
template <class R, class... P>
template <typename R, typename... P>
#endif // TYPED_METHOD_BIND
class MethodBindTRC : public MethodBind {
R(MB_T::*method)
@@ -581,7 +581,7 @@ public:
}
};
template <class T, class R, class... P>
template <typename T, typename R, typename... P>
MethodBind *create_method_bind(R (T::*p_method)(P...) const) {
#ifdef TYPED_METHOD_BIND
MethodBind *a = memnew((MethodBindTRC<T, R, P...>)(p_method));
@@ -596,7 +596,7 @@ MethodBind *create_method_bind(R (T::*p_method)(P...) const) {
// no return
template <class... P>
template <typename... P>
class MethodBindTS : public MethodBind {
void (*function)(P...);
@@ -652,7 +652,7 @@ public:
}
};
template <class... P>
template <typename... P>
MethodBind *create_static_method_bind(void (*p_method)(P...)) {
MethodBind *a = memnew((MethodBindTS<P...>)(p_method));
return a;
@@ -660,7 +660,7 @@ MethodBind *create_static_method_bind(void (*p_method)(P...)) {
// return
template <class R, class... P>
template <typename R, typename... P>
class MethodBindTRS : public MethodBind {
R(*function)
(P...);
@@ -722,7 +722,7 @@ public:
}
};
template <class R, class... P>
template <typename R, typename... P>
MethodBind *create_static_method_bind(R (*p_method)(P...)) {
MethodBind *a = memnew((MethodBindTRS<R, P...>)(p_method));
return a;

View File

@@ -39,7 +39,7 @@
namespace godot {
template <class T>
template <typename T>
struct PtrToArg {};
#define MAKE_PTRARG(m_type) \
@@ -166,8 +166,9 @@ MAKE_PTRARG_BY_REFERENCE(Variant);
// This is for Object.
template <class T>
template <typename T>
struct PtrToArg<T *> {
static_assert(std::is_base_of<Object, T>::value, "Cannot encode non-Object value as an Object");
_FORCE_INLINE_ static T *convert(const void *p_ptr) {
return reinterpret_cast<T *>(godot::internal::get_object_instance_binding(*reinterpret_cast<GDExtensionObjectPtr *>(const_cast<void *>(p_ptr))));
}
@@ -177,8 +178,9 @@ struct PtrToArg<T *> {
}
};
template <class T>
template <typename T>
struct PtrToArg<const T *> {
static_assert(std::is_base_of<Object, T>::value, "Cannot encode non-Object value as an Object");
_FORCE_INLINE_ static const T *convert(const void *p_ptr) {
return reinterpret_cast<const T *>(godot::internal::get_object_instance_binding(*reinterpret_cast<GDExtensionObjectPtr *>(const_cast<void *>(p_ptr))));
}

View File

@@ -45,10 +45,10 @@
#include <vector>
#define ADD_SIGNAL(m_signal) godot::ClassDB::add_signal(get_class_static(), m_signal)
#define ADD_GROUP(m_name, m_prefix) godot::ClassDB::add_property_group(get_class_static(), m_name, m_prefix)
#define ADD_SUBGROUP(m_name, m_prefix) godot::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix)
#define ADD_PROPERTY(m_property, m_setter, m_getter) godot::ClassDB::add_property(get_class_static(), m_property, m_setter, m_getter)
#define ADD_SIGNAL(m_signal) ::godot::ClassDB::add_signal(get_class_static(), m_signal)
#define ADD_GROUP(m_name, m_prefix) ::godot::ClassDB::add_property_group(get_class_static(), m_name, m_prefix)
#define ADD_SUBGROUP(m_name, m_prefix) ::godot::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix)
#define ADD_PROPERTY(m_property, m_setter, m_getter) ::godot::ClassDB::add_property(get_class_static(), m_property, m_setter, m_getter)
namespace godot {
@@ -75,31 +75,31 @@ struct MethodInfo {
MethodInfo();
MethodInfo(StringName p_name);
template <class... Args>
template <typename... Args>
MethodInfo(StringName p_name, const Args &...args);
MethodInfo(Variant::Type ret);
MethodInfo(Variant::Type ret, StringName p_name);
template <class... Args>
template <typename... Args>
MethodInfo(Variant::Type ret, StringName p_name, const Args &...args);
MethodInfo(const PropertyInfo &p_ret, StringName p_name);
template <class... Args>
template <typename... Args>
MethodInfo(const PropertyInfo &p_ret, StringName p_name, const Args &...);
};
template <class... Args>
template <typename... Args>
MethodInfo::MethodInfo(StringName p_name, const Args &...args) :
name(p_name), flags(GDEXTENSION_METHOD_FLAG_NORMAL) {
arguments = { args... };
}
template <class... Args>
template <typename... Args>
MethodInfo::MethodInfo(Variant::Type ret, StringName p_name, const Args &...args) :
name(p_name), flags(GDEXTENSION_METHOD_FLAG_NORMAL) {
return_val.type = ret;
arguments = { args... };
}
template <class... Args>
template <typename... Args>
MethodInfo::MethodInfo(const PropertyInfo &p_ret, StringName p_name, const Args &...args) :
name(p_name), return_val(p_ret), flags(GDEXTENSION_METHOD_FLAG_NORMAL) {
arguments = { args... };
@@ -138,7 +138,7 @@ public:
}
};
template <class T>
template <typename T>
T *Object::cast_to(Object *p_object) {
if (p_object == nullptr) {
return nullptr;
@@ -151,7 +151,7 @@ T *Object::cast_to(Object *p_object) {
return dynamic_cast<T *>(internal::get_object_instance_binding(casted));
}
template <class T>
template <typename T>
const T *Object::cast_to(const Object *p_object) {
if (p_object == nullptr) {
return nullptr;

View File

@@ -47,9 +47,9 @@ struct PropertyInfo {
Variant::Type type = Variant::NIL;
StringName name;
StringName class_name;
uint32_t hint = 0;
uint32_t hint = PROPERTY_HINT_NONE;
String hint_string;
uint32_t usage = 7;
uint32_t usage = PROPERTY_USAGE_DEFAULT;
PropertyInfo() = default;

View File

@@ -90,7 +90,7 @@ static PropertyInfo make_property_info(Variant::Type p_type, const StringName &p
// instead of a forward declaration. You can always forward declare 'T' in a header file, and then
// include the actual declaration of 'T' in the source file where 'GetTypeInfo<T>' is instantiated.
template <class T, typename = void>
template <typename T, typename = void>
struct GetTypeInfo;
#define MAKE_TYPE_INFO(m_type, m_var_type) \
@@ -248,14 +248,14 @@ inline StringName _gde_constant_get_enum_name(T param, StringName p_constant) {
return GetTypeInfo<T>::get_class_info().class_name;
}
template <class T>
template <typename T>
class BitField {
int64_t value = 0;
public:
_FORCE_INLINE_ void set_flag(T p_flag) { value |= p_flag; }
_FORCE_INLINE_ bool has_flag(T p_flag) const { return value & p_flag; }
_FORCE_INLINE_ void clear_flag(T p_flag) { return value &= ~p_flag; }
_FORCE_INLINE_ void clear_flag(T p_flag) { value &= ~p_flag; }
_FORCE_INLINE_ BitField(int64_t p_value) { value = p_value; }
_FORCE_INLINE_ operator int64_t() const { return value; }
_FORCE_INLINE_ operator Variant() const { return value; }
@@ -295,7 +295,7 @@ inline StringName _gde_constant_get_bitfield_name(T param, StringName p_constant
return GetTypeInfo<BitField<T>>::get_class_info().class_name;
}
template <class T>
template <typename T>
struct PtrToArg<TypedArray<T>> {
_FORCE_INLINE_ static TypedArray<T> convert(const void *p_ptr) {
return TypedArray<T>(*reinterpret_cast<const Array *>(p_ptr));
@@ -306,7 +306,7 @@ struct PtrToArg<TypedArray<T>> {
}
};
template <class T>
template <typename T>
struct PtrToArg<const TypedArray<T> &> {
typedef Array EncodeT;
_FORCE_INLINE_ static TypedArray<T>

View File

@@ -187,29 +187,44 @@ enum ModuleInitializationLevel {
MODULE_INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
MODULE_INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS,
MODULE_INITIALIZATION_LEVEL_SCENE = GDEXTENSION_INITIALIZATION_SCENE,
MODULE_INITIALIZATION_LEVEL_EDITOR = GDEXTENSION_INITIALIZATION_EDITOR
MODULE_INITIALIZATION_LEVEL_EDITOR = GDEXTENSION_INITIALIZATION_EDITOR,
MODULE_INITIALIZATION_LEVEL_MAX
};
class GDExtensionBinding {
private:
static void register_engine_classes();
public:
using Callback = void (*)(ModuleInitializationLevel p_level);
static Callback init_callback;
static Callback terminate_callback;
static GDExtensionInitializationLevel minimum_initialization_level;
static GDExtensionBool init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization);
struct InitData {
GDExtensionInitializationLevel minimum_initialization_level = GDEXTENSION_INITIALIZATION_CORE;
Callback init_callback = nullptr;
Callback terminate_callback = nullptr;
};
class InitDataList {
int data_count = 0;
int data_capacity = 0;
InitData **data = nullptr;
public:
void add(InitData *p_cb);
~InitDataList();
};
static bool api_initialized;
static int level_initialized[MODULE_INITIALIZATION_LEVEL_MAX];
static InitDataList initdata;
static GDExtensionBool init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, InitData *p_init_data, GDExtensionInitialization *r_initialization);
public:
static void initialize_level(void *userdata, GDExtensionInitializationLevel p_level);
static void deinitialize_level(void *userdata, GDExtensionInitializationLevel p_level);
static void initialize_level(void *p_userdata, GDExtensionInitializationLevel p_level);
static void deinitialize_level(void *p_userdata, GDExtensionInitializationLevel p_level);
class InitObject {
GDExtensionInterfaceGetProcAddress get_proc_address;
GDExtensionClassLibraryPtr library;
GDExtensionInitialization *initialization;
mutable InitData *init_data = nullptr;
public:
InitObject(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization);

View File

@@ -32,108 +32,160 @@
#define GODOT_COWDATA_HPP
#include <godot_cpp/classes/global_constants.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/error_macros.hpp>
#include <godot_cpp/core/math.hpp>
#include <godot_cpp/core/memory.hpp>
#include <godot_cpp/templates/safe_refcount.hpp>
#include <cstring>
#include <new>
#include <type_traits>
namespace godot {
template <class T>
template <typename T>
class Vector;
template <class T, class V>
template <typename T, typename V>
class VMap;
template <typename T>
class CharStringT;
static_assert(std::is_trivially_destructible_v<std::atomic<uint64_t>>);
// Silence a false positive warning (see GH-52119).
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wplacement-new"
#endif
template <class T>
template <typename T>
class CowData {
template <class TV>
template <typename TV>
friend class Vector;
template <class TV, class VV>
template <typename TV, typename VV>
friend class VMap;
template <typename TS>
friend class CharStringT;
public:
typedef int64_t Size;
typedef uint64_t USize;
static constexpr USize MAX_INT = INT64_MAX;
private:
// Function to find the next power of 2 to an integer.
static _FORCE_INLINE_ USize next_po2(USize x) {
if (x == 0) {
return 0;
}
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
if (sizeof(USize) == 8) {
x |= x >> 32;
}
return ++x;
}
// Alignment: ↓ max_align_t ↓ USize ↓ max_align_t
// ┌────────────────────┬──┬─────────────┬──┬───────────...
// │ SafeNumeric<USize> │░░│ USize │░░│ T[]
// │ ref. count │░░│ data size │░░│ data
// └────────────────────┴──┴─────────────┴──┴───────────...
// Offset: ↑ REF_COUNT_OFFSET ↑ SIZE_OFFSET ↑ DATA_OFFSET
static constexpr size_t REF_COUNT_OFFSET = 0;
static constexpr size_t SIZE_OFFSET = ((REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) % alignof(USize) == 0) ? (REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) : ((REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) + alignof(USize) - ((REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) % alignof(USize)));
static constexpr size_t DATA_OFFSET = ((SIZE_OFFSET + sizeof(USize)) % alignof(max_align_t) == 0) ? (SIZE_OFFSET + sizeof(USize)) : ((SIZE_OFFSET + sizeof(USize)) + alignof(max_align_t) - ((SIZE_OFFSET + sizeof(USize)) % alignof(max_align_t)));
mutable T *_ptr = nullptr;
// internal helpers
_FORCE_INLINE_ SafeNumeric<uint32_t> *_get_refcount() const {
static _FORCE_INLINE_ SafeNumeric<USize> *_get_refcount_ptr(uint8_t *p_ptr) {
return (SafeNumeric<USize> *)(p_ptr + REF_COUNT_OFFSET);
}
static _FORCE_INLINE_ USize *_get_size_ptr(uint8_t *p_ptr) {
return (USize *)(p_ptr + SIZE_OFFSET);
}
static _FORCE_INLINE_ T *_get_data_ptr(uint8_t *p_ptr) {
return (T *)(p_ptr + DATA_OFFSET);
}
_FORCE_INLINE_ SafeNumeric<USize> *_get_refcount() const {
if (!_ptr) {
return nullptr;
}
return reinterpret_cast<SafeNumeric<uint32_t> *>(_ptr) - 2;
return (SafeNumeric<USize> *)((uint8_t *)_ptr - DATA_OFFSET + REF_COUNT_OFFSET);
}
_FORCE_INLINE_ uint32_t *_get_size() const {
_FORCE_INLINE_ USize *_get_size() const {
if (!_ptr) {
return nullptr;
}
return reinterpret_cast<uint32_t *>(_ptr) - 1;
return (USize *)((uint8_t *)_ptr - DATA_OFFSET + SIZE_OFFSET);
}
_FORCE_INLINE_ T *_get_data() const {
if (!_ptr) {
return nullptr;
_FORCE_INLINE_ USize _get_alloc_size(USize p_elements) const {
return next_po2(p_elements * sizeof(T));
}
_FORCE_INLINE_ bool _get_alloc_size_checked(USize p_elements, USize *out) const {
if (unlikely(p_elements == 0)) {
*out = 0;
return true;
}
return reinterpret_cast<T *>(_ptr);
}
_FORCE_INLINE_ size_t _get_alloc_size(size_t p_elements) const {
return next_power_of_2(p_elements * sizeof(T));
}
_FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const {
#if defined(__GNUC__)
size_t o;
size_t p;
#if defined(__GNUC__) && defined(IS_32_BIT)
USize o;
USize p;
if (__builtin_mul_overflow(p_elements, sizeof(T), &o)) {
*out = 0;
return false;
}
*out = next_power_of_2(o);
if (__builtin_add_overflow(o, static_cast<size_t>(32), &p)) {
*out = next_po2(o);
if (__builtin_add_overflow(o, static_cast<USize>(32), &p)) {
return false; // No longer allocated here.
}
return true;
#else
// Speed is more important than correctness here, do the operations unchecked
// and hope for the best.
*out = _get_alloc_size(p_elements);
return true;
#endif
return *out;
}
void _unref(void *p_data);
void _ref(const CowData *p_from);
void _ref(const CowData &p_from);
uint32_t _copy_on_write();
USize _copy_on_write();
public:
void operator=(const CowData<T> &p_from) { _ref(p_from); }
_FORCE_INLINE_ T *ptrw() {
_copy_on_write();
return (T *)_get_data();
return _ptr;
}
_FORCE_INLINE_ const T *ptr() const {
return _get_data();
return _ptr;
}
_FORCE_INLINE_ int size() const {
uint32_t *size = (uint32_t *)_get_size();
_FORCE_INLINE_ Size size() const {
USize *size = (USize *)_get_size();
if (size) {
return *size;
} else {
@@ -144,41 +196,42 @@ public:
_FORCE_INLINE_ void clear() { resize(0); }
_FORCE_INLINE_ bool is_empty() const { return _ptr == nullptr; }
_FORCE_INLINE_ void set(int p_index, const T &p_elem) {
_FORCE_INLINE_ void set(Size p_index, const T &p_elem) {
ERR_FAIL_INDEX(p_index, size());
_copy_on_write();
_get_data()[p_index] = p_elem;
_ptr[p_index] = p_elem;
}
_FORCE_INLINE_ T &get_m(int p_index) {
_FORCE_INLINE_ T &get_m(Size p_index) {
CRASH_BAD_INDEX(p_index, size());
_copy_on_write();
return _get_data()[p_index];
return _ptr[p_index];
}
_FORCE_INLINE_ const T &get(int p_index) const {
_FORCE_INLINE_ const T &get(Size p_index) const {
CRASH_BAD_INDEX(p_index, size());
return _get_data()[p_index];
return _ptr[p_index];
}
Error resize(int p_size);
template <bool p_ensure_zero = false>
Error resize(Size p_size);
_FORCE_INLINE_ void remove_at(int p_index) {
_FORCE_INLINE_ void remove_at(Size p_index) {
ERR_FAIL_INDEX(p_index, size());
T *p = ptrw();
int len = size();
for (int i = p_index; i < len - 1; i++) {
Size len = size();
for (Size i = p_index; i < len - 1; i++) {
p[i] = p[i + 1];
}
resize(len - 1);
}
Error insert(int p_pos, const T &p_val) {
Error insert(Size p_pos, const T &p_val) {
ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER);
resize(size() + 1);
for (int i = (size() - 1); i > p_pos; i--) {
for (Size i = (size() - 1); i > p_pos; i--) {
set(i, get(i - 1));
}
set(p_pos, p_val);
@@ -186,83 +239,88 @@ public:
return OK;
}
int find(const T &p_val, int p_from = 0) const;
Size find(const T &p_val, Size p_from = 0) const;
Size rfind(const T &p_val, Size p_from = -1) const;
Size count(const T &p_val) const;
_FORCE_INLINE_ CowData() {}
_FORCE_INLINE_ ~CowData();
_FORCE_INLINE_ CowData(CowData<T> &p_from) { _ref(p_from); }
_FORCE_INLINE_ CowData(CowData<T> &p_from) { _ref(p_from); };
};
template <class T>
template <typename T>
void CowData<T>::_unref(void *p_data) {
if (!p_data) {
return;
}
SafeNumeric<uint32_t> *refc = _get_refcount();
SafeNumeric<USize> *refc = _get_refcount();
if (refc->decrement() > 0) {
return; // still in use
}
// clean up
if (!__has_trivial_destructor(T)) {
uint32_t *count = _get_size();
if constexpr (!std::is_trivially_destructible_v<T>) {
USize *count = _get_size();
T *data = (T *)(count + 1);
for (uint32_t i = 0; i < *count; ++i) {
for (USize i = 0; i < *count; ++i) {
// call destructors
data[i].~T();
}
}
// free mem
Memory::free_static((uint8_t *)p_data, true);
Memory::free_static(((uint8_t *)p_data) - DATA_OFFSET, false);
}
template <class T>
uint32_t CowData<T>::_copy_on_write() {
template <typename T>
typename CowData<T>::USize CowData<T>::_copy_on_write() {
if (!_ptr) {
return 0;
}
SafeNumeric<uint32_t> *refc = _get_refcount();
SafeNumeric<USize> *refc = _get_refcount();
uint32_t rc = refc->get();
USize rc = refc->get();
if (unlikely(rc > 1)) {
/* in use by more than me */
uint32_t current_size = *_get_size();
USize current_size = *_get_size();
uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true);
uint8_t *mem_new = (uint8_t *)Memory::alloc_static(_get_alloc_size(current_size) + DATA_OFFSET, false);
ERR_FAIL_NULL_V(mem_new, 0);
new (mem_new - 2) SafeNumeric<uint32_t>(1); // refcount
*(mem_new - 1) = current_size; // size
SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new);
USize *_size_ptr = _get_size_ptr(mem_new);
T *_data_ptr = _get_data_ptr(mem_new);
T *_data = (T *)(mem_new);
new (_refc_ptr) SafeNumeric<USize>(1); //refcount
*(_size_ptr) = current_size; //size
// initialize new elements
if (__has_trivial_copy(T)) {
memcpy(mem_new, _ptr, current_size * sizeof(T));
if constexpr (std::is_trivially_copyable_v<T>) {
memcpy((uint8_t *)_data_ptr, _ptr, current_size * sizeof(T));
} else {
for (uint32_t i = 0; i < current_size; i++) {
memnew_placement(&_data[i], T(_get_data()[i]));
for (USize i = 0; i < current_size; i++) {
memnew_placement(&_data_ptr[i], T(_ptr[i]));
}
}
_unref(_ptr);
_ptr = _data;
_ptr = _data_ptr;
rc = 1;
}
return rc;
}
template <class T>
Error CowData<T>::resize(int p_size) {
template <typename T>
template <bool p_ensure_zero>
Error CowData<T>::resize(Size p_size) {
ERR_FAIL_COND_V(p_size < 0, ERR_INVALID_PARAMETER);
int current_size = size();
Size current_size = size();
if (p_size == current_size) {
return OK;
@@ -276,59 +334,71 @@ Error CowData<T>::resize(int p_size) {
}
// possibly changing size, copy on write
uint32_t rc = _copy_on_write();
USize rc = _copy_on_write();
size_t current_alloc_size = _get_alloc_size(current_size);
size_t alloc_size;
USize current_alloc_size = _get_alloc_size(current_size);
USize alloc_size;
ERR_FAIL_COND_V(!_get_alloc_size_checked(p_size, &alloc_size), ERR_OUT_OF_MEMORY);
if (p_size > current_size) {
if (alloc_size != current_alloc_size) {
if (current_size == 0) {
// alloc from scratch
uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true);
ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY);
*(ptr - 1) = 0; // size, currently none
new (ptr - 2) SafeNumeric<uint32_t>(1); // refcount
uint8_t *mem_new = (uint8_t *)Memory::alloc_static(alloc_size + DATA_OFFSET, false);
ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY);
_ptr = (T *)ptr;
SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new);
USize *_size_ptr = _get_size_ptr(mem_new);
T *_data_ptr = _get_data_ptr(mem_new);
new (_refc_ptr) SafeNumeric<USize>(1); //refcount
*(_size_ptr) = 0; //size, currently none
_ptr = _data_ptr;
} else {
uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true);
ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY);
new (_ptrnew - 2) SafeNumeric<uint32_t>(rc); // refcount
uint8_t *mem_new = (uint8_t *)Memory::realloc_static(((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false);
ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY);
_ptr = (T *)(_ptrnew);
SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new);
T *_data_ptr = _get_data_ptr(mem_new);
new (_refc_ptr) SafeNumeric<USize>(rc); //refcount
_ptr = _data_ptr;
}
}
// construct the newly created elements
if (!__has_trivial_constructor(T)) {
T *elems = _get_data();
for (int i = *_get_size(); i < p_size; i++) {
memnew_placement(&elems[i], T);
if constexpr (!std::is_trivially_constructible_v<T>) {
for (Size i = *_get_size(); i < p_size; i++) {
memnew_placement(&_ptr[i], T);
}
} else if (p_ensure_zero) {
memset((void *)(_ptr + current_size), 0, (p_size - current_size) * sizeof(T));
}
*_get_size() = p_size;
} else if (p_size < current_size) {
if (!__has_trivial_destructor(T)) {
if constexpr (!std::is_trivially_destructible_v<T>) {
// deinitialize no longer needed elements
for (uint32_t i = p_size; i < *_get_size(); i++) {
T *t = &_get_data()[i];
for (USize i = p_size; i < *_get_size(); i++) {
T *t = &_ptr[i];
t->~T();
}
}
if (alloc_size != current_alloc_size) {
uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true);
ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY);
new (_ptrnew - 2) SafeNumeric<uint32_t>(rc); // refcount
uint8_t *mem_new = (uint8_t *)Memory::realloc_static(((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false);
ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY);
_ptr = (T *)(_ptrnew);
SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new);
T *_data_ptr = _get_data_ptr(mem_new);
new (_refc_ptr) SafeNumeric<USize>(rc); //refcount
_ptr = _data_ptr;
}
*_get_size() = p_size;
@@ -337,15 +407,15 @@ Error CowData<T>::resize(int p_size) {
return OK;
}
template <class T>
int CowData<T>::find(const T &p_val, int p_from) const {
int ret = -1;
template <typename T>
typename CowData<T>::Size CowData<T>::find(const T &p_val, Size p_from) const {
Size ret = -1;
if (p_from < 0 || size() == 0) {
return ret;
}
for (int i = p_from; i < size(); i++) {
for (Size i = p_from; i < size(); i++) {
if (get(i) == p_val) {
ret = i;
break;
@@ -355,12 +425,42 @@ int CowData<T>::find(const T &p_val, int p_from) const {
return ret;
}
template <class T>
template <typename T>
typename CowData<T>::Size CowData<T>::rfind(const T &p_val, Size p_from) const {
const Size s = size();
if (p_from < 0) {
p_from = s + p_from;
}
if (p_from < 0 || p_from >= s) {
p_from = s - 1;
}
for (Size i = p_from; i >= 0; i--) {
if (get(i) == p_val) {
return i;
}
}
return -1;
}
template <typename T>
typename CowData<T>::Size CowData<T>::count(const T &p_val) const {
Size amount = 0;
for (Size i = 0; i < size(); i++) {
if (get(i) == p_val) {
amount++;
}
}
return amount;
}
template <typename T>
void CowData<T>::_ref(const CowData *p_from) {
_ref(*p_from);
}
template <class T>
template <typename T>
void CowData<T>::_ref(const CowData &p_from) {
if (_ptr == p_from._ptr) {
return; // self assign, do nothing.
@@ -378,7 +478,7 @@ void CowData<T>::_ref(const CowData &p_from) {
}
}
template <class T>
template <typename T>
CowData<T>::~CowData() {
_unref(_ptr);
}

View File

@@ -52,7 +52,7 @@ namespace godot {
* The assignment operator copy the pairs from one map to the other.
*/
template <class TKey, class TValue>
template <typename TKey, typename TValue>
struct HashMapElement {
HashMapElement *next = nullptr;
HashMapElement *prev = nullptr;
@@ -62,10 +62,10 @@ struct HashMapElement {
data(p_key, p_value) {}
};
template <class TKey, class TValue,
class Hasher = HashMapHasherDefault,
class Comparator = HashMapComparatorDefault<TKey>,
class Allocator = DefaultTypedAllocator<HashMapElement<TKey, TValue>>>
template <typename TKey, typename TValue,
typename Hasher = HashMapHasherDefault,
typename Comparator = HashMapComparatorDefault<TKey>,
typename Allocator = DefaultTypedAllocator<HashMapElement<TKey, TValue>>>
class HashMap {
public:
const uint32_t MIN_CAPACITY_INDEX = 2; // Use a prime.

View File

@@ -48,9 +48,9 @@ namespace godot {
*
*/
template <class TKey,
class Hasher = HashMapHasherDefault,
class Comparator = HashMapComparatorDefault<TKey>>
template <typename TKey,
typename Hasher = HashMapHasherDefault,
typename Comparator = HashMapComparatorDefault<TKey>>
class HashSet {
public:
static constexpr uint32_t MIN_CAPACITY_INDEX = 2; // Use a prime.

View File

@@ -253,7 +253,7 @@ static _FORCE_INLINE_ uint32_t hash_djb2_one_float(double p_in, uint32_t p_prev
return ((p_prev << 5) + p_prev) + hash_one_uint64(u.i);
}
template <class T>
template <typename T>
static _FORCE_INLINE_ uint32_t hash_make_uint32_t(T p_in) {
union {
T t;
@@ -286,7 +286,7 @@ static _FORCE_INLINE_ uint64_t hash_djb2_one_64(uint64_t p_in, uint64_t p_prev =
return ((p_prev << 5) + p_prev) ^ p_in;
}
template <class T>
template <typename T>
static _FORCE_INLINE_ uint64_t hash_make_uint64_t(T p_in) {
union {
T t;
@@ -298,15 +298,15 @@ static _FORCE_INLINE_ uint64_t hash_make_uint64_t(T p_in) {
return _u._u64;
}
template <class T>
template <typename T>
class Ref;
struct HashMapHasherDefault {
// Generic hash function for any type.
template <class T>
template <typename T>
static _FORCE_INLINE_ uint32_t hash(const T *p_pointer) { return hash_one_uint64((uint64_t)p_pointer); }
template <class T>
template <typename T>
static _FORCE_INLINE_ uint32_t hash(const Ref<T> &p_ref) { return hash_one_uint64((uint64_t)p_ref.operator->()); }
static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); }

View File

@@ -45,7 +45,7 @@
namespace godot {
template <class T, class A = DefaultAllocator>
template <typename T, typename A = DefaultAllocator>
class List {
struct _Data;
@@ -221,7 +221,7 @@ private:
int size_cache = 0;
bool erase(const Element *p_I) {
ERR_FAIL_COND_V(!p_I, false);
ERR_FAIL_NULL_V(p_I, false);
ERR_FAIL_COND_V(p_I->data != this, false);
if (first == p_I) {
@@ -410,7 +410,7 @@ public:
/**
* find an element in the list,
*/
template <class T_v>
template <typename T_v>
Element *find(const T_v &p_val) {
Element *it = front();
while (it) {
@@ -646,7 +646,7 @@ public:
sort_custom<Comparator<T>>();
}
template <class C>
template <typename C>
void sort_custom_inplace() {
if (size() < 2) {
return;
@@ -693,7 +693,7 @@ public:
_data->last = to;
}
template <class C>
template <typename C>
struct AuxiliaryComparator {
C compare;
_FORCE_INLINE_ bool operator()(const Element *a, const Element *b) const {
@@ -701,7 +701,7 @@ public:
}
};
template <class C>
template <typename C>
void sort_custom() {
// this version uses auxiliary memory for speed.
// if you don't want to use auxiliary memory, use the in_place version

View File

@@ -43,7 +43,7 @@ namespace godot {
// If tight, it grows strictly as much as needed.
// Otherwise, it grows exponentially (the default and what you want in most cases).
template <class T, class U = uint32_t, bool force_trivial = false, bool tight = false>
template <typename T, typename U = uint32_t, bool force_trivial = false, bool tight = false>
class LocalVector {
private:
U count = 0;
@@ -257,7 +257,7 @@ public:
return -1;
}
template <class C>
template <typename C>
void sort_custom() {
U len = count;
if (len == 0) {
@@ -331,7 +331,7 @@ public:
}
};
template <class T, class U = uint32_t, bool force_trivial = false>
template <typename T, typename U = uint32_t, bool force_trivial = false>
using TightLocalVector = LocalVector<T, U, force_trivial, true>;
} // namespace godot

View File

@@ -33,7 +33,7 @@
namespace godot {
template <class F, class S>
template <typename F, typename S>
struct Pair {
F first;
S second;
@@ -49,17 +49,17 @@ struct Pair {
}
};
template <class F, class S>
template <typename F, typename S>
bool operator==(const Pair<F, S> &pair, const Pair<F, S> &other) {
return (pair.first == other.first) && (pair.second == other.second);
}
template <class F, class S>
template <typename F, typename S>
bool operator!=(const Pair<F, S> &pair, const Pair<F, S> &other) {
return (pair.first != other.first) || (pair.second != other.second);
}
template <class F, class S>
template <typename F, typename S>
struct PairSort {
bool operator()(const Pair<F, S> &A, const Pair<F, S> &B) const {
if (A.first != B.first) {
@@ -69,7 +69,7 @@ struct PairSort {
}
};
template <class K, class V>
template <typename K, typename V>
struct KeyValue {
const K key;
V value;
@@ -85,17 +85,17 @@ struct KeyValue {
}
};
template <class K, class V>
template <typename K, typename V>
bool operator==(const KeyValue<K, V> &pair, const KeyValue<K, V> &other) {
return (pair.key == other.key) && (pair.value == other.value);
}
template <class K, class V>
template <typename K, typename V>
bool operator!=(const KeyValue<K, V> &pair, const KeyValue<K, V> &other) {
return (pair.key != other.key) || (pair.value != other.value);
}
template <class K, class V>
template <typename K, typename V>
struct KeyValueSort {
bool operator()(const KeyValue<K, V> &A, const KeyValue<K, V> &B) const {
return A.key < B.key;

View File

@@ -40,7 +40,7 @@ namespace godot {
// based on the very nice implementation of rb-trees by:
// https://web.archive.org/web/20120507164830/https://web.mit.edu/~emin/www/source_code/red_black_tree/index.html
template <class K, class V, class C = Comparator<K>, class A = DefaultAllocator>
template <typename K, typename V, typename C = Comparator<K>, typename A = DefaultAllocator>
class RBMap {
enum Color {
RED,

View File

@@ -38,7 +38,7 @@
namespace godot {
template <class T, class C = Comparator<T>, class A = DefaultAllocator>
template <typename T, typename C = Comparator<T>, typename A = DefaultAllocator>
class RBSet {
enum Color {
RED,

View File

@@ -42,7 +42,7 @@
namespace godot {
template <class T, bool THREAD_SAFE = false>
template <typename T, bool THREAD_SAFE = false>
class RID_Alloc {
T **chunks = nullptr;
uint32_t **free_list_chunks = nullptr;
@@ -186,12 +186,12 @@ public:
}
void initialize_rid(RID p_rid) {
T *mem = get_or_null(p_rid, true);
ERR_FAIL_COND(!mem);
ERR_FAIL_NULL(mem);
memnew_placement(mem, T);
}
void initialize_rid(RID p_rid, const T &p_value) {
T *mem = get_or_null(p_rid, true);
ERR_FAIL_COND(!mem);
ERR_FAIL_NULL(mem);
memnew_placement(mem, T(p_value));
}
@@ -347,7 +347,7 @@ public:
}
};
template <class T, bool THREAD_SAFE = false>
template <typename T, bool THREAD_SAFE = false>
class RID_PtrOwner {
RID_Alloc<T *, THREAD_SAFE> alloc;
@@ -374,7 +374,7 @@ public:
_FORCE_INLINE_ void replace(const RID &p_rid, T *p_new_ptr) {
T **ptr = alloc.get_or_null(p_rid);
ERR_FAIL_COND(!ptr);
ERR_FAIL_NULL(ptr);
*ptr = p_new_ptr;
}
@@ -406,7 +406,7 @@ public:
alloc(p_target_chunk_byte_size) {}
};
template <class T, bool THREAD_SAFE = false>
template <typename T, bool THREAD_SAFE = false>
class RID_Owner {
RID_Alloc<T, THREAD_SAFE> alloc;

View File

@@ -48,7 +48,16 @@ namespace godot {
// value and, as an important benefit, you can be sure the value is properly synchronized
// even with threads that are already running.
template <class T>
// These are used in very specific areas of the engine where it's critical that these guarantees are held
#define SAFE_NUMERIC_TYPE_PUN_GUARANTEES(m_type) \
static_assert(sizeof(SafeNumeric<m_type>) == sizeof(m_type)); \
static_assert(alignof(SafeNumeric<m_type>) == alignof(m_type)); \
static_assert(std::is_trivially_destructible<std::atomic<m_type>>::value);
#define SAFE_FLAG_TYPE_PUN_GUARANTEES \
static_assert(sizeof(SafeFlag) == sizeof(bool)); \
static_assert(alignof(SafeFlag) == alignof(bool));
template <typename T>
class SafeNumeric {
std::atomic<T> value;
@@ -186,7 +195,7 @@ public:
#else
template <class T>
template <typename T>
class SafeNumeric {
protected:
T value;

View File

@@ -35,7 +35,7 @@
namespace godot {
template <class T, class Comparator = _DefaultComparator<T>>
template <typename T, typename Comparator = _DefaultComparator<T>>
class SearchArray {
public:
Comparator compare;

View File

@@ -36,7 +36,7 @@
namespace godot {
template <class T>
template <typename T>
class SelfList {
public:
class List {

View File

@@ -41,7 +41,7 @@ namespace godot {
break; \
}
template <class T>
template <typename T>
struct _DefaultComparator {
_FORCE_INLINE_ bool operator()(const T &a, const T &b) const { return (a < b); }
};
@@ -52,7 +52,7 @@ struct _DefaultComparator {
#define SORT_ARRAY_VALIDATE_ENABLED false
#endif
template <class T, class Comparator = _DefaultComparator<T>, bool Validate = SORT_ARRAY_VALIDATE_ENABLED>
template <typename T, typename Comparator = _DefaultComparator<T>, bool Validate = SORT_ARRAY_VALIDATE_ENABLED>
class SortArray {
enum {
INTROSORT_THRESHOLD = 16

View File

@@ -52,7 +52,7 @@ class ThreadWorkPool {
virtual ~BaseWork() = default;
};
template <class C, class M, class U>
template <typename C, typename M, typename U>
struct Work : public BaseWork {
C *instance;
M method;
@@ -94,9 +94,9 @@ class ThreadWorkPool {
}
public:
template <class C, class M, class U>
template <typename C, typename M, typename U>
void begin_work(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) {
ERR_FAIL_COND(!threads); // never initialized
ERR_FAIL_NULL(threads); // Never initialized.
ERR_FAIL_COND(current_work != nullptr);
index.store(0, std::memory_order_release);
@@ -123,18 +123,18 @@ public:
}
bool is_done_dispatching() const {
ERR_FAIL_COND_V(current_work == nullptr, true);
ERR_FAIL_NULL_V(current_work, true);
return index.load(std::memory_order_acquire) >= current_work->max_elements;
}
uint32_t get_work_index() const {
ERR_FAIL_COND_V(current_work == nullptr, 0);
ERR_FAIL_NULL_V(current_work, 0);
uint32_t idx = index.load(std::memory_order_acquire);
return Math::min(idx, current_work->max_elements);
}
void end_work() {
ERR_FAIL_COND(current_work == nullptr);
ERR_FAIL_NULL(current_work);
for (uint32_t i = 0; i < threads_working; i++) {
threads[i].completed.wait();
threads[i].work = nullptr;
@@ -145,7 +145,7 @@ public:
current_work = nullptr;
}
template <class C, class M, class U>
template <typename C, typename M, typename U>
void do_work(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) {
switch (p_elements) {
case 0:

View File

@@ -47,38 +47,42 @@
namespace godot {
template <class T>
template <typename T>
class VectorWriteProxy {
public:
_FORCE_INLINE_ T &operator[](int p_index) {
_FORCE_INLINE_ T &operator[](typename CowData<T>::Size p_index) {
CRASH_BAD_INDEX(p_index, ((Vector<T> *)(this))->_cowdata.size());
return ((Vector<T> *)(this))->_cowdata.ptrw()[p_index];
}
};
template <class T>
template <typename T>
class Vector {
friend class VectorWriteProxy<T>;
public:
VectorWriteProxy<T> write;
typedef typename CowData<T>::Size Size;
private:
CowData<T> _cowdata;
public:
bool push_back(T p_elem);
_FORCE_INLINE_ bool append(const T &p_elem) { return push_back(p_elem); } // alias
_FORCE_INLINE_ bool append(const T &p_elem) { return push_back(p_elem); } //alias
void fill(T p_elem);
void remove_at(int p_index) { _cowdata.remove_at(p_index); }
void erase(const T &p_val) {
int idx = find(p_val);
void remove_at(Size p_index) { _cowdata.remove_at(p_index); }
_FORCE_INLINE_ bool erase(const T &p_val) {
Size idx = find(p_val);
if (idx >= 0) {
remove_at(idx);
return true;
}
return false;
}
void reverse();
_FORCE_INLINE_ T *ptrw() { return _cowdata.ptrw(); }
@@ -86,37 +90,45 @@ public:
_FORCE_INLINE_ void clear() { resize(0); }
_FORCE_INLINE_ bool is_empty() const { return _cowdata.is_empty(); }
_FORCE_INLINE_ T get(int p_index) { return _cowdata.get(p_index); }
_FORCE_INLINE_ const T &get(int p_index) const { return _cowdata.get(p_index); }
_FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); }
_FORCE_INLINE_ int size() const { return _cowdata.size(); }
Error resize(int p_size) { return _cowdata.resize(p_size); }
_FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata.get(p_index); }
Error insert(int p_pos, T p_val) { return _cowdata.insert(p_pos, p_val); }
int find(const T &p_val, int p_from = 0) const { return _cowdata.find(p_val, p_from); }
_FORCE_INLINE_ T get(Size p_index) { return _cowdata.get(p_index); }
_FORCE_INLINE_ const T &get(Size p_index) const { return _cowdata.get(p_index); }
_FORCE_INLINE_ void set(Size p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); }
_FORCE_INLINE_ Size size() const { return _cowdata.size(); }
Error resize(Size p_size) { return _cowdata.resize(p_size); }
Error resize_zeroed(Size p_size) { return _cowdata.template resize<true>(p_size); }
_FORCE_INLINE_ const T &operator[](Size p_index) const { return _cowdata.get(p_index); }
Error insert(Size p_pos, T p_val) { return _cowdata.insert(p_pos, p_val); }
Size find(const T &p_val, Size p_from = 0) const { return _cowdata.find(p_val, p_from); }
Size rfind(const T &p_val, Size p_from = -1) const { return _cowdata.rfind(p_val, p_from); }
Size count(const T &p_val) const { return _cowdata.count(p_val); }
void append_array(Vector<T> p_other);
_FORCE_INLINE_ bool has(const T &p_val) const { return find(p_val) != -1; }
template <class C>
void sort_custom() {
int len = _cowdata.size();
void sort() {
sort_custom<_DefaultComparator<T>>();
}
template <typename Comparator, bool Validate = SORT_ARRAY_VALIDATE_ENABLED, typename... Args>
void sort_custom(Args &&...args) {
Size len = _cowdata.size();
if (len == 0) {
return;
}
T *data = ptrw();
SortArray<T, C> sorter;
SortArray<T, Comparator, Validate> sorter{ args... };
sorter.sort(data, len);
}
void sort() {
sort_custom<_DefaultComparator<T>>();
Size bsearch(const T &p_value, bool p_before) {
return bsearch_custom<_DefaultComparator<T>>(p_value, p_before);
}
int bsearch(const T &p_value, bool p_before) {
SearchArray<T> search;
template <typename Comparator, typename Value, typename... Args>
Size bsearch_custom(const Value &p_value, bool p_before, Args &&...args) {
SearchArray<T, Comparator> search{ args... };
return search.bisect(ptrw(), size(), p_value, p_before);
}
@@ -125,7 +137,7 @@ public:
}
void ordered_insert(const T &p_val) {
int i;
Size i;
for (i = 0; i < _cowdata.size(); i++) {
if (p_val < operator[](i)) {
break;
@@ -140,33 +152,36 @@ public:
Vector<uint8_t> to_byte_array() const {
Vector<uint8_t> ret;
if (is_empty()) {
return ret;
}
ret.resize(size() * sizeof(T));
memcpy(ret.ptrw(), ptr(), sizeof(T) * size());
return ret;
}
Vector<T> slice(int p_begin, int p_end = INT_MAX) const {
Vector<T> slice(Size p_begin, Size p_end = CowData<T>::MAX_INT) const {
Vector<T> result;
const int s = size();
const Size s = size();
int begin = Math::clamp(p_begin, -s, s);
Size begin = CLAMP(p_begin, -s, s);
if (begin < 0) {
begin += s;
}
int end = Math::clamp(p_end, -s, s);
Size end = CLAMP(p_end, -s, s);
if (end < 0) {
end += s;
}
ERR_FAIL_COND_V(begin > end, result);
int result_size = end - begin;
Size result_size = end - begin;
result.resize(result_size);
const T *const r = ptr();
T *const w = result.ptrw();
for (int i = 0; i < result_size; ++i) {
for (Size i = 0; i < result_size; ++i) {
w[i] = r[begin + i];
}
@@ -174,11 +189,11 @@ public:
}
bool operator==(const Vector<T> &p_arr) const {
int s = size();
Size s = size();
if (s != p_arr.size()) {
return false;
}
for (int i = 0; i < s; i++) {
for (Size i = 0; i < s; i++) {
if (operator[](i) != p_arr[i]) {
return false;
}
@@ -187,11 +202,11 @@ public:
}
bool operator!=(const Vector<T> &p_arr) const {
int s = size();
Size s = size();
if (s != p_arr.size()) {
return true;
}
for (int i = 0; i < s; i++) {
for (Size i = 0; i < s; i++) {
if (operator[](i) != p_arr[i]) {
return true;
}
@@ -268,7 +283,7 @@ public:
Error err = _cowdata.resize(p_init.size());
ERR_FAIL_COND(err);
int i = 0;
Size i = 0;
for (const T &element : p_init) {
_cowdata.set(i++, element);
}
@@ -278,28 +293,28 @@ public:
_FORCE_INLINE_ ~Vector() {}
};
template <class T>
template <typename T>
void Vector<T>::reverse() {
for (int i = 0; i < size() / 2; i++) {
for (Size i = 0; i < size() / 2; i++) {
T *p = ptrw();
SWAP(p[i], p[size() - i - 1]);
}
}
template <class T>
template <typename T>
void Vector<T>::append_array(Vector<T> p_other) {
const int ds = p_other.size();
const Size ds = p_other.size();
if (ds == 0) {
return;
}
const int bs = size();
const Size bs = size();
resize(bs + ds);
for (int i = 0; i < ds; ++i) {
for (Size i = 0; i < ds; ++i) {
ptrw()[bs + i] = p_other[i];
}
}
template <class T>
template <typename T>
bool Vector<T>::push_back(T p_elem) {
Error err = resize(size() + 1);
ERR_FAIL_COND_V(err, true);
@@ -308,10 +323,10 @@ bool Vector<T>::push_back(T p_elem) {
return false;
}
template <class T>
template <typename T>
void Vector<T>::fill(T p_elem) {
T *p = ptrw();
for (int i = 0; i < size(); i++) {
for (Size i = 0; i < size(); i++) {
p[i] = p_elem;
}
}

View File

@@ -35,7 +35,7 @@
namespace godot {
template <class T, class V>
template <typename T, typename V>
class VMap {
public:
struct Pair {

View File

@@ -35,7 +35,7 @@
namespace godot {
template <class T>
template <typename T>
class VSet {
Vector<T> _data;

View File

@@ -31,82 +31,111 @@
#ifndef GODOT_CHAR_STRING_HPP
#define GODOT_CHAR_STRING_HPP
#include <godot_cpp/templates/cowdata.hpp>
#include <cstddef>
#include <cstdint>
namespace godot {
class CharString {
friend class String;
template <typename T>
class CharStringT;
const char *_data = nullptr;
int _length = 0;
template <typename T>
class CharProxy {
template <typename TS>
friend class CharStringT;
CharString(const char *str, int length);
const int64_t _index;
CowData<T> &_cowdata;
static inline const T _null = 0;
_FORCE_INLINE_ CharProxy(const int64_t &p_index, CowData<T> &p_cowdata) :
_index(p_index),
_cowdata(p_cowdata) {}
public:
int length() const;
const char *get_data() const;
_FORCE_INLINE_ CharProxy(const CharProxy<T> &p_other) :
_index(p_other._index),
_cowdata(p_other._cowdata) {}
CharString(CharString &&p_str);
void operator=(CharString &&p_str);
CharString() {}
~CharString();
_FORCE_INLINE_ operator T() const {
if (unlikely(_index == _cowdata.size())) {
return _null;
}
return _cowdata.get(_index);
}
_FORCE_INLINE_ const T *operator&() const {
return _cowdata.ptr() + _index;
}
_FORCE_INLINE_ void operator=(const T &p_other) const {
_cowdata.set(_index, p_other);
}
_FORCE_INLINE_ void operator=(const CharProxy<T> &p_other) const {
_cowdata.set(_index, p_other.operator T());
}
};
class Char16String {
template <typename T>
class CharStringT {
friend class String;
const char16_t *_data = nullptr;
int _length = 0;
Char16String(const char16_t *str, int length);
CowData<T> _cowdata;
static inline const T _null = 0;
public:
int length() const;
const char16_t *get_data() const;
_FORCE_INLINE_ T *ptrw() { return _cowdata.ptrw(); }
_FORCE_INLINE_ const T *ptr() const { return _cowdata.ptr(); }
_FORCE_INLINE_ int64_t size() const { return _cowdata.size(); }
Error resize(int64_t p_size) { return _cowdata.resize(p_size); }
Char16String(Char16String &&p_str);
void operator=(Char16String &&p_str);
Char16String() {}
~Char16String();
_FORCE_INLINE_ T get(int64_t p_index) const { return _cowdata.get(p_index); }
_FORCE_INLINE_ void set(int64_t p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); }
_FORCE_INLINE_ const T &operator[](int64_t p_index) const {
if (unlikely(p_index == _cowdata.size())) {
return _null;
}
return _cowdata.get(p_index);
}
_FORCE_INLINE_ CharProxy<T> operator[](int64_t p_index) { return CharProxy<T>(p_index, _cowdata); }
_FORCE_INLINE_ CharStringT() {}
_FORCE_INLINE_ CharStringT(const CharStringT<T> &p_str) { _cowdata._ref(p_str._cowdata); }
_FORCE_INLINE_ void operator=(const CharStringT<T> &p_str) { _cowdata._ref(p_str._cowdata); }
_FORCE_INLINE_ CharStringT(const T *p_cstr) { copy_from(p_cstr); }
void operator=(const T *p_cstr);
bool operator<(const CharStringT<T> &p_right) const;
CharStringT<T> &operator+=(T p_char);
int64_t length() const { return size() ? size() - 1 : 0; }
const T *get_data() const;
operator const T *() const { return get_data(); };
protected:
void copy_from(const T *p_cstr);
};
class Char32String {
friend class String;
template <>
const char *CharStringT<char>::get_data() const;
const char32_t *_data = nullptr;
int _length = 0;
template <>
const char16_t *CharStringT<char16_t>::get_data() const;
Char32String(const char32_t *str, int length);
template <>
const char32_t *CharStringT<char32_t>::get_data() const;
public:
int length() const;
const char32_t *get_data() const;
template <>
const wchar_t *CharStringT<wchar_t>::get_data() const;
Char32String(Char32String &&p_str);
void operator=(Char32String &&p_str);
Char32String() {}
~Char32String();
};
class CharWideString {
friend class String;
const wchar_t *_data = nullptr;
int _length = 0;
CharWideString(const wchar_t *str, int length);
public:
int length() const;
const wchar_t *get_data() const;
CharWideString(CharWideString &&p_str);
void operator=(CharWideString &&p_str);
CharWideString() {}
~CharWideString();
};
typedef CharStringT<char> CharString;
typedef CharStringT<char16_t> Char16String;
typedef CharStringT<char32_t> Char32String;
typedef CharStringT<wchar_t> CharWideString;
} // namespace godot

View File

@@ -36,7 +36,7 @@
namespace godot {
template <class T>
template <typename T>
class TypedArray : public Array {
public:
_FORCE_INLINE_ void operator=(const Array &p_array) {

View File

@@ -47,8 +47,6 @@ class ObjectID;
class Variant {
uint8_t opaque[GODOT_CPP_VARIANT_SIZE]{ 0 };
_FORCE_INLINE_ GDExtensionVariantPtr _native_ptr() const { return const_cast<uint8_t(*)[GODOT_CPP_VARIANT_SIZE]>(&opaque); }
friend class GDExtensionBinding;
friend class MethodBind;
@@ -122,6 +120,7 @@ public:
OP_NEGATE,
OP_POSITIVE,
OP_MODULE,
OP_POWER,
// bitwise
OP_SHIFT_LEFT,
OP_SHIFT_RIGHT,
@@ -144,6 +143,7 @@ private:
static GDExtensionTypeFromVariantConstructorFunc to_type_constructor[VARIANT_MAX];
public:
_FORCE_INLINE_ GDExtensionVariantPtr _native_ptr() const { return const_cast<uint8_t(*)[GODOT_CPP_VARIANT_SIZE]>(&opaque); }
Variant();
Variant(std::nullptr_t n) :
Variant() {}
@@ -154,10 +154,18 @@ public:
Variant(int64_t v);
Variant(int32_t v) :
Variant(static_cast<int64_t>(v)) {}
Variant(uint32_t v) :
Variant(int16_t v) :
Variant(static_cast<int64_t>(v)) {}
Variant(int8_t v) :
Variant(static_cast<int64_t>(v)) {}
Variant(uint64_t v) :
Variant(static_cast<int64_t>(v)) {}
Variant(uint32_t v) :
Variant(static_cast<int64_t>(v)) {}
Variant(uint16_t v) :
Variant(static_cast<int64_t>(v)) {}
Variant(uint8_t v) :
Variant(static_cast<int64_t>(v)) {}
Variant(double v);
Variant(float v) :
Variant((double)v) {}
@@ -209,8 +217,12 @@ public:
operator bool() const;
operator int64_t() const;
operator int32_t() const;
operator int16_t() const;
operator int8_t() const;
operator uint64_t() const;
operator uint32_t() const;
operator uint16_t() const;
operator uint8_t() const;
operator double() const;
operator float() const;
operator String() const;
@@ -255,25 +267,33 @@ public:
bool operator!=(const Variant &other) const;
bool operator<(const Variant &other) const;
void call(const StringName &method, const Variant **args, int argcount, Variant &r_ret, GDExtensionCallError &r_error);
void callp(const StringName &method, const Variant **args, int argcount, Variant &r_ret, GDExtensionCallError &r_error);
template <class... Args>
template <typename... Args>
Variant call(const StringName &method, Args... args) {
std::array<Variant, sizeof...(args)> vargs = { args... };
std::array<const Variant *, sizeof...(args)> argptrs;
for (size_t i = 0; i < vargs.size(); i++) {
argptrs[i] = &vargs[i];
}
Variant result;
GDExtensionCallError error;
std::array<GDExtensionConstVariantPtr, sizeof...(Args)> call_args = { Variant(args)... };
call(method, call_args.data(), call_args.size(), result, error);
callp(method, argptrs.data(), argptrs.size(), result, error);
return result;
}
static void call_static(Variant::Type type, const StringName &method, const Variant **args, int argcount, Variant &r_ret, GDExtensionCallError &r_error);
static void callp_static(Variant::Type type, const StringName &method, const Variant **args, int argcount, Variant &r_ret, GDExtensionCallError &r_error);
template <class... Args>
template <typename... Args>
static Variant call_static(Variant::Type type, const StringName &method, Args... args) {
std::array<Variant, sizeof...(args)> vargs = { args... };
std::array<const Variant *, sizeof...(args)> argptrs;
for (size_t i = 0; i < vargs.size(); i++) {
argptrs[i] = &vargs[i];
}
Variant result;
GDExtensionCallError error;
std::array<GDExtensionConstVariantPtr, sizeof...(Args)> call_args = { Variant(args)... };
call_static(type, method, call_args.data(), call_args.size(), result, error);
callp_static(type, method, argptrs.data(), argptrs.size(), sizeof...(args), result, error);
return result;
}

View File

@@ -78,6 +78,14 @@ struct _NO_DISCARD_ Vector3 {
return x < y ? (y < z ? Vector3::AXIS_Z : Vector3::AXIS_Y) : (x < z ? Vector3::AXIS_Z : Vector3::AXIS_X);
}
Vector3 min(const Vector3 &p_vector3) const {
return Vector3(MIN(x, p_vector3.x), MIN(y, p_vector3.y), MIN(z, p_vector3.z));
}
Vector3 max(const Vector3 &p_vector3) const {
return Vector3(MAX(x, p_vector3.x), MAX(y, p_vector3.y), MAX(z, p_vector3.z));
}
_FORCE_INLINE_ real_t length() const;
_FORCE_INLINE_ real_t length_squared() const;

View File

@@ -71,6 +71,14 @@ struct _NO_DISCARD_ Vector3i {
Vector3i::Axis min_axis_index() const;
Vector3i::Axis max_axis_index() const;
Vector3i min(const Vector3i &p_vector3i) const {
return Vector3i(MIN(x, p_vector3i.x), MIN(y, p_vector3i.y), MIN(z, p_vector3i.z));
}
Vector3i max(const Vector3i &p_vector3i) const {
return Vector3i(MAX(x, p_vector3i.x), MAX(y, p_vector3i.y), MAX(z, p_vector3i.z));
}
_FORCE_INLINE_ int64_t length_squared() const;
_FORCE_INLINE_ double length() const;

View File

@@ -70,6 +70,14 @@ struct _NO_DISCARD_ Vector4 {
Vector4::Axis min_axis_index() const;
Vector4::Axis max_axis_index() const;
Vector4 min(const Vector4 &p_vector4) const {
return Vector4(MIN(x, p_vector4.x), MIN(y, p_vector4.y), MIN(z, p_vector4.z), MIN(w, p_vector4.w));
}
Vector4 max(const Vector4 &p_vector4) const {
return Vector4(MAX(x, p_vector4.x), MAX(y, p_vector4.y), MAX(z, p_vector4.z), MAX(w, p_vector4.w));
}
_FORCE_INLINE_ real_t length_squared() const;
bool is_equal_approx(const Vector4 &p_vec4) const;
bool is_zero_approx() const;

View File

@@ -73,6 +73,14 @@ struct _NO_DISCARD_ Vector4i {
Vector4i::Axis min_axis_index() const;
Vector4i::Axis max_axis_index() const;
Vector4i min(const Vector4i &p_vector4i) const {
return Vector4i(MIN(x, p_vector4i.x), MIN(y, p_vector4i.y), MIN(z, p_vector4i.z), MIN(w, p_vector4i.w));
}
Vector4i max(const Vector4i &p_vector4i) const {
return Vector4i(MAX(x, p_vector4i.x), MAX(y, p_vector4i.y), MAX(z, p_vector4i.z), MAX(w, p_vector4i.w));
}
_FORCE_INLINE_ int64_t length_squared() const;
_FORCE_INLINE_ double length() const;

View File

@@ -1,5 +1,5 @@
/**************************************************************************/
/* editor_plugin.cpp */
/* editor_plugin_registration.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,9 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include <godot_cpp/classes/editor_plugin.hpp>
#include <godot_cpp/classes/editor_plugin_registration.hpp>
#include <godot_cpp/variant/string_name.hpp>
#include <godot_cpp/variant/variant.hpp>
namespace godot {

View File

@@ -28,12 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include <vector>
#include <godot_cpp/classes/wrapped.hpp>
#include <godot_cpp/variant/builtin_types.hpp>
#include <godot_cpp/classes/object.hpp>
#include <godot_cpp/core/class_db.hpp>
namespace godot {
const StringName *Wrapped::_get_extension_class_name() const {
@@ -46,6 +50,12 @@ void Wrapped::_postinitialize() {
godot::internal::gdextension_interface_object_set_instance(_owner, reinterpret_cast<GDExtensionConstStringNamePtr>(extension_class), this);
}
godot::internal::gdextension_interface_object_set_instance_binding(_owner, godot::internal::token, this, _get_bindings_callbacks());
if (extension_class) {
Object *obj = dynamic_cast<Object *>(this);
if (obj) {
obj->notification(Object::NOTIFICATION_POSTINITIALIZE);
}
}
}
Wrapped::Wrapped(const StringName p_godot_class) {
@@ -60,4 +70,29 @@ void postinitialize_handler(Wrapped *p_wrapped) {
p_wrapped->_postinitialize();
}
namespace internal {
std::vector<EngineClassRegistrationCallback> &get_engine_class_registration_callbacks() {
static std::vector<EngineClassRegistrationCallback> engine_class_registration_callbacks;
return engine_class_registration_callbacks;
}
void add_engine_class_registration_callback(EngineClassRegistrationCallback p_callback) {
get_engine_class_registration_callbacks().push_back(p_callback);
}
void register_engine_class(const StringName &p_name, const GDExtensionInstanceBindingCallbacks *p_callbacks) {
ClassDB::_register_engine_class(p_name, p_callbacks);
}
void register_engine_classes() {
std::vector<EngineClassRegistrationCallback> &engine_class_registration_callbacks = get_engine_class_registration_callbacks();
for (EngineClassRegistrationCallback cb : engine_class_registration_callbacks) {
cb();
}
engine_class_registration_callbacks.clear();
}
} // namespace internal
} // namespace godot

View File

@@ -77,7 +77,7 @@ void ClassDB::add_property(const StringName &p_class, const PropertyInfo &p_pinf
if (p_setter != String("")) {
setter = get_method(p_class, p_setter);
ERR_FAIL_COND_MSG(!setter, String("Setter method '{0}::{1}()' not found for property '{2}::{3}'.").format(Array::make(p_class, p_setter, p_class, p_pinfo.name)));
ERR_FAIL_NULL_MSG(setter, String("Setter method '{0}::{1}()' not found for property '{2}::{3}'.").format(Array::make(p_class, p_setter, p_class, p_pinfo.name)));
size_t exp_args = 1 + (p_index >= 0 ? 1 : 0);
ERR_FAIL_COND_MSG((int)exp_args != setter->get_argument_count(), String("Setter method '{0}::{1}()' must take a single argument.").format(Array::make(p_class, p_setter)));
@@ -86,7 +86,7 @@ void ClassDB::add_property(const StringName &p_class, const PropertyInfo &p_pinf
ERR_FAIL_COND_MSG(p_getter == String(""), String("Getter method must be specified for '{0}::{1}'.").format(Array::make(p_class, p_pinfo.name)));
MethodBind *getter = get_method(p_class, p_getter);
ERR_FAIL_COND_MSG(!getter, String("Getter method '{0}::{1}()' not found for property '{2}::{3}'.").format(Array::make(p_class, p_getter, p_class, p_pinfo.name)));
ERR_FAIL_NULL_MSG(getter, String("Getter method '{0}::{1}()' not found for property '{2}::{3}'.").format(Array::make(p_class, p_getter, p_class, p_pinfo.name)));
{
size_t exp_args = 0 + (p_index >= 0 ? 1 : 0);
ERR_FAIL_COND_MSG((int)exp_args != getter->get_argument_count(), String("Getter method '{0}::{1}()' must not take any argument.").format(Array::make(p_class, p_getter)));
@@ -318,7 +318,18 @@ GDExtensionClassCallVirtual ClassDB::get_virtual_func(void *p_userdata, GDExtens
const GDExtensionInstanceBindingCallbacks *ClassDB::get_instance_binding_callbacks(const StringName &p_class) {
std::unordered_map<StringName, const GDExtensionInstanceBindingCallbacks *>::iterator callbacks_it = instance_binding_callbacks.find(p_class);
ERR_FAIL_COND_V_MSG(callbacks_it == instance_binding_callbacks.end(), nullptr, String("Cannot find instance binding callbacks for class '{0}'.").format(Array::make(p_class)));
if (likely(callbacks_it != instance_binding_callbacks.end())) {
return callbacks_it->second;
}
// If we don't have an instance binding callback for the given class, find the closest parent where we do.
StringName class_name = p_class;
do {
class_name = get_parent_class(class_name);
ERR_FAIL_COND_V_MSG(class_name == StringName(), nullptr, String("Cannot find instance binding callbacks for class '{0}'.").format(Array::make(p_class)));
callbacks_it = instance_binding_callbacks.find(class_name);
} while (callbacks_it == instance_binding_callbacks.end());
return callbacks_it->second;
}

View File

@@ -41,12 +41,12 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) {
bool prepad = p_pad_align;
#endif
void *mem = internal::gdextension_interface_mem_alloc(p_bytes + (prepad ? PAD_ALIGN : 0));
ERR_FAIL_COND_V(!mem, nullptr);
void *mem = internal::gdextension_interface_mem_alloc(p_bytes + (prepad ? DATA_OFFSET : 0));
ERR_FAIL_NULL_V(mem, nullptr);
if (prepad) {
uint8_t *s8 = (uint8_t *)mem;
return s8 + PAD_ALIGN;
return s8 + DATA_OFFSET;
} else {
return mem;
}
@@ -69,10 +69,10 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) {
#endif
if (prepad) {
mem -= PAD_ALIGN;
mem = (uint8_t *)internal::gdextension_interface_mem_realloc(mem, p_bytes + PAD_ALIGN);
ERR_FAIL_COND_V(!mem, nullptr);
return mem + PAD_ALIGN;
mem -= DATA_OFFSET;
mem = (uint8_t *)internal::gdextension_interface_mem_realloc(mem, p_bytes + DATA_OFFSET);
ERR_FAIL_NULL_V(mem, nullptr);
return mem + DATA_OFFSET;
} else {
return (uint8_t *)internal::gdextension_interface_mem_realloc(mem, p_bytes);
}
@@ -88,7 +88,7 @@ void Memory::free_static(void *p_ptr, bool p_pad_align) {
#endif
if (prepad) {
mem -= PAD_ALIGN;
mem -= DATA_OFFSET;
}
internal::gdextension_interface_mem_free(mem);
}

View File

@@ -30,10 +30,11 @@
#include <godot_cpp/godot.hpp>
#include <godot_cpp/classes/editor_plugin.hpp>
#include <godot_cpp/classes/editor_plugin_registration.hpp>
#include <godot_cpp/classes/wrapped.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/memory.hpp>
#include <godot_cpp/core/version.hpp>
#include <godot_cpp/variant/variant.hpp>
#include <godot_cpp/core/error_macros.hpp>
@@ -188,13 +189,19 @@ GDExtensionInterfaceEditorRemovePlugin gdextension_interface_editor_remove_plugi
} // namespace internal
GDExtensionBinding::Callback GDExtensionBinding::init_callback = nullptr;
GDExtensionBinding::Callback GDExtensionBinding::terminate_callback = nullptr;
GDExtensionInitializationLevel GDExtensionBinding::minimum_initialization_level = GDEXTENSION_INITIALIZATION_CORE;
bool GDExtensionBinding::api_initialized = false;
int GDExtensionBinding::level_initialized[MODULE_INITIALIZATION_LEVEL_MAX] = { 0 };
GDExtensionBinding::InitDataList GDExtensionBinding::initdata;
#define LOAD_PROC_ADDRESS(m_name, m_type) \
internal::gdextension_interface_##m_name = (m_type)p_get_proc_address(#m_name); \
ERR_FAIL_NULL_V_MSG(internal::gdextension_interface_##m_name, false, "Unable to load GDExtension interface function " #m_name "()")
#define ERR_PRINT_EARLY(m_msg) \
internal::gdextension_interface_print_error(m_msg, FUNCTION_STR, __FILE__, __LINE__, false)
#define LOAD_PROC_ADDRESS(m_name, m_type) \
internal::gdextension_interface_##m_name = (m_type)p_get_proc_address(#m_name); \
if (!internal::gdextension_interface_##m_name) { \
ERR_PRINT_EARLY("Unable to load GDExtension interface function " #m_name "()"); \
return false; \
}
// Partial definition of the legacy interface so we can detect it and show an error.
typedef struct {
@@ -211,20 +218,34 @@ typedef struct {
GDExtensionInterfacePrintErrorWithMessage print_error_with_message;
} LegacyGDExtensionInterface;
GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, InitData *p_init_data, GDExtensionInitialization *r_initialization) {
if (!p_init_data || !p_init_data->init_callback) {
ERR_FAIL_V_MSG(false, "Initialization callback must be defined.");
}
if (api_initialized) {
r_initialization->initialize = initialize_level;
r_initialization->deinitialize = deinitialize_level;
r_initialization->userdata = p_init_data;
r_initialization->minimum_initialization_level = p_init_data->minimum_initialization_level;
return true;
}
// Make sure we weren't passed the legacy struct.
uint32_t *raw_interface = (uint32_t *)(void *)p_get_proc_address;
if (raw_interface[0] == 4 && raw_interface[1] == 0) {
// Use the legacy interface only to give a nice error.
LegacyGDExtensionInterface *legacy_interface = (LegacyGDExtensionInterface *)p_get_proc_address;
internal::gdextension_interface_print_error_with_message = (GDExtensionInterfacePrintErrorWithMessage)legacy_interface->print_error_with_message;
ERR_FAIL_V_MSG(false, "Cannot load a GDExtension built for Godot 4.1+ in Godot 4.0.");
internal::gdextension_interface_print_error = (GDExtensionInterfacePrintError)legacy_interface->print_error;
ERR_PRINT_EARLY("Cannot load a GDExtension built for Godot 4.1+ in Godot 4.0.");
return false;
}
// Load the "print_error_with_message" function first (needed by the ERR_FAIL_NULL_V_MSG() macro).
internal::gdextension_interface_print_error_with_message = (GDExtensionInterfacePrintErrorWithMessage)p_get_proc_address("print_error_with_message");
if (!internal::gdextension_interface_print_error_with_message) {
printf("ERROR: Unable to load GDExtension interface function print_error_with_message().\n");
// Load the "print_error" function first (needed by the ERR_PRINT_EARLY() macro).
internal::gdextension_interface_print_error = (GDExtensionInterfacePrintError)p_get_proc_address("print_error");
if (!internal::gdextension_interface_print_error) {
printf("ERROR: Unable to load GDExtension interface function print_error().\n");
return false;
}
@@ -233,10 +254,38 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge
internal::token = p_library;
LOAD_PROC_ADDRESS(get_godot_version, GDExtensionInterfaceGetGodotVersion);
internal::gdextension_interface_get_godot_version(&internal::godot_version);
// Check that godot-cpp was compiled using an extension_api.json older or at the
// same version as the Godot that is loading it.
bool compatible;
if (internal::godot_version.major != GODOT_VERSION_MAJOR) {
compatible = internal::godot_version.major > GODOT_VERSION_MAJOR;
} else if (internal::godot_version.minor != GODOT_VERSION_MINOR) {
compatible = internal::godot_version.minor > GODOT_VERSION_MINOR;
} else {
#if GODOT_VERSION_PATCH > 0
compatible = internal::godot_version.patch >= GODOT_VERSION_PATCH;
#else
// Prevent -Wtype-limits warning due to unsigned comparison.
compatible = true;
#endif
}
if (!compatible) {
// We need to use snprintf() here because vformat() uses Variant, and we haven't loaded
// the GDExtension interface far enough to use Variants yet.
char msg[128];
snprintf(msg, 128, "Cannot load a GDExtension built for Godot %d.%d.%d using an older version of Godot (%d.%d.%d).",
GODOT_VERSION_MAJOR, GODOT_VERSION_MINOR, GODOT_VERSION_PATCH,
internal::godot_version.major, internal::godot_version.minor, internal::godot_version.patch);
ERR_PRINT_EARLY(msg);
return false;
}
LOAD_PROC_ADDRESS(mem_alloc, GDExtensionInterfaceMemAlloc);
LOAD_PROC_ADDRESS(mem_realloc, GDExtensionInterfaceMemRealloc);
LOAD_PROC_ADDRESS(mem_free, GDExtensionInterfaceMemFree);
LOAD_PROC_ADDRESS(print_error, GDExtensionInterfacePrintError);
LOAD_PROC_ADDRESS(print_error_with_message, GDExtensionInterfacePrintErrorWithMessage);
LOAD_PROC_ADDRESS(print_warning, GDExtensionInterfacePrintWarning);
LOAD_PROC_ADDRESS(print_warning_with_message, GDExtensionInterfacePrintWarningWithMessage);
LOAD_PROC_ADDRESS(print_script_error, GDExtensionInterfacePrintScriptError);
@@ -368,63 +417,98 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge
LOAD_PROC_ADDRESS(editor_add_plugin, GDExtensionInterfaceEditorAddPlugin);
LOAD_PROC_ADDRESS(editor_remove_plugin, GDExtensionInterfaceEditorRemovePlugin);
// Load the Godot version.
internal::gdextension_interface_get_godot_version(&internal::godot_version);
r_initialization->initialize = initialize_level;
r_initialization->deinitialize = deinitialize_level;
r_initialization->minimum_initialization_level = minimum_initialization_level;
ERR_FAIL_COND_V_MSG(init_callback == nullptr, false, "Initialization callback must be defined.");
r_initialization->userdata = p_init_data;
r_initialization->minimum_initialization_level = p_init_data->minimum_initialization_level;
Variant::init_bindings();
register_engine_classes();
godot::internal::register_engine_classes();
api_initialized = true;
return true;
}
#undef LOAD_PROC_ADDRESS
#undef ERR_PRINT_EARLY
void GDExtensionBinding::initialize_level(void *userdata, GDExtensionInitializationLevel p_level) {
void GDExtensionBinding::initialize_level(void *p_userdata, GDExtensionInitializationLevel p_level) {
ERR_FAIL_COND(static_cast<ModuleInitializationLevel>(p_level) >= MODULE_INITIALIZATION_LEVEL_MAX);
ClassDB::current_level = p_level;
if (init_callback) {
init_callback(static_cast<ModuleInitializationLevel>(p_level));
InitData *init_data = static_cast<InitData *>(p_userdata);
if (init_data && init_data->init_callback) {
init_data->init_callback(static_cast<ModuleInitializationLevel>(p_level));
}
ClassDB::initialize(p_level);
if (level_initialized[p_level] == 0) {
ClassDB::initialize(p_level);
}
level_initialized[p_level]++;
}
void GDExtensionBinding::deinitialize_level(void *userdata, GDExtensionInitializationLevel p_level) {
void GDExtensionBinding::deinitialize_level(void *p_userdata, GDExtensionInitializationLevel p_level) {
ERR_FAIL_COND(static_cast<ModuleInitializationLevel>(p_level) >= MODULE_INITIALIZATION_LEVEL_MAX);
ClassDB::current_level = p_level;
if (terminate_callback) {
terminate_callback(static_cast<ModuleInitializationLevel>(p_level));
InitData *init_data = static_cast<InitData *>(p_userdata);
if (init_data && init_data->terminate_callback) {
init_data->terminate_callback(static_cast<ModuleInitializationLevel>(p_level));
}
EditorPlugins::deinitialize(p_level);
ClassDB::deinitialize(p_level);
level_initialized[p_level]--;
if (level_initialized[p_level] == 0) {
EditorPlugins::deinitialize(p_level);
ClassDB::deinitialize(p_level);
}
}
void GDExtensionBinding::InitDataList::add(InitData *p_data) {
if (data_count == data_capacity) {
void *new_ptr = realloc(data, sizeof(InitData *) * (data_capacity + 32));
if (new_ptr) {
data = (InitData **)(new_ptr);
data_capacity += 32;
} else {
ERR_FAIL_MSG("Unable to allocate memory for extension callbacks.");
}
}
data[data_count++] = p_data;
}
GDExtensionBinding::InitDataList::~InitDataList() {
for (int i = 0; i < data_count; i++) {
if (data[i]) {
delete data[i];
}
}
if (data) {
free(data);
}
}
GDExtensionBinding::InitObject::InitObject(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
get_proc_address = p_get_proc_address;
library = p_library;
initialization = r_initialization;
init_data = new InitData();
GDExtensionBinding::initdata.add(init_data);
}
void GDExtensionBinding::InitObject::register_initializer(Callback p_init) const {
GDExtensionBinding::init_callback = p_init;
init_data->init_callback = p_init;
}
void GDExtensionBinding::InitObject::register_terminator(Callback p_terminate) const {
GDExtensionBinding::terminate_callback = p_terminate;
init_data->terminate_callback = p_terminate;
}
void GDExtensionBinding::InitObject::set_minimum_library_initialization_level(ModuleInitializationLevel p_level) const {
GDExtensionBinding::minimum_initialization_level = static_cast<GDExtensionInitializationLevel>(p_level);
init_data->minimum_initialization_level = static_cast<GDExtensionInitializationLevel>(p_level);
}
GDExtensionBool GDExtensionBinding::InitObject::init() const {
return GDExtensionBinding::init(get_proc_address, library, initialization);
return GDExtensionBinding::init(get_proc_address, library, init_data, initialization);
}
} // namespace godot

View File

@@ -38,117 +38,121 @@
#include <godot_cpp/godot.hpp>
#include <cmath>
#include <string>
namespace godot {
int CharString::length() const {
return _length;
}
template <typename L, typename R>
_FORCE_INLINE_ bool is_str_less(const L *l_ptr, const R *r_ptr) {
while (true) {
const char32_t l = *l_ptr;
const char32_t r = *r_ptr;
const char *CharString::get_data() const {
return _data;
}
if (l == 0 && r == 0) {
return false;
} else if (l == 0) {
return true;
} else if (r == 0) {
return false;
} else if (l < r) {
return true;
} else if (l > r) {
return false;
}
CharString::CharString(CharString &&p_str) {
SWAP(_length, p_str._length);
SWAP(_data, p_str._data);
}
void CharString::operator=(CharString &&p_str) {
SWAP(_length, p_str._length);
SWAP(_data, p_str._data);
}
CharString::CharString(const char *str, int length) :
_data(str), _length(length) {}
CharString::~CharString() {
if (_data != nullptr) {
memdelete_arr(_data);
l_ptr++;
r_ptr++;
}
}
int Char16String::length() const {
return _length;
template <typename T>
bool CharStringT<T>::operator<(const CharStringT<T> &p_right) const {
if (length() == 0) {
return p_right.length() != 0;
}
return is_str_less(get_data(), p_right.get_data());
}
const char16_t *Char16String::get_data() const {
return _data;
template <typename T>
CharStringT<T> &CharStringT<T>::operator+=(T p_char) {
const int64_t lhs_len = length();
resize(lhs_len + 2);
T *dst = ptrw();
dst[lhs_len] = p_char;
dst[lhs_len + 1] = 0;
return *this;
}
Char16String::Char16String(Char16String &&p_str) {
SWAP(_length, p_str._length);
SWAP(_data, p_str._data);
template <typename T>
void CharStringT<T>::operator=(const T *p_cstr) {
copy_from(p_cstr);
}
void Char16String::operator=(Char16String &&p_str) {
SWAP(_length, p_str._length);
SWAP(_data, p_str._data);
}
Char16String::Char16String(const char16_t *str, int length) :
_data(str), _length(length) {}
Char16String::~Char16String() {
if (_data != nullptr) {
memdelete_arr(_data);
template <>
const char *CharStringT<char>::get_data() const {
if (size()) {
return &operator[](0);
} else {
return "";
}
}
int Char32String::length() const {
return _length;
}
const char32_t *Char32String::get_data() const {
return _data;
}
Char32String::Char32String(Char32String &&p_str) {
SWAP(_length, p_str._length);
SWAP(_data, p_str._data);
}
void Char32String::operator=(Char32String &&p_str) {
SWAP(_length, p_str._length);
SWAP(_data, p_str._data);
}
Char32String::Char32String(const char32_t *str, int length) :
_data(str), _length(length) {}
Char32String::~Char32String() {
if (_data != nullptr) {
memdelete_arr(_data);
template <>
const char16_t *CharStringT<char16_t>::get_data() const {
if (size()) {
return &operator[](0);
} else {
return u"";
}
}
int CharWideString::length() const {
return _length;
}
const wchar_t *CharWideString::get_data() const {
return _data;
}
CharWideString::CharWideString(CharWideString &&p_str) {
SWAP(_length, p_str._length);
SWAP(_data, p_str._data);
}
void CharWideString::operator=(CharWideString &&p_str) {
SWAP(_length, p_str._length);
SWAP(_data, p_str._data);
}
CharWideString::CharWideString(const wchar_t *str, int length) :
_data(str), _length(length) {}
CharWideString::~CharWideString() {
if (_data != nullptr) {
memdelete_arr(_data);
template <>
const char32_t *CharStringT<char32_t>::get_data() const {
if (size()) {
return &operator[](0);
} else {
return U"";
}
}
template <>
const wchar_t *CharStringT<wchar_t>::get_data() const {
if (size()) {
return &operator[](0);
} else {
return L"";
}
}
template <typename T>
void CharStringT<T>::copy_from(const T *p_cstr) {
if (!p_cstr) {
resize(0);
return;
}
size_t len = std::char_traits<T>::length(p_cstr);
if (len == 0) {
resize(0);
return;
}
Error err = resize(++len); // include terminating null char
ERR_FAIL_COND_MSG(err != OK, "Failed to copy C-string.");
memcpy(ptrw(), p_cstr, len);
}
template class CharStringT<char>;
template class CharStringT<char16_t>;
template class CharStringT<char32_t>;
template class CharStringT<wchar_t>;
// Custom String functions that are not part of bound API.
// It's easier to have them written in C++ directly than in a Python script that generates them.
@@ -168,23 +172,23 @@ String::String(const char32_t *from) {
internal::gdextension_interface_string_new_with_utf32_chars(_native_ptr(), from);
}
String String::utf8(const char *from, int len) {
String String::utf8(const char *from, int64_t len) {
String ret;
ret.parse_utf8(from, len);
return ret;
}
void String::parse_utf8(const char *from, int len) {
void String::parse_utf8(const char *from, int64_t len) {
internal::gdextension_interface_string_new_with_utf8_chars_and_len(_native_ptr(), from, len);
}
String String::utf16(const char16_t *from, int len) {
String String::utf16(const char16_t *from, int64_t len) {
String ret;
ret.parse_utf16(from, len);
return ret;
}
void String::parse_utf16(const char16_t *from, int len) {
void String::parse_utf16(const char16_t *from, int64_t len) {
internal::gdextension_interface_string_new_with_utf16_chars_and_len(_native_ptr(), from, len);
}
@@ -226,58 +230,63 @@ String rtoss(double p_val) {
}
CharString String::utf8() const {
int length = internal::gdextension_interface_string_to_utf8_chars(_native_ptr(), nullptr, 0);
int size = length + 1;
char *cstr = memnew_arr(char, size);
internal::gdextension_interface_string_to_utf8_chars(_native_ptr(), cstr, length);
int64_t length = internal::gdextension_interface_string_to_utf8_chars(_native_ptr(), nullptr, 0);
int64_t size = length + 1;
CharString str;
str.resize(size);
internal::gdextension_interface_string_to_utf8_chars(_native_ptr(), str.ptrw(), length);
cstr[length] = '\0';
str[length] = '\0';
return CharString(cstr, length);
return str;
}
CharString String::ascii() const {
int length = internal::gdextension_interface_string_to_latin1_chars(_native_ptr(), nullptr, 0);
int size = length + 1;
char *cstr = memnew_arr(char, size);
internal::gdextension_interface_string_to_latin1_chars(_native_ptr(), cstr, length);
int64_t length = internal::gdextension_interface_string_to_latin1_chars(_native_ptr(), nullptr, 0);
int64_t size = length + 1;
CharString str;
str.resize(size);
internal::gdextension_interface_string_to_latin1_chars(_native_ptr(), str.ptrw(), length);
cstr[length] = '\0';
str[length] = '\0';
return CharString(cstr, length);
return str;
}
Char16String String::utf16() const {
int length = internal::gdextension_interface_string_to_utf16_chars(_native_ptr(), nullptr, 0);
int size = length + 1;
char16_t *cstr = memnew_arr(char16_t, size);
internal::gdextension_interface_string_to_utf16_chars(_native_ptr(), cstr, length);
int64_t length = internal::gdextension_interface_string_to_utf16_chars(_native_ptr(), nullptr, 0);
int64_t size = length + 1;
Char16String str;
str.resize(size);
internal::gdextension_interface_string_to_utf16_chars(_native_ptr(), str.ptrw(), length);
cstr[length] = '\0';
str[length] = '\0';
return Char16String(cstr, length);
return str;
}
Char32String String::utf32() const {
int length = internal::gdextension_interface_string_to_utf32_chars(_native_ptr(), nullptr, 0);
int size = length + 1;
char32_t *cstr = memnew_arr(char32_t, size);
internal::gdextension_interface_string_to_utf32_chars(_native_ptr(), cstr, length);
int64_t length = internal::gdextension_interface_string_to_utf32_chars(_native_ptr(), nullptr, 0);
int64_t size = length + 1;
Char32String str;
str.resize(size);
internal::gdextension_interface_string_to_utf32_chars(_native_ptr(), str.ptrw(), length);
cstr[length] = '\0';
str[length] = '\0';
return Char32String(cstr, length);
return str;
}
CharWideString String::wide_string() const {
int length = internal::gdextension_interface_string_to_wide_chars(_native_ptr(), nullptr, 0);
int size = length + 1;
wchar_t *cstr = memnew_arr(wchar_t, size);
internal::gdextension_interface_string_to_wide_chars(_native_ptr(), cstr, length);
int64_t length = internal::gdextension_interface_string_to_wide_chars(_native_ptr(), nullptr, 0);
int64_t size = length + 1;
CharWideString str;
str.resize(size);
internal::gdextension_interface_string_to_wide_chars(_native_ptr(), str.ptrw(), length);
cstr[length] = '\0';
str[length] = '\0';
return CharWideString(cstr, length);
return str;
}
String &String::operator=(const char *p_str) {
@@ -377,11 +386,11 @@ String &String::operator+=(const char32_t *p_str) {
return *this;
}
const char32_t &String::operator[](int p_index) const {
const char32_t &String::operator[](int64_t p_index) const {
return *internal::gdextension_interface_string_operator_index_const((GDExtensionStringPtr)this, p_index);
}
char32_t &String::operator[](int p_index) {
char32_t &String::operator[](int64_t p_index) {
return *internal::gdextension_interface_string_operator_index((GDExtensionStringPtr)this, p_index);
}

View File

@@ -46,11 +46,11 @@
namespace godot {
const uint8_t &PackedByteArray::operator[](int p_index) const {
const uint8_t &PackedByteArray::operator[](int64_t p_index) const {
return *internal::gdextension_interface_packed_byte_array_operator_index_const((GDExtensionTypePtr *)this, p_index);
}
uint8_t &PackedByteArray::operator[](int p_index) {
uint8_t &PackedByteArray::operator[](int64_t p_index) {
return *internal::gdextension_interface_packed_byte_array_operator_index((GDExtensionTypePtr *)this, p_index);
}
@@ -62,12 +62,12 @@ uint8_t *PackedByteArray::ptrw() {
return internal::gdextension_interface_packed_byte_array_operator_index((GDExtensionTypePtr *)this, 0);
}
const Color &PackedColorArray::operator[](int p_index) const {
const Color &PackedColorArray::operator[](int64_t p_index) const {
const Color *color = (const Color *)internal::gdextension_interface_packed_color_array_operator_index_const((GDExtensionTypePtr *)this, p_index);
return *color;
}
Color &PackedColorArray::operator[](int p_index) {
Color &PackedColorArray::operator[](int64_t p_index) {
Color *color = (Color *)internal::gdextension_interface_packed_color_array_operator_index((GDExtensionTypePtr *)this, p_index);
return *color;
}
@@ -80,11 +80,11 @@ Color *PackedColorArray::ptrw() {
return (Color *)internal::gdextension_interface_packed_color_array_operator_index((GDExtensionTypePtr *)this, 0);
}
const float &PackedFloat32Array::operator[](int p_index) const {
const float &PackedFloat32Array::operator[](int64_t p_index) const {
return *internal::gdextension_interface_packed_float32_array_operator_index_const((GDExtensionTypePtr *)this, p_index);
}
float &PackedFloat32Array::operator[](int p_index) {
float &PackedFloat32Array::operator[](int64_t p_index) {
return *internal::gdextension_interface_packed_float32_array_operator_index((GDExtensionTypePtr *)this, p_index);
}
@@ -96,11 +96,11 @@ float *PackedFloat32Array::ptrw() {
return internal::gdextension_interface_packed_float32_array_operator_index((GDExtensionTypePtr *)this, 0);
}
const double &PackedFloat64Array::operator[](int p_index) const {
const double &PackedFloat64Array::operator[](int64_t p_index) const {
return *internal::gdextension_interface_packed_float64_array_operator_index_const((GDExtensionTypePtr *)this, p_index);
}
double &PackedFloat64Array::operator[](int p_index) {
double &PackedFloat64Array::operator[](int64_t p_index) {
return *internal::gdextension_interface_packed_float64_array_operator_index((GDExtensionTypePtr *)this, p_index);
}
@@ -112,11 +112,11 @@ double *PackedFloat64Array::ptrw() {
return internal::gdextension_interface_packed_float64_array_operator_index((GDExtensionTypePtr *)this, 0);
}
const int32_t &PackedInt32Array::operator[](int p_index) const {
const int32_t &PackedInt32Array::operator[](int64_t p_index) const {
return *internal::gdextension_interface_packed_int32_array_operator_index_const((GDExtensionTypePtr *)this, p_index);
}
int32_t &PackedInt32Array::operator[](int p_index) {
int32_t &PackedInt32Array::operator[](int64_t p_index) {
return *internal::gdextension_interface_packed_int32_array_operator_index((GDExtensionTypePtr *)this, p_index);
}
@@ -128,11 +128,11 @@ int32_t *PackedInt32Array::ptrw() {
return internal::gdextension_interface_packed_int32_array_operator_index((GDExtensionTypePtr *)this, 0);
}
const int64_t &PackedInt64Array::operator[](int p_index) const {
const int64_t &PackedInt64Array::operator[](int64_t p_index) const {
return *internal::gdextension_interface_packed_int64_array_operator_index_const((GDExtensionTypePtr *)this, p_index);
}
int64_t &PackedInt64Array::operator[](int p_index) {
int64_t &PackedInt64Array::operator[](int64_t p_index) {
return *internal::gdextension_interface_packed_int64_array_operator_index((GDExtensionTypePtr *)this, p_index);
}
@@ -144,12 +144,12 @@ int64_t *PackedInt64Array::ptrw() {
return internal::gdextension_interface_packed_int64_array_operator_index((GDExtensionTypePtr *)this, 0);
}
const String &PackedStringArray::operator[](int p_index) const {
const String &PackedStringArray::operator[](int64_t p_index) const {
const String *string = (const String *)internal::gdextension_interface_packed_string_array_operator_index_const((GDExtensionTypePtr *)this, p_index);
return *string;
}
String &PackedStringArray::operator[](int p_index) {
String &PackedStringArray::operator[](int64_t p_index) {
String *string = (String *)internal::gdextension_interface_packed_string_array_operator_index((GDExtensionTypePtr *)this, p_index);
return *string;
}
@@ -162,12 +162,12 @@ String *PackedStringArray::ptrw() {
return (String *)internal::gdextension_interface_packed_string_array_operator_index((GDExtensionTypePtr *)this, 0);
}
const Vector2 &PackedVector2Array::operator[](int p_index) const {
const Vector2 &PackedVector2Array::operator[](int64_t p_index) const {
const Vector2 *vec = (const Vector2 *)internal::gdextension_interface_packed_vector2_array_operator_index_const((GDExtensionTypePtr *)this, p_index);
return *vec;
}
Vector2 &PackedVector2Array::operator[](int p_index) {
Vector2 &PackedVector2Array::operator[](int64_t p_index) {
Vector2 *vec = (Vector2 *)internal::gdextension_interface_packed_vector2_array_operator_index((GDExtensionTypePtr *)this, p_index);
return *vec;
}
@@ -180,12 +180,12 @@ Vector2 *PackedVector2Array::ptrw() {
return (Vector2 *)internal::gdextension_interface_packed_vector2_array_operator_index((GDExtensionTypePtr *)this, 0);
}
const Vector3 &PackedVector3Array::operator[](int p_index) const {
const Vector3 &PackedVector3Array::operator[](int64_t p_index) const {
const Vector3 *vec = (const Vector3 *)internal::gdextension_interface_packed_vector3_array_operator_index_const((GDExtensionTypePtr *)this, p_index);
return *vec;
}
Vector3 &PackedVector3Array::operator[](int p_index) {
Vector3 &PackedVector3Array::operator[](int64_t p_index) {
Vector3 *vec = (Vector3 *)internal::gdextension_interface_packed_vector3_array_operator_index((GDExtensionTypePtr *)this, p_index);
return *vec;
}
@@ -198,12 +198,12 @@ Vector3 *PackedVector3Array::ptrw() {
return (Vector3 *)internal::gdextension_interface_packed_vector3_array_operator_index((GDExtensionTypePtr *)this, 0);
}
const Variant &Array::operator[](int p_index) const {
const Variant &Array::operator[](int64_t p_index) const {
const Variant *var = (const Variant *)internal::gdextension_interface_array_operator_index_const((GDExtensionTypePtr *)this, p_index);
return *var;
}
Variant &Array::operator[](int p_index) {
Variant &Array::operator[](int64_t p_index) {
Variant *var = (Variant *)internal::gdextension_interface_array_operator_index((GDExtensionTypePtr *)this, p_index);
return *var;
}

View File

@@ -136,7 +136,7 @@ Projection Projection::create_for_hmd(int p_eye, real_t p_aspect, real_t p_intra
Projection Projection::create_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) {
Projection proj;
proj.set_orthogonal(p_left, p_right, p_bottom, p_top, p_zfar, p_zfar);
proj.set_orthogonal(p_left, p_right, p_bottom, p_top, p_znear, p_zfar);
return proj;
}

View File

@@ -268,6 +268,14 @@ Variant::operator int32_t() const {
return static_cast<int32_t>(operator int64_t());
}
Variant::operator int16_t() const {
return static_cast<int16_t>(operator int64_t());
}
Variant::operator int8_t() const {
return static_cast<int8_t>(operator int64_t());
}
Variant::operator uint64_t() const {
return static_cast<uint64_t>(operator int64_t());
}
@@ -276,6 +284,14 @@ Variant::operator uint32_t() const {
return static_cast<uint32_t>(operator int64_t());
}
Variant::operator uint16_t() const {
return static_cast<uint16_t>(operator int64_t());
}
Variant::operator uint8_t() const {
return static_cast<uint8_t>(operator int64_t());
}
Variant::operator double() const {
double result;
to_type_constructor[FLOAT](&result, _native_ptr());
@@ -287,123 +303,131 @@ Variant::operator float() const {
}
Variant::operator String() const {
String result;
to_type_constructor[STRING](result._native_ptr(), _native_ptr());
return result;
return String(this);
}
Variant::operator Vector2() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
Vector2 result;
to_type_constructor[VECTOR2]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator Vector2i() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
Vector2i result;
to_type_constructor[VECTOR2I]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator Rect2() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
Rect2 result;
to_type_constructor[RECT2]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator Rect2i() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
Rect2i result;
to_type_constructor[RECT2I]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator Vector3() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
Vector3 result;
to_type_constructor[VECTOR3]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator Vector3i() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
Vector3i result;
to_type_constructor[VECTOR3I]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator Transform2D() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
Transform2D result;
to_type_constructor[TRANSFORM2D]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator Vector4() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
Vector4 result;
to_type_constructor[VECTOR4]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator Vector4i() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
Vector4i result;
to_type_constructor[VECTOR4I]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator Plane() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
Plane result;
to_type_constructor[PLANE]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator Quaternion() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
Quaternion result;
to_type_constructor[QUATERNION]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator godot::AABB() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
godot::AABB result;
to_type_constructor[AABB]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator Basis() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
Basis result;
to_type_constructor[BASIS]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator Transform3D() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
Transform3D result;
to_type_constructor[TRANSFORM3D]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator Projection() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
Projection result;
to_type_constructor[PROJECTION]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator Color() const {
// @todo Avoid initializing result before calling constructor (which will initialize it again)
Color result;
to_type_constructor[COLOR]((GDExtensionTypePtr)&result, _native_ptr());
return result;
}
Variant::operator StringName() const {
StringName result;
to_type_constructor[STRING_NAME](result._native_ptr(), _native_ptr());
return result;
return StringName(this);
}
Variant::operator NodePath() const {
NodePath result;
to_type_constructor[NODE_PATH](result._native_ptr(), _native_ptr());
return result;
return NodePath(this);
}
Variant::operator godot::RID() const {
godot::RID result;
to_type_constructor[RID](result._native_ptr(), _native_ptr());
return result;
return godot::RID(this);
}
Variant::operator Object *() const {
@@ -431,81 +455,55 @@ Variant::operator ObjectID() const {
}
Variant::operator Callable() const {
Callable result;
to_type_constructor[CALLABLE](result._native_ptr(), _native_ptr());
return result;
return Callable(this);
}
Variant::operator Signal() const {
Signal result;
to_type_constructor[SIGNAL](result._native_ptr(), _native_ptr());
return result;
return Signal(this);
}
Variant::operator Dictionary() const {
Dictionary result;
to_type_constructor[DICTIONARY](result._native_ptr(), _native_ptr());
return result;
return Dictionary(this);
}
Variant::operator Array() const {
Array result;
to_type_constructor[ARRAY](result._native_ptr(), _native_ptr());
return result;
return Array(this);
}
Variant::operator PackedByteArray() const {
PackedByteArray result;
to_type_constructor[PACKED_BYTE_ARRAY](result._native_ptr(), _native_ptr());
return result;
return PackedByteArray(this);
}
Variant::operator PackedInt32Array() const {
PackedInt32Array result;
to_type_constructor[PACKED_INT32_ARRAY](result._native_ptr(), _native_ptr());
return result;
return PackedInt32Array(this);
}
Variant::operator PackedInt64Array() const {
PackedInt64Array result;
to_type_constructor[PACKED_INT64_ARRAY](result._native_ptr(), _native_ptr());
return result;
return PackedInt64Array(this);
}
Variant::operator PackedFloat32Array() const {
PackedFloat32Array result;
to_type_constructor[PACKED_FLOAT32_ARRAY](result._native_ptr(), _native_ptr());
return result;
return PackedFloat32Array(this);
}
Variant::operator PackedFloat64Array() const {
PackedFloat64Array result;
to_type_constructor[PACKED_FLOAT64_ARRAY](result._native_ptr(), _native_ptr());
return result;
return PackedFloat64Array(this);
}
Variant::operator PackedStringArray() const {
PackedStringArray result;
to_type_constructor[PACKED_STRING_ARRAY](result._native_ptr(), _native_ptr());
return result;
return PackedStringArray(this);
}
Variant::operator PackedVector2Array() const {
PackedVector2Array result;
to_type_constructor[PACKED_VECTOR2_ARRAY](result._native_ptr(), _native_ptr());
return result;
return PackedVector2Array(this);
}
Variant::operator PackedVector3Array() const {
PackedVector3Array result;
to_type_constructor[PACKED_VECTOR3_ARRAY](result._native_ptr(), _native_ptr());
return result;
return PackedVector3Array(this);
}
Variant::operator PackedColorArray() const {
PackedColorArray result;
to_type_constructor[PACKED_COLOR_ARRAY](result._native_ptr(), _native_ptr());
return result;
return PackedColorArray(this);
}
Variant &Variant::operator=(const Variant &other) {
@@ -549,11 +547,11 @@ bool Variant::operator<(const Variant &other) const {
return result.operator bool();
}
void Variant::call(const StringName &method, const Variant **args, int argcount, Variant &r_ret, GDExtensionCallError &r_error) {
void Variant::callp(const StringName &method, const Variant **args, int argcount, Variant &r_ret, GDExtensionCallError &r_error) {
internal::gdextension_interface_variant_call(_native_ptr(), method._native_ptr(), reinterpret_cast<GDExtensionConstVariantPtr *>(args), argcount, r_ret._native_ptr(), &r_error);
}
void Variant::call_static(Variant::Type type, const StringName &method, const Variant **args, int argcount, Variant &r_ret, GDExtensionCallError &r_error) {
void Variant::callp_static(Variant::Type type, const StringName &method, const Variant **args, int argcount, Variant &r_ret, GDExtensionCallError &r_error) {
internal::gdextension_interface_variant_call_static(static_cast<GDExtensionVariantType>(type), method._native_ptr(), reinterpret_cast<GDExtensionConstVariantPtr *>(args), argcount, r_ret._native_ptr(), &r_error);
}
@@ -638,14 +636,16 @@ bool Variant::in(const Variant &index, bool *r_valid) const {
bool Variant::iter_init(Variant &r_iter, bool &r_valid) const {
GDExtensionBool valid;
internal::gdextension_interface_variant_iter_init(_native_ptr(), r_iter._native_ptr(), &valid);
return PtrToArg<bool>::convert(&valid);
GDExtensionBool result = internal::gdextension_interface_variant_iter_init(_native_ptr(), r_iter._native_ptr(), &valid);
r_valid = PtrToArg<bool>::convert(&valid);
return PtrToArg<bool>::convert(&result);
}
bool Variant::iter_next(Variant &r_iter, bool &r_valid) const {
GDExtensionBool valid;
internal::gdextension_interface_variant_iter_next(_native_ptr(), r_iter._native_ptr(), &valid);
return PtrToArg<bool>::convert(&valid);
GDExtensionBool result = internal::gdextension_interface_variant_iter_next(_native_ptr(), r_iter._native_ptr(), &valid);
r_valid = PtrToArg<bool>::convert(&valid);
return PtrToArg<bool>::convert(&result);
}
Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const {

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.13)
project(godot-cpp-test)
cmake_minimum_required(VERSION 3.6)
set(GODOT_GDEXTENSION_DIR ../gdextension/ CACHE STRING "Path to GDExtension interface header directory")
set(CPP_BINDINGS_PATH ../ CACHE STRING "Path to C++ bindings")
@@ -36,7 +36,7 @@ set(GODOT_LINKER_FLAGS )
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# using Visual Studio C++
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /EHsc /WX") # /GF /MP
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /WX") # /GF /MP
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /DTYPED_METHOD_BIND")
if(CMAKE_BUILD_TYPE MATCHES Debug)
@@ -59,31 +59,9 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
else()
#elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# using Clang
#elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# using GCC and maybe MinGW?
set(GODOT_LINKER_FLAGS "-static-libgcc -static-libstdc++ -Wl,-R,'$$ORIGIN'")
# Hmm.. maybe to strikt?
set(GODOT_COMPILE_FLAGS "-fPIC -g -Wwrite-strings")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wchar-subscripts -Wcomment -Wdisabled-optimization")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wformat -Wformat=2 -Wformat-security -Wformat-y2k")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wimport -Winit-self -Winline -Winvalid-pch -Werror")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wmissing-braces -Wmissing-format-attribute")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wmissing-include-dirs -Wmissing-noreturn -Wpacked -Wpointer-arith")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wredundant-decls -Wreturn-type -Wsequence-point")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wswitch -Wswitch-enum -Wtrigraphs")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wuninitialized -Wunknown-pragmas -Wunreachable-code -Wunused-label")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wunused-value -Wvariadic-macros -Wvolatile-register-var -Wno-error=attributes")
# -Wshadow -Wextra -Wall -Weffc++ -Wfloat-equal -Wstack-protector -Wunused-parameter -Wsign-compare -Wunused-variable -Wcast-align
# -Wunused-function -Wstrict-aliasing -Wstrict-aliasing=2 -Wmissing-field-initializers
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wno-ignored-attributes")
endif()
if(CMAKE_BUILD_TYPE MATCHES Debug)
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0")
@@ -92,6 +70,21 @@ else()
endif(CMAKE_BUILD_TYPE MATCHES Debug)
endif()
# Disable exception handling. Godot doesn't use exceptions anywhere, and this
# saves around 20% of binary size and very significant build time (GH-80513).
option(GODOT_DISABLE_EXCEPTIONS OFF "Force disabling exception handling code")
if (GODOT_DISABLE_EXCEPTIONS)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -D_HAS_EXCEPTIONS=0")
else()
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-exceptions")
endif()
else()
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /EHsc")
endif()
endif()
# Get Sources
file(GLOB_RECURSE SOURCES src/*.c**)
file(GLOB_RECURSE HEADERS include/*.h**)

View File

@@ -1,7 +1,7 @@
[configuration]
entry_symbol = "example_library_init"
compatibility_minimum = 4.1
compatibility_minimum = "4.1"
[libraries]
@@ -21,3 +21,5 @@ android.debug.x86_64 = "res://bin/libgdexample.android.template_debug.x86_64.so"
android.release.x86_64 = "res://bin/libgdexample.android.template_release.x86_64.so"
android.debug.arm64 = "res://bin/libgdexample.android.template_debug.arm64.so"
android.release.arm64 = "res://bin/libgdexample.android.template_release.arm64.so"
web.debug.wasm32 = "res://bin/libgdexample.web.template_debug.wasm32.wasm"
web.release.wasm32 = "res://bin/libgdexample.web.template_release.wasm32.wasm"

View File

@@ -2,6 +2,9 @@ extends "res://test_base.gd"
var custom_signal_emitted = null
class TestClass:
func test(p_msg: String) -> String:
return p_msg + " world"
func _ready():
var example: Example = $Example
@@ -82,14 +85,66 @@ func _ready():
# UtilityFunctions::str()
assert_equal(example.test_str_utility(), "Hello, World! The answer is 42")
# Test converting string to char* and doing comparison.
assert_equal(example.test_string_is_fourty_two("blah"), false)
assert_equal(example.test_string_is_fourty_two("fourty two"), true)
# PackedArray iterators
assert_equal(example.test_vector_ops(), 105)
assert_equal(example.test_vector_init_list(), 105)
# Properties.
assert_equal(example.group_subgroup_custom_position, Vector2(0, 0))
example.group_subgroup_custom_position = Vector2(50, 50)
assert_equal(example.group_subgroup_custom_position, Vector2(50, 50))
# Test Object::cast_to<>() and that correct wrappers are being used.
var control = Control.new()
var sprite = Sprite2D.new()
var example_ref = ExampleRef.new()
assert_equal(example.test_object_cast_to_node(control), true)
assert_equal(example.test_object_cast_to_control(control), true)
assert_equal(example.test_object_cast_to_example(control), false)
assert_equal(example.test_object_cast_to_node(example), true)
assert_equal(example.test_object_cast_to_control(example), true)
assert_equal(example.test_object_cast_to_example(example), true)
assert_equal(example.test_object_cast_to_node(sprite), true)
assert_equal(example.test_object_cast_to_control(sprite), false)
assert_equal(example.test_object_cast_to_example(sprite), false)
assert_equal(example.test_object_cast_to_node(example_ref), false)
assert_equal(example.test_object_cast_to_control(example_ref), false)
assert_equal(example.test_object_cast_to_example(example_ref), false)
control.queue_free()
sprite.queue_free()
# Test conversions to and from Variant.
assert_equal(example.test_variant_vector2i_conversion(Vector2i(1, 1)), Vector2i(1, 1))
assert_equal(example.test_variant_vector2i_conversion(Vector2(1.0, 1.0)), Vector2i(1, 1))
assert_equal(example.test_variant_int_conversion(10), 10)
assert_equal(example.test_variant_int_conversion(10.0), 10)
assert_equal(example.test_variant_float_conversion(10.0), 10.0)
assert_equal(example.test_variant_float_conversion(10), 10.0)
# Test that ptrcalls from GDExtension to the engine are correctly encoding Object and RefCounted.
var new_node = Node.new()
example.test_add_child(new_node)
assert_equal(new_node.get_parent(), example)
var new_tileset = TileSet.new()
var new_tilemap = TileMap.new()
example.test_set_tileset(new_tilemap, new_tileset)
assert_equal(new_tilemap.tile_set, new_tileset)
new_tilemap.queue_free()
# Test variant call.
var test_obj = TestClass.new()
assert_equal(example.test_variant_call(test_obj), "hello world")
# Constants.
assert_equal(Example.FIRST, 0)
assert_equal(Example.ANSWER_TO_EVERYTHING, 42)
@@ -115,6 +170,16 @@ func _ready():
get_viewport().push_input(event)
assert_equal(custom_signal_emitted, ["_input: H", 72])
# Check NOTIFICATION_POST_INITIALIZED, both when created from GDScript and godot-cpp.
var new_example_ref = ExampleRef.new()
assert_equal(new_example_ref.was_post_initialized(), true)
assert_equal(example.test_post_initialize(), true)
# Test that notifications happen on both parent and child classes.
var example_child = $ExampleChild
assert_equal(example_child.get_value1(), 11)
assert_equal(example_child.get_value2(), 33)
exit_with_status()
func _on_Example_custom_signal(signal_name, value):

View File

@@ -22,4 +22,6 @@ offset_right = 79.0
offset_bottom = 29.0
text = "Click me!"
[node name="ExampleChild" type="ExampleChild" parent="."]
[connection signal="custom_signal" from="Example" to="." method="_on_Example_custom_signal"]

View File

@@ -23,10 +23,18 @@ int ExampleRef::get_id() const {
return id;
}
void ExampleRef::_notification(int p_what) {
if (p_what == NOTIFICATION_POSTINITIALIZE) {
post_initialized = true;
}
}
void ExampleRef::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_id", "id"), &ExampleRef::set_id);
ClassDB::bind_method(D_METHOD("get_id"), &ExampleRef::get_id);
ClassDB::bind_method(D_METHOD("was_post_initialized"), &ExampleRef::was_post_initialized);
ADD_PROPERTY(PropertyInfo(Variant::INT, "id"), "set_id", "get_id");
}
@@ -138,7 +146,22 @@ void Example::_bind_methods() {
ClassDB::bind_method(D_METHOD("test_node_argument"), &Example::test_node_argument);
ClassDB::bind_method(D_METHOD("test_string_ops"), &Example::test_string_ops);
ClassDB::bind_method(D_METHOD("test_str_utility"), &Example::test_str_utility);
ClassDB::bind_method(D_METHOD("test_string_is_fourty_two"), &Example::test_string_is_fourty_two);
ClassDB::bind_method(D_METHOD("test_vector_ops"), &Example::test_vector_ops);
ClassDB::bind_method(D_METHOD("test_vector_init_list"), &Example::test_vector_init_list);
ClassDB::bind_method(D_METHOD("test_object_cast_to_node", "object"), &Example::test_object_cast_to_node);
ClassDB::bind_method(D_METHOD("test_object_cast_to_control", "object"), &Example::test_object_cast_to_control);
ClassDB::bind_method(D_METHOD("test_object_cast_to_example", "object"), &Example::test_object_cast_to_example);
ClassDB::bind_method(D_METHOD("test_variant_vector2i_conversion", "variant"), &Example::test_variant_vector2i_conversion);
ClassDB::bind_method(D_METHOD("test_variant_int_conversion", "variant"), &Example::test_variant_int_conversion);
ClassDB::bind_method(D_METHOD("test_variant_float_conversion", "variant"), &Example::test_variant_float_conversion);
ClassDB::bind_method(D_METHOD("test_add_child", "node"), &Example::test_add_child);
ClassDB::bind_method(D_METHOD("test_set_tileset", "tilemap", "tileset"), &Example::test_set_tileset);
ClassDB::bind_method(D_METHOD("test_variant_call", "variant"), &Example::test_variant_call);
ClassDB::bind_method(D_METHOD("test_bitfield", "flags"), &Example::test_bitfield);
@@ -147,6 +170,7 @@ void Example::_bind_methods() {
ClassDB::bind_method(D_METHOD("return_last_rpc_arg"), &Example::return_last_rpc_arg);
ClassDB::bind_method(D_METHOD("def_args", "a", "b"), &Example::def_args, DEFVAL(100), DEFVAL(200));
ClassDB::bind_method(D_METHOD("test_post_initialize"), &Example::test_post_initialize);
ClassDB::bind_static_method("Example", D_METHOD("test_static", "a", "b"), &Example::test_static);
ClassDB::bind_static_method("Example", D_METHOD("test_static2"), &Example::test_static2);
@@ -299,6 +323,10 @@ String Example::test_str_utility() const {
return UtilityFunctions::str("Hello, ", "World", "! The answer is ", 42);
}
bool Example::test_string_is_fourty_two(const String &p_string) const {
return strcmp(p_string.utf8().ptr(), "fourty two") == 0;
}
int Example::test_vector_ops() const {
PackedInt32Array arr;
arr.push_back(10);
@@ -312,6 +340,15 @@ int Example::test_vector_ops() const {
return ret;
}
int Example::test_vector_init_list() const {
PackedInt32Array arr = { 10, 20, 30, 45 };
int ret = 0;
for (const int32_t &E : arr) {
ret += E;
}
return ret;
}
int Example::test_tarray_arg(const TypedArray<int64_t> &p_array) {
int sum = 0;
for (int i = 0; i < p_array.size(); i++) {
@@ -343,6 +380,42 @@ Example *Example::test_node_argument(Example *p_node) const {
return p_node;
}
bool Example::test_object_cast_to_node(Object *p_object) const {
return Object::cast_to<Node>(p_object) != nullptr;
}
bool Example::test_object_cast_to_control(Object *p_object) const {
return Object::cast_to<Control>(p_object) != nullptr;
}
bool Example::test_object_cast_to_example(Object *p_object) const {
return Object::cast_to<Example>(p_object) != nullptr;
}
Vector2i Example::test_variant_vector2i_conversion(const Variant &p_variant) const {
return p_variant;
}
int Example::test_variant_int_conversion(const Variant &p_variant) const {
return p_variant;
}
float Example::test_variant_float_conversion(const Variant &p_variant) const {
return p_variant;
}
void Example::test_add_child(Node *p_node) {
add_child(p_node);
}
void Example::test_set_tileset(TileMap *p_tilemap, const Ref<TileSet> &p_tileset) const {
p_tilemap->set_tileset(p_tileset);
}
Variant Example::test_variant_call(Variant p_variant) {
return p_variant.call("test", "hello");
}
BitField<Example::Flags> Example::test_bitfield(BitField<Flags> flags) {
return flags;
}
@@ -372,6 +445,12 @@ Vector4 Example::get_v4() const {
return Vector4(1.2, 3.4, 5.6, 7.8);
}
bool Example::test_post_initialize() const {
Ref<ExampleRef> new_example_ref;
new_example_ref.instantiate();
return new_example_ref->was_post_initialized();
}
// Virtual function override.
bool Example::_has_point(const Vector2 &point) const {
Label *label = get_node<Label>("Label");
@@ -386,3 +465,21 @@ void Example::_input(const Ref<InputEvent> &event) {
emit_custom_signal(String("_input: ") + key_event->get_key_label(), key_event->get_unicode());
}
}
void ExampleBase::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_value1"), &ExampleBase::get_value1);
ClassDB::bind_method(D_METHOD("get_value2"), &ExampleBase::get_value2);
}
void ExampleBase::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
value1 = 11;
value2 = 22;
}
}
void ExampleChild::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
value2 = 33;
}
}

View File

@@ -18,7 +18,10 @@
#include <godot_cpp/classes/global_constants.hpp>
#include <godot_cpp/classes/image.hpp>
#include <godot_cpp/classes/input_event_key.hpp>
#include <godot_cpp/classes/tile_map.hpp>
#include <godot_cpp/classes/tile_set.hpp>
#include <godot_cpp/classes/viewport.hpp>
#include <godot_cpp/variant/variant.hpp>
#include <godot_cpp/core/binder_common.hpp>
@@ -32,16 +35,21 @@ private:
static int last_id;
int id;
bool post_initialized = false;
protected:
static void _bind_methods();
void _notification(int p_what);
public:
ExampleRef();
~ExampleRef();
void set_id(int p_id);
int get_id() const;
bool was_post_initialized() const { return post_initialized; }
};
class ExampleMin : public Control {
@@ -117,7 +125,22 @@ public:
Example *test_node_argument(Example *p_node) const;
String test_string_ops() const;
String test_str_utility() const;
bool test_string_is_fourty_two(const String &p_str) const;
int test_vector_ops() const;
int test_vector_init_list() const;
bool test_object_cast_to_node(Object *p_object) const;
bool test_object_cast_to_control(Object *p_object) const;
bool test_object_cast_to_example(Object *p_object) const;
Vector2i test_variant_vector2i_conversion(const Variant &p_variant) const;
int test_variant_int_conversion(const Variant &p_variant) const;
float test_variant_float_conversion(const Variant &p_variant) const;
void test_add_child(Node *p_node);
void test_set_tileset(TileMap *p_tilemap, const Ref<TileSet> &p_tileset) const;
Variant test_variant_call(Variant p_variant);
BitField<Flags> test_bitfield(BitField<Flags> flags);
@@ -131,6 +154,8 @@ public:
Vector2 get_custom_position() const;
Vector4 get_v4() const;
bool test_post_initialize() const;
// Static method.
static int test_static(int p_a, int p_b);
static void test_static2();
@@ -162,4 +187,29 @@ protected:
static void _bind_methods() {}
};
class ExampleBase : public Node {
GDCLASS(ExampleBase, Node);
protected:
int value1 = 0;
int value2 = 0;
static void _bind_methods();
void _notification(int p_what);
public:
int get_value1() { return value1; }
int get_value2() { return value2; }
};
class ExampleChild : public ExampleBase {
GDCLASS(ExampleChild, ExampleBase);
protected:
static void _bind_methods() {}
void _notification(int p_what);
};
#endif // EXAMPLE_CLASS_H

View File

@@ -21,11 +21,13 @@ void initialize_example_module(ModuleInitializationLevel p_level) {
return;
}
ClassDB::register_class<ExampleRef>();
ClassDB::register_class<ExampleMin>();
ClassDB::register_class<Example>();
ClassDB::register_class<ExampleVirtual>(true);
ClassDB::register_abstract_class<ExampleAbstract>();
GDREGISTER_CLASS(ExampleRef);
GDREGISTER_CLASS(ExampleMin);
GDREGISTER_CLASS(Example);
GDREGISTER_VIRTUAL_CLASS(ExampleVirtual);
GDREGISTER_ABSTRACT_CLASS(ExampleAbstract);
GDREGISTER_CLASS(ExampleBase);
GDREGISTER_CLASS(ExampleChild);
}
void uninitialize_example_module(ModuleInitializationLevel p_level) {

View File

@@ -1,6 +1,7 @@
import os
import sys
import my_spawn
import common_compiler_flags
from SCons.Script import ARGUMENTS
@@ -8,41 +9,51 @@ def options(opts):
opts.Add(
"android_api_level",
"Target Android API level",
"18" if "32" in ARGUMENTS.get("arch", "arm64") else "21",
"21",
)
opts.Add(
"ANDROID_NDK_ROOT",
"Path to your Android NDK installation. By default, uses ANDROID_NDK_ROOT from your defined environment variables.",
os.environ.get("ANDROID_NDK_ROOT", None),
"ANDROID_HOME",
"Path to your Android SDK installation. By default, uses ANDROID_HOME from your defined environment variables.",
os.environ.get("ANDROID_HOME", os.environ.get("ANDROID_SDK_ROOT")),
)
def exists(env):
return "ANDROID_NDK_ROOT" in os.environ or "ANDROID_NDK_ROOT" in ARGUMENTS
return get_android_ndk_root(env) is not None
# This must be kept in sync with the value in https://github.com/godotengine/godot/blob/master/platform/android/detect.py#L58.
def get_ndk_version():
return "23.2.8568313"
def get_android_ndk_root(env):
if env["ANDROID_HOME"]:
return env["ANDROID_HOME"] + "/ndk/" + get_ndk_version()
else:
return os.environ.get("ANDROID_NDK_ROOT")
def generate(env):
if "ANDROID_NDK_ROOT" not in env:
if get_android_ndk_root(env) is None:
raise ValueError(
"To build for Android, ANDROID_NDK_ROOT must be defined. Please set ANDROID_NDK_ROOT to the root folder of your Android NDK installation."
"To build for Android, the path to the NDK must be defined. Please set ANDROID_HOME to the root folder of your Android SDK installation."
)
if env["arch"] not in ("arm64", "x86_64", "arm32", "x86_32"):
print("Only arm64, x86_64, arm32, and x86_32 are supported on Android. Exiting.")
Exit()
env.Exit(1)
if sys.platform == "win32" or sys.platform == "msys":
my_spawn.configure(env)
# Validate API level
api_level = int(env["android_api_level"])
if "64" in env["arch"] and api_level < 21:
print("WARN: 64-bit Android architectures require an API level of at least 21; setting android_api_level=21")
if int(env["android_api_level"]) < 21:
print("WARNING: minimum supported Android target api is 21. Forcing target api 21.")
env["android_api_level"] = "21"
api_level = 21
# Setup toolchain
toolchain = env["ANDROID_NDK_ROOT"] + "/toolchains/llvm/prebuilt/"
toolchain = get_android_ndk_root(env) + "/toolchains/llvm/prebuilt/"
if sys.platform == "win32" or sys.platform == "msys":
toolchain += "windows"
import platform as pltfm
@@ -54,6 +65,12 @@ def generate(env):
elif sys.platform == "darwin":
toolchain += "darwin-x86_64"
env.Append(LINKFLAGS=["-shared"])
if not os.path.exists(toolchain):
print("ERROR: Could not find NDK toolchain at " + toolchain + ".")
print("Make sure NDK version " + get_ndk_version() + " is installed.")
env.Exit(1)
env.PrependENVPath("PATH", toolchain + "/bin") # This does nothing half of the time, but we'll put it here anyways
# Get architecture info
@@ -100,3 +117,7 @@ def generate(env):
)
env.Append(CCFLAGS=arch_info["ccflags"])
env.Append(LINKFLAGS=["--target=" + arch_info["target"] + env["android_api_level"], "-march=" + arch_info["march"]])
env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED"])
common_compiler_flags.generate(env)

View File

@@ -0,0 +1,86 @@
import os
import subprocess
import sys
def using_clang(env):
return "clang" in os.path.basename(env["CC"])
def is_vanilla_clang(env):
if not using_clang(env):
return False
try:
version = subprocess.check_output([env.subst(env["CXX"]), "--version"]).strip().decode("utf-8")
except (subprocess.CalledProcessError, OSError):
print("Couldn't parse CXX environment variable to infer compiler version.")
return False
return not version.startswith("Apple")
def exists(env):
return True
def generate(env):
# Require C++17
if env.get("is_msvc", False):
env.Append(CXXFLAGS=["/std:c++17"])
else:
env.Append(CXXFLAGS=["-std=c++17"])
# Disable exception handling. Godot doesn't use exceptions anywhere, and this
# saves around 20% of binary size and very significant build time.
if env["disable_exceptions"]:
if env.get("is_msvc", False):
env.Append(CPPDEFINES=[("_HAS_EXCEPTIONS", 0)])
else:
env.Append(CXXFLAGS=["-fno-exceptions"])
elif env.get("is_msvc", False):
env.Append(CXXFLAGS=["/EHsc"])
# Set optimize and debug_symbols flags.
# "custom" means do nothing and let users set their own optimization flags.
if env.get("is_msvc", False):
if env["debug_symbols"]:
env.Append(CCFLAGS=["/Zi", "/FS"])
env.Append(LINKFLAGS=["/DEBUG:FULL"])
if env["optimize"] == "speed":
env.Append(CCFLAGS=["/O2"])
env.Append(LINKFLAGS=["/OPT:REF"])
elif env["optimize"] == "speed_trace":
env.Append(CCFLAGS=["/O2"])
env.Append(LINKFLAGS=["/OPT:REF", "/OPT:NOICF"])
elif env["optimize"] == "size":
env.Append(CCFLAGS=["/O1"])
env.Append(LINKFLAGS=["/OPT:REF"])
elif env["optimize"] == "debug" or env["optimize"] == "none":
env.Append(CCFLAGS=["/Od"])
else:
if env["debug_symbols"]:
# Adding dwarf-4 explicitly makes stacktraces work with clang builds,
# otherwise addr2line doesn't understand them.
env.Append(CCFLAGS=["-gdwarf-4"])
if env.dev_build:
env.Append(CCFLAGS=["-g3"])
else:
env.Append(CCFLAGS=["-g2"])
else:
if using_clang(env) and not is_vanilla_clang(env):
# Apple Clang, its linker doesn't like -s.
env.Append(LINKFLAGS=["-Wl,-S", "-Wl,-x", "-Wl,-dead_strip"])
else:
env.Append(LINKFLAGS=["-s"])
if env["optimize"] == "speed":
env.Append(CCFLAGS=["-O3"])
# `-O2` is friendlier to debuggers than `-O3`, leading to better crash backtraces.
elif env["optimize"] == "speed_trace":
env.Append(CCFLAGS=["-O2"])
elif env["optimize"] == "size":
env.Append(CCFLAGS=["-Os"])
elif env["optimize"] == "debug":
env.Append(CCFLAGS=["-Og"])
elif env["optimize"] == "none":
env.Append(CCFLAGS=["-O0"])

482
tools/godotcpp.py Normal file
View File

@@ -0,0 +1,482 @@
import os, sys, platform
from SCons.Variables import EnumVariable, PathVariable, BoolVariable
from SCons.Variables.BoolVariable import _text2bool
from SCons.Tool import Tool
from SCons.Action import Action
from SCons.Builder import Builder
from SCons.Errors import UserError
from SCons.Script import ARGUMENTS
from binding_generator import scons_generate_bindings, scons_emit_files
def add_sources(sources, dir, extension):
for f in os.listdir(dir):
if f.endswith("." + extension):
sources.append(dir + "/" + f)
def get_cmdline_bool(option, default):
"""We use `ARGUMENTS.get()` to check if options were manually overridden on the command line,
and SCons' _text2bool helper to convert them to booleans, otherwise they're handled as strings.
"""
cmdline_val = ARGUMENTS.get(option)
if cmdline_val is not None:
return _text2bool(cmdline_val)
else:
return default
def normalize_path(val, env):
return val if os.path.isabs(val) else os.path.join(env.Dir("#").abspath, val)
def validate_file(key, val, env):
if not os.path.isfile(normalize_path(val, env)):
raise UserError("'%s' is not a file: %s" % (key, val))
def validate_dir(key, val, env):
if not os.path.isdir(normalize_path(val, env)):
raise UserError("'%s' is not a directory: %s" % (key, val))
def validate_parent_dir(key, val, env):
if not os.path.isdir(normalize_path(os.path.dirname(val), env)):
raise UserError("'%s' is not a directory: %s" % (key, os.path.dirname(val)))
def get_platform_tools_paths(env):
path = env.get("custom_tools", None)
if path is None:
return ["tools"]
return [normalize_path(path, env), "tools"]
def get_custom_platforms(env):
path = env.get("custom_tools", None)
if path is None:
return []
platforms = []
for x in os.listdir(normalize_path(path, env)):
if not x.endswith(".py"):
continue
platforms.append(x.removesuffix(".py"))
return platforms
def no_verbose(env):
colors = {}
# Colors are disabled in non-TTY environments such as pipes. This means
# that if output is redirected to a file, it will not contain color codes
if sys.stdout.isatty():
colors["blue"] = "\033[0;94m"
colors["bold_blue"] = "\033[1;94m"
colors["reset"] = "\033[0m"
else:
colors["blue"] = ""
colors["bold_blue"] = ""
colors["reset"] = ""
# There is a space before "..." to ensure that source file names can be
# Ctrl + clicked in the VS Code terminal.
compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
java_compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
compile_shared_source_message = "{}Compiling shared {}$SOURCE{} ...{}".format(
colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
link_program_message = "{}Linking Program {}$TARGET{} ...{}".format(
colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
link_library_message = "{}Linking Static Library {}$TARGET{} ...{}".format(
colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
ranlib_library_message = "{}Ranlib Library {}$TARGET{} ...{}".format(
colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(
colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(
colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(
colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
generated_file_message = "{}Generating {}$TARGET{} ...{}".format(
colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
env.Append(CXXCOMSTR=[compile_source_message])
env.Append(CCCOMSTR=[compile_source_message])
env.Append(SHCCCOMSTR=[compile_shared_source_message])
env.Append(SHCXXCOMSTR=[compile_shared_source_message])
env.Append(ARCOMSTR=[link_library_message])
env.Append(RANLIBCOMSTR=[ranlib_library_message])
env.Append(SHLINKCOMSTR=[link_shared_library_message])
env.Append(LINKCOMSTR=[link_program_message])
env.Append(JARCOMSTR=[java_library_message])
env.Append(JAVACCOMSTR=[java_compile_source_message])
env.Append(RCCOMSTR=[compiled_resource_message])
env.Append(GENCOMSTR=[generated_file_message])
platforms = ["linux", "macos", "windows", "android", "ios", "web"]
# CPU architecture options.
architecture_array = [
"",
"universal",
"x86_32",
"x86_64",
"arm32",
"arm64",
"rv64",
"ppc32",
"ppc64",
"wasm32",
]
architecture_aliases = {
"x64": "x86_64",
"amd64": "x86_64",
"armv7": "arm32",
"armv8": "arm64",
"arm64v8": "arm64",
"aarch64": "arm64",
"rv": "rv64",
"riscv": "rv64",
"riscv64": "rv64",
"ppcle": "ppc32",
"ppc": "ppc32",
"ppc64le": "ppc64",
}
def exists(env):
return True
def options(opts, env):
# Try to detect the host platform automatically.
# This is used if no `platform` argument is passed
if sys.platform.startswith("linux"):
default_platform = "linux"
elif sys.platform == "darwin":
default_platform = "macos"
elif sys.platform == "win32" or sys.platform == "msys":
default_platform = "windows"
elif ARGUMENTS.get("platform", ""):
default_platform = ARGUMENTS.get("platform")
else:
raise ValueError("Could not detect platform automatically, please specify with platform=<platform>")
opts.Add(
PathVariable(
key="custom_tools",
help="Path to directory containing custom tools",
default=env.get("custom_tools", None),
validator=validate_dir,
)
)
opts.Update(env)
custom_platforms = get_custom_platforms(env)
opts.Add(
EnumVariable(
key="platform",
help="Target platform",
default=env.get("platform", default_platform),
allowed_values=platforms + custom_platforms,
ignorecase=2,
)
)
# Editor and template_debug are compatible (i.e. you can use the same binary for Godot editor builds and Godot debug templates).
# Godot release templates are only compatible with "template_release" builds.
# For this reason, we default to template_debug builds, unlike Godot which defaults to editor builds.
opts.Add(
EnumVariable(
key="target",
help="Compilation target",
default=env.get("target", "template_debug"),
allowed_values=("editor", "template_release", "template_debug"),
)
)
opts.Add(
PathVariable(
key="gdextension_dir",
help="Path to a custom directory containing GDExtension interface header and API JSON file",
default=env.get("gdextension_dir", None),
validator=validate_dir,
)
)
opts.Add(
PathVariable(
key="custom_api_file",
help="Path to a custom GDExtension API JSON file (takes precedence over `gdextension_dir`)",
default=env.get("custom_api_file", None),
validator=validate_file,
)
)
opts.Add(
BoolVariable(
key="generate_bindings",
help="Force GDExtension API bindings generation. Auto-detected by default.",
default=env.get("generate_bindings", False),
)
)
opts.Add(
BoolVariable(
key="generate_template_get_node",
help="Generate a template version of the Node class's get_node.",
default=env.get("generate_template_get_node", True),
)
)
opts.Add(
BoolVariable(
key="build_library",
help="Build the godot-cpp library.",
default=env.get("build_library", True),
)
)
opts.Add(
EnumVariable(
key="precision",
help="Set the floating-point precision level",
default=env.get("precision", "single"),
allowed_values=("single", "double"),
)
)
opts.Add(
EnumVariable(
key="arch",
help="CPU architecture",
default=env.get("arch", ""),
allowed_values=architecture_array,
map=architecture_aliases,
)
)
# compiledb
opts.Add(
BoolVariable(
key="compiledb",
help="Generate compilation DB (`compile_commands.json`) for external tools",
default=env.get("compiledb", False),
)
)
opts.Add(
PathVariable(
key="compiledb_file",
help="Path to a custom `compile_commands.json` file",
default=env.get("compiledb_file", "compile_commands.json"),
validator=validate_parent_dir,
)
)
opts.Add(
BoolVariable(
"disable_exceptions",
"Force disabling exception handling code",
default=env.get("disable_exceptions", False),
)
)
opts.Add(
EnumVariable(
"optimize",
"The desired optimization flags",
"speed_trace",
("none", "custom", "debug", "speed", "speed_trace", "size"),
)
)
opts.Add(BoolVariable("debug_symbols", "Build with debugging symbols", True))
opts.Add(BoolVariable("dev_build", "Developer build with dev-only debugging code (DEV_ENABLED)", False))
opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False))
# Add platform options (custom tools can override platforms)
for pl in sorted(set(platforms + custom_platforms)):
tool = Tool(pl, toolpath=get_platform_tools_paths(env))
if hasattr(tool, "options"):
tool.options(opts)
def generate(env):
# Default num_jobs to local cpu count if not user specified.
# SCons has a peculiarity where user-specified options won't be overridden
# by SetOption, so we can rely on this to know if we should use our default.
initial_num_jobs = env.GetOption("num_jobs")
altered_num_jobs = initial_num_jobs + 1
env.SetOption("num_jobs", altered_num_jobs)
if env.GetOption("num_jobs") == altered_num_jobs:
cpu_count = os.cpu_count()
if cpu_count is None:
print("Couldn't auto-detect CPU count to configure build parallelism. Specify it with the -j argument.")
else:
safer_cpu_count = cpu_count if cpu_count <= 4 else cpu_count - 1
print(
"Auto-detected %d CPU cores available for build parallelism. Using %d cores by default. You can override it with the -j argument."
% (cpu_count, safer_cpu_count)
)
env.SetOption("num_jobs", safer_cpu_count)
# Process CPU architecture argument.
if env["arch"] == "":
# No architecture specified. Default to arm64 if building for Android,
# universal if building for macOS or iOS, wasm32 if building for web,
# otherwise default to the host architecture.
if env["platform"] in ["macos", "ios"]:
env["arch"] = "universal"
elif env["platform"] == "android":
env["arch"] = "arm64"
elif env["platform"] == "web":
env["arch"] = "wasm32"
else:
host_machine = platform.machine().lower()
if host_machine in architecture_array:
env["arch"] = host_machine
elif host_machine in architecture_aliases.keys():
env["arch"] = architecture_aliases[host_machine]
elif "86" in host_machine:
# Catches x86, i386, i486, i586, i686, etc.
env["arch"] = "x86_32"
else:
print("Unsupported CPU architecture: " + host_machine)
env.Exit(1)
print("Building for architecture " + env["arch"] + " on platform " + env["platform"])
# These defaults may be needed by platform tools
env.editor_build = env["target"] == "editor"
env.dev_build = env["dev_build"]
env.debug_features = env["target"] in ["editor", "template_debug"]
if env.dev_build:
opt_level = "none"
elif env.debug_features:
opt_level = "speed_trace"
else: # Release
opt_level = "speed"
env["optimize"] = ARGUMENTS.get("optimize", opt_level)
env["debug_symbols"] = get_cmdline_bool("debug_symbols", env.dev_build)
tool = Tool(env["platform"], toolpath=get_platform_tools_paths(env))
if tool is None or not tool.exists(env):
raise ValueError("Required toolchain not found for platform " + env["platform"])
tool.generate(env)
if env.editor_build:
env.Append(CPPDEFINES=["TOOLS_ENABLED"])
# Configuration of build targets:
# - Editor or template
# - Debug features (DEBUG_ENABLED code)
# - Dev only code (DEV_ENABLED code)
# - Optimization level
# - Debug symbols for crash traces / debuggers
# Keep this configuration in sync with SConstruct in upstream Godot.
if env.debug_features:
# DEBUG_ENABLED enables debugging *features* and debug-only code, which is intended
# to give *users* extra debugging information for their game development.
env.Append(CPPDEFINES=["DEBUG_ENABLED"])
# In upstream Godot this is added in typedefs.h when DEBUG_ENABLED is set.
env.Append(CPPDEFINES=["DEBUG_METHODS_ENABLED"])
if env.dev_build:
# DEV_ENABLED enables *engine developer* code which should only be compiled for those
# working on the engine itself.
env.Append(CPPDEFINES=["DEV_ENABLED"])
else:
# Disable assert() for production targets (only used in thirdparty code).
env.Append(CPPDEFINES=["NDEBUG"])
if env["precision"] == "double":
env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"])
# Allow detecting when building as a GDExtension.
env.Append(CPPDEFINES=["GDEXTENSION"])
# Suffix
suffix = ".{}.{}".format(env["platform"], env["target"])
if env.dev_build:
suffix += ".dev"
if env["precision"] == "double":
suffix += ".double"
suffix += "." + env["arch"]
if env["ios_simulator"]:
suffix += ".simulator"
env["suffix"] = suffix # Exposed when included from another project
env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"]
# compile_commands.json
env.Tool("compilation_db")
env.Alias("compiledb", env.CompilationDatabase(normalize_path(env["compiledb_file"], env)))
# Formatting
if not env["verbose"]:
no_verbose(env)
# Builders
env.Append(
BUILDERS={
"GodotCPPBindings": Builder(action=Action(scons_generate_bindings, "$GENCOMSTR"), emitter=scons_emit_files)
}
)
env.AddMethod(_godot_cpp, "GodotCPP")
def _godot_cpp(env):
extension_dir = normalize_path(env.get("gdextension_dir", env.Dir("gdextension").abspath), env)
api_file = normalize_path(env.get("custom_api_file", env.File(extension_dir + "/extension_api.json").abspath), env)
bindings = env.GodotCPPBindings(
env.Dir("."),
[
api_file,
os.path.join(extension_dir, "gdextension_interface.h"),
"binding_generator.py",
],
)
# Forces bindings regeneration.
if env["generate_bindings"]:
env.AlwaysBuild(bindings)
env.NoCache(bindings)
# Sources to compile
sources = []
add_sources(sources, "src", "cpp")
add_sources(sources, "src/classes", "cpp")
add_sources(sources, "src/core", "cpp")
add_sources(sources, "src/variant", "cpp")
sources.extend([f for f in bindings if str(f).endswith(".cpp")])
# Includes
env.AppendUnique(CPPPATH=[env.Dir(d) for d in [extension_dir, "include", "gen/include"]])
library = None
library_name = "libgodot-cpp" + env["suffix"] + env["LIBSUFFIX"]
if env["build_library"]:
library = env.StaticLibrary(target=env.File("bin/%s" % library_name), source=sources)
default_args = [library]
# Add compiledb if the option is set
if env.get("compiledb", False):
default_args += ["compiledb"]
env.Default(*default_args)
env.AppendUnique(LIBS=[env.File("bin/%s" % library_name)])
return library

View File

@@ -1,7 +1,7 @@
import os
import sys
import subprocess
import ios_osxcross
import common_compiler_flags
from SCons.Variables import *
if sys.version_info < (3,):
@@ -16,6 +16,10 @@ else:
return codecs.utf_8_decode(x)[0]
def has_ios_osxcross():
return "OSXCROSS_IOS" in os.environ
def options(opts):
opts.Add(BoolVariable("ios_simulator", "Target iOS Simulator", False))
opts.Add("ios_min_version", "Target minimum iphoneos/iphonesimulator version", "10.0")
@@ -25,17 +29,18 @@ def options(opts):
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain",
)
opts.Add("IOS_SDK_PATH", "Path to the iOS SDK", "")
ios_osxcross.options(opts)
if has_ios_osxcross():
opts.Add("ios_triple", "Triple for ios toolchain", "")
def exists(env):
return sys.platform == "darwin" or ios_osxcross.exists(env)
return sys.platform == "darwin" or has_ios_osxcross()
def generate(env):
if env["arch"] not in ("universal", "arm64", "x86_64"):
print("Only universal, arm64, and x86_64 are supported on iOS. Exiting.")
Exit()
raise ValueError("Only universal, arm64, and x86_64 are supported on iOS. Exiting.")
if env["ios_simulator"]:
sdk_name = "iphonesimulator"
@@ -64,7 +69,26 @@ def generate(env):
env["ENV"]["PATH"] = env["IOS_TOOLCHAIN_PATH"] + "/Developer/usr/bin/:" + env["ENV"]["PATH"]
else:
ios_osxcross.generate(env)
# OSXCross
compiler_path = "$IOS_TOOLCHAIN_PATH/usr/bin/${ios_triple}"
env["CC"] = compiler_path + "clang"
env["CXX"] = compiler_path + "clang++"
env["AR"] = compiler_path + "ar"
env["RANLIB"] = compiler_path + "ranlib"
env["SHLIBSUFFIX"] = ".dylib"
env.Prepend(
CPPPATH=[
"$IOS_SDK_PATH/usr/include",
"$IOS_SDK_PATH/System/Library/Frameworks/AudioUnit.framework/Headers",
]
)
env.Append(CCFLAGS=["-stdlib=libc++"])
binpath = os.path.join(env["IOS_TOOLCHAIN_PATH"], "usr", "bin")
if binpath not in env["ENV"]["PATH"]:
env.PrependENVPath("PATH", binpath)
if env["arch"] == "universal":
if env["ios_simulator"]:
@@ -79,3 +103,7 @@ def generate(env):
env.Append(CCFLAGS=["-isysroot", env["IOS_SDK_PATH"]])
env.Append(LINKFLAGS=["-isysroot", env["IOS_SDK_PATH"], "-F" + env["IOS_SDK_PATH"]])
env.Append(CPPDEFINES=["IOS_ENABLED", "UNIX_ENABLED"])
common_compiler_flags.generate(env)

View File

@@ -1,26 +0,0 @@
import os
def options(opts):
opts.Add("ios_triple", "Triple for ios toolchain", "")
def exists(env):
return "OSXCROSS_IOS" in os.environ
def generate(env):
compiler_path = "$IOS_TOOLCHAIN_PATH/usr/bin/${ios_triple}"
env["CC"] = compiler_path + "clang"
env["CXX"] = compiler_path + "clang++"
env["AR"] = compiler_path + "ar"
env["RANLIB"] = compiler_path + "ranlib"
env["SHLIBSUFFIX"] = ".dylib"
env.Prepend(
CPPPATH=[
"$IOS_SDK_PATH/usr/include",
"$IOS_SDK_PATH/System/Library/Frameworks/AudioUnit.framework/Headers",
]
)
env.Append(CCFLAGS=["-stdlib=libc++"])

View File

@@ -1,3 +1,4 @@
import common_compiler_flags
from SCons.Variables import *
from SCons.Tool import clang, clangxx
@@ -32,3 +33,7 @@ def generate(env):
elif env["arch"] == "rv64":
env.Append(CCFLAGS=["-march=rv64gc"])
env.Append(LINKFLAGS=["-march=rv64gc"])
env.Append(CPPDEFINES=["LINUX_ENABLED", "UNIX_ENABLED"])
common_compiler_flags.generate(env)

View File

@@ -1,31 +1,52 @@
import os
import sys
import macos_osxcross
import common_compiler_flags
def has_osxcross():
return "OSXCROSS_ROOT" in os.environ
def options(opts):
opts.Add("macos_deployment_target", "macOS deployment target", "default")
opts.Add("macos_sdk_path", "macOS SDK path", "")
macos_osxcross.options(opts)
if has_osxcross():
opts.Add("osxcross_sdk", "OSXCross SDK version", "darwin16")
def exists(env):
return sys.platform == "darwin" or macos_osxcross.exists(env)
return sys.platform == "darwin" or has_osxcross()
def generate(env):
if env["arch"] not in ("universal", "arm64", "x86_64"):
print("Only universal, arm64, and x86_64 are supported on macOS. Exiting.")
Exit()
env.Exit(1)
if sys.platform == "darwin":
# Use clang on macOS by default
env["CXX"] = "clang++"
env["CC"] = "clang"
else:
# Use osxcross
macos_osxcross.generate(env)
# OSXCross
root = os.environ.get("OSXCROSS_ROOT", "")
if env["arch"] == "arm64":
basecmd = root + "/target/bin/arm64-apple-" + env["osxcross_sdk"] + "-"
else:
basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-"
env["CC"] = basecmd + "clang"
env["CXX"] = basecmd + "clang++"
env["AR"] = basecmd + "ar"
env["RANLIB"] = basecmd + "ranlib"
env["AS"] = basecmd + "as"
binpath = os.path.join(root, "target", "bin")
if binpath not in env["ENV"]["PATH"]:
# Add OSXCROSS bin folder to PATH (required for linking).
env.PrependENVPath("PATH", binpath)
# Common flags
if env["arch"] == "universal":
env.Append(LINKFLAGS=["-arch", "x86_64", "-arch", "arm64"])
env.Append(CCFLAGS=["-arch", "x86_64", "-arch", "arm64"])
@@ -48,3 +69,7 @@ def generate(env):
"-Wl,-undefined,dynamic_lookup",
]
)
env.Append(CPPDEFINES=["MACOS_ENABLED", "UNIX_ENABLED"])
common_compiler_flags.generate(env)

View File

@@ -1,28 +0,0 @@
import os
def options(opts):
opts.Add("osxcross_sdk", "OSXCross SDK version", "darwin16")
def exists(env):
return "OSXCROSS_ROOT" in os.environ
def generate(env):
root = os.environ.get("OSXCROSS_ROOT", "")
if env["arch"] == "arm64":
basecmd = root + "/target/bin/arm64-apple-" + env["osxcross_sdk"] + "-"
else:
basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-"
env["CC"] = basecmd + "clang"
env["CXX"] = basecmd + "clang++"
env["AR"] = basecmd + "ar"
env["RANLIB"] = basecmd + "ranlib"
env["AS"] = basecmd + "as"
binpath = os.path.join(root, "target", "bin")
if binpath not in env["ENV"]["PATH"]:
# Add OSXCROSS bin folder to PATH (required for linking).
env["ENV"]["PATH"] = "%s:%s" % (binpath, env["ENV"]["PATH"])

View File

@@ -1,92 +0,0 @@
import os
import sys
from SCons.Script import ARGUMENTS
from SCons.Variables import *
from SCons.Variables.BoolVariable import _text2bool
def get_cmdline_bool(option, default):
"""We use `ARGUMENTS.get()` to check if options were manually overridden on the command line,
and SCons' _text2bool helper to convert them to booleans, otherwise they're handled as strings.
"""
cmdline_val = ARGUMENTS.get(option)
if cmdline_val is not None:
return _text2bool(cmdline_val)
else:
return default
def options(opts):
opts.Add(
EnumVariable(
"optimize",
"The desired optimization flags",
"speed_trace",
("none", "custom", "debug", "speed", "speed_trace", "size"),
)
)
opts.Add(BoolVariable("debug_symbols", "Build with debugging symbols", True))
opts.Add(BoolVariable("dev_build", "Developer build with dev-only debugging code (DEV_ENABLED)", False))
def exists(env):
return True
def generate(env):
env.dev_build = env["dev_build"]
env.debug_features = env["target"] in ["editor", "template_debug"]
env.editor_build = env["target"] == "editor"
if env.editor_build:
env.AppendUnique(CPPDEFINES=["TOOLS_ENABLED"])
if env.debug_features:
env.AppendUnique(CPPDEFINES=["DEBUG_ENABLED", "DEBUG_METHODS_ENABLED"])
if env.dev_build:
opt_level = "none"
env.AppendUnique(CPPDEFINES=["DEV_ENABLED"])
elif env.debug_features:
opt_level = "speed_trace"
else: # Release
opt_level = "speed"
env["optimize"] = ARGUMENTS.get("optimize", opt_level)
env["debug_symbols"] = get_cmdline_bool("debug_symbols", env.dev_build)
if env.get("is_msvc", False):
if env["debug_symbols"]:
env.Append(CCFLAGS=["/Zi", "/FS"])
env.Append(LINKFLAGS=["/DEBUG:FULL"])
if env["optimize"] == "speed" or env["optimize"] == "speed_trace":
env.Append(CCFLAGS=["/O2"])
env.Append(LINKFLAGS=["/OPT:REF"])
elif env["optimize"] == "size":
env.Append(CCFLAGS=["/O1"])
env.Append(LINKFLAGS=["/OPT:REF"])
if env["optimize"] == "debug" or env["optimize"] == "none":
env.Append(CCFLAGS=["/MDd", "/Od"])
else:
env.Append(CCFLAGS=["/MD"])
else:
if env["debug_symbols"]:
if env.dev_build:
env.Append(CCFLAGS=["-g3"])
else:
env.Append(CCFLAGS=["-g2"])
if env["optimize"] == "speed":
env.Append(CCFLAGS=["-O3"])
# `-O2` is friendlier to debuggers than `-O3`, leading to better crash backtraces.
elif env["optimize"] == "speed_trace":
env.Append(CCFLAGS=["-O2"])
elif env["optimize"] == "size":
env.Append(CCFLAGS=["-Os"])
elif env["optimize"] == "debug":
env.Append(CCFLAGS=["-Og"])
elif env["optimize"] == "none":
env.Append(CCFLAGS=["-O0"])

View File

@@ -1,45 +1,47 @@
import os
import common_compiler_flags
from SCons.Util import WhereIs
def exists(env):
return "EM_CONFIG" in os.environ
return WhereIs("emcc") is not None
def generate(env):
if env["arch"] not in ("wasm32"):
print("Only wasm32 supported on web. Exiting.")
Exit()
if "EM_CONFIG" in os.environ:
env["ENV"] = os.environ
env.Exit(1)
# Emscripten toolchain
env["CC"] = "emcc"
env["CXX"] = "em++"
env["AR"] = "emar"
env["RANLIB"] = "emranlib"
env.Append(CPPFLAGS=["-s", "SIDE_MODULE=1"])
env.Append(LINKFLAGS=["-s", "SIDE_MODULE=1"])
env["SHOBJSUFFIX"] = ".bc"
env["SHLIBSUFFIX"] = ".wasm"
# Use TempFileMunge since some AR invocations are too long for cmd.exe.
# Use POSIX-style paths, required with TempFileMunge.
env["ARCOM_POSIX"] = env["ARCOM"].replace("$TARGET", "$TARGET.posix").replace("$SOURCES", "$SOURCES.posix")
env["ARCOM"] = "${TEMPFILE(ARCOM_POSIX)}"
# All intermediate files are just LLVM bitcode.
env["OBJPREFIX"] = ""
env["OBJSUFFIX"] = ".bc"
env["PROGPREFIX"] = ""
# Program() output consists of multiple files, so specify suffixes manually at builder.
env["PROGSUFFIX"] = ""
# All intermediate files are just object files.
env["OBJSUFFIX"] = ".o"
env["SHOBJSUFFIX"] = ".o"
# Static libraries clang-style.
env["LIBPREFIX"] = "lib"
env["LIBSUFFIX"] = ".a"
env["LIBPREFIXES"] = ["$LIBPREFIX"]
env["LIBSUFFIXES"] = ["$LIBSUFFIX"]
env.Replace(SHLINKFLAGS="$LINKFLAGS")
env.Replace(SHLINKFLAGS="$LINKFLAGS")
if env["target"] == "debug":
env.Append(CCFLAGS=["-O0", "-g"])
elif env["target"] == "release":
env.Append(CCFLAGS=["-O3"])
# Shared library as wasm.
env["SHLIBSUFFIX"] = ".wasm"
# Thread support (via SharedArrayBuffer).
env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"])
env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"])
# Build as side module (shared library).
env.Append(CPPFLAGS=["-s", "SIDE_MODULE=1"])
env.Append(LINKFLAGS=["-s", "SIDE_MODULE=1"])
env.Append(CPPDEFINES=["WEB_ENABLED", "UNIX_ENABLED"])
common_compiler_flags.generate(env)

View File

@@ -1,7 +1,6 @@
import sys
import my_spawn
import common_compiler_flags
from SCons.Tool import msvc, mingw
from SCons.Variables import *
@@ -9,6 +8,7 @@ from SCons.Variables import *
def options(opts):
opts.Add(BoolVariable("use_mingw", "Use the MinGW compiler instead of MSVC - only effective on Windows", False))
opts.Add(BoolVariable("use_clang_cl", "Use the clang driver instead of MSVC - only effective on Windows", False))
opts.Add(BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True))
def exists(env):
@@ -30,13 +30,18 @@ def generate(env):
env.Tool("mslink")
env.Append(CPPDEFINES=["TYPED_METHOD_BIND", "NOMINMAX"])
env.Append(CCFLAGS=["/EHsc", "/utf-8"])
env.Append(CCFLAGS=["/utf-8"])
env.Append(LINKFLAGS=["/WX"])
if env["use_clang_cl"]:
env["CC"] = "clang-cl"
env["CXX"] = "clang-cl"
if env["use_static_cpp"]:
env.Append(CCFLAGS=["/MT"])
else:
env.Append(CCFLAGS=["/MD"])
elif sys.platform == "win32" or sys.platform == "msys":
env["use_mingw"] = True
mingw.generate(env)
@@ -45,6 +50,18 @@ def generate(env):
env["SHLIBPREFIX"] = ""
# Want dll suffix
env["SHLIBSUFFIX"] = ".dll"
env.Append(CCFLAGS=["-Wwrite-strings"])
env.Append(LINKFLAGS=["-Wl,--no-undefined"])
if env["use_static_cpp"]:
env.Append(
LINKFLAGS=[
"-static",
"-static-libgcc",
"-static-libstdc++",
]
)
# Long line hack. Use custom spawn, quick AR append (to avoid files with the same names to override each other).
my_spawn.configure(env)
@@ -60,13 +77,17 @@ def generate(env):
# Want dll suffix
env["SHLIBSUFFIX"] = ".dll"
# These options are for a release build even using target=debug
env.Append(CCFLAGS=["-O3", "-Wwrite-strings"])
env.Append(
LINKFLAGS=[
"--static",
"-Wl,--no-undefined",
"-static-libgcc",
"-static-libstdc++",
]
)
env.Append(CCFLAGS=["-Wwrite-strings"])
env.Append(LINKFLAGS=["-Wl,--no-undefined"])
if env["use_static_cpp"]:
env.Append(
LINKFLAGS=[
"-static",
"-static-libgcc",
"-static-libstdc++",
]
)
env.Append(CPPDEFINES=["WINDOWS_ENABLED"])
common_compiler_flags.generate(env)