├── .gitignore
├── .gitmodules
├── .vscode
├── extensions.json
├── tasks.json
├── launch.json
└── godot.natvis
├── src
├── CMakeLists.txt
├── GDExtensionTemplate.h
├── GDExtensionTemplate.cpp
├── Version.h.in
├── RegisterExtension.cpp
├── Example.h
└── Example.cpp
├── templates
├── template.debug.gdextension.in
├── template.release.gdextension.in
└── CMakeLists.txt
├── cmake
├── ccache.cmake
├── ClangFormat.cmake
├── GitVersionInfo.cmake
├── GetGitRevisionDescription.cmake.in
├── CompilerWarnings.cmake
└── GetGitRevisionDescription.cmake
├── LICENSE.md
├── CHANGELOG.md
├── support_files
└── icons
│ └── Example.svg
├── CMakePresets.json
├── .github
└── workflows
│ └── main.yml
├── .clang-format
├── CMakeLists.txt
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | CMakeLists.txt.user
2 | .vscode/settings.json
3 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "extern/godot-cpp"]
2 | path = extern/godot-cpp
3 | url = https://github.com/godotengine/godot-cpp.git
4 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "ms-vscode.cpptools-extension-pack",
4 | "ms-vscode.cmake-tools"
5 | ]
6 | }
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Unlicense
2 |
3 | target_sources( ${PROJECT_NAME}
4 | PRIVATE
5 | Example.h
6 | Example.cpp
7 | GDExtensionTemplate.h
8 | GDExtensionTemplate.cpp
9 | RegisterExtension.cpp
10 | )
11 |
12 | target_include_directories( ${PROJECT_NAME}
13 | PRIVATE
14 | "src"
15 | )
16 |
--------------------------------------------------------------------------------
/templates/template.debug.gdextension.in:
--------------------------------------------------------------------------------
1 | [configuration]
2 | entry_symbol = "GDExtensionInit"
3 | compatibility_minimum = 4.1
4 |
5 | [icons]
6 | Example = "icons/Example.svg"
7 |
8 | [libraries]
9 | linux.debug.x86_64 = "lib/Linux-x86_64/lib${PROJECT_NAME}-d.so"
10 | macos.debug = "lib/Darwin-Universal/lib${PROJECT_NAME}-d.dylib"
11 | windows.debug.x86_64 = "lib/Windows-AMD64/${LIB_PREFIX}${PROJECT_NAME}-d.dll"
12 |
--------------------------------------------------------------------------------
/templates/template.release.gdextension.in:
--------------------------------------------------------------------------------
1 | [configuration]
2 | entry_symbol = "GDExtensionInit"
3 | compatibility_minimum = 4.1
4 |
5 | [icons]
6 | Example = "icons/Example.svg"
7 |
8 | [libraries]
9 | linux.release.x86_64 = "lib/Linux-x86_64/lib${PROJECT_NAME}.so"
10 | macos.release = "lib/Darwin-universal/lib${PROJECT_NAME}.dylib"
11 | windows.release.x86_64 = "lib/Windows-AMD64/${LIB_PREFIX}${PROJECT_NAME}.dll"
12 |
--------------------------------------------------------------------------------
/src/GDExtensionTemplate.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | // SPDX-License-Identifier: Unlicense
3 |
4 | #include "godot_cpp/classes/object.hpp"
5 |
6 | namespace godot
7 | {
8 | class ClassDB;
9 | };
10 |
11 | class GDExtensionTemplate : public godot::Object
12 | {
13 | GDCLASS( GDExtensionTemplate, godot::Object )
14 |
15 | public:
16 | static godot::String version();
17 | static godot::String godotCPPVersion();
18 |
19 | private:
20 | static void _bind_methods();
21 | };
22 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "type": "cmake",
6 | "label": "CMake: build",
7 | "command": "build",
8 | "targets": [
9 | "all"
10 | ],
11 | "preset": "${command:cmake.activeBuildPresetName}",
12 | "group": "build",
13 | "problemMatcher": [],
14 | "detail": "CMake template build task"
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/cmake/ccache.cmake:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Unlicense
2 |
3 | # See: https://crascit.com/2016/04/09/using-ccache-with-cmake/
4 | find_program( CCACHE_PROGRAM ccache )
5 |
6 | if ( CCACHE_PROGRAM )
7 | # get version information
8 | execute_process(
9 | COMMAND "${CCACHE_PROGRAM}" --version
10 | OUTPUT_VARIABLE CCACHE_VERSION
11 | )
12 |
13 | string( REGEX MATCH "[^\r\n]*" CCACHE_VERSION ${CCACHE_VERSION} )
14 |
15 | message( STATUS "Using ccache: ${CCACHE_PROGRAM} (${CCACHE_VERSION})" )
16 |
17 | # Turn on ccache for all targets
18 | set( CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}" )
19 | set( CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}" )
20 |
21 | unset( CCACHE_VERSION )
22 | endif()
23 |
--------------------------------------------------------------------------------
/cmake/ClangFormat.cmake:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Unlicense
2 |
3 | find_program( CLANG_FORMAT_PROGRAM NAMES clang-format )
4 |
5 | if ( CLANG_FORMAT_PROGRAM )
6 | # get version information
7 | execute_process(
8 | COMMAND "${CLANG_FORMAT_PROGRAM}" --version
9 | OUTPUT_VARIABLE CLANG_FORMAT_VERSION
10 | OUTPUT_STRIP_TRAILING_WHITESPACE
11 | )
12 |
13 | message( STATUS "Using clang-format: ${CLANG_FORMAT_PROGRAM} (${CLANG_FORMAT_VERSION})" )
14 |
15 | get_target_property( CLANG_FORMAT_SOURCES ${PROJECT_NAME} SOURCES )
16 |
17 | # Remove some files from the list
18 | list( FILTER CLANG_FORMAT_SOURCES EXCLUDE REGEX ".*/extern/.*" )
19 | list( FILTER CLANG_FORMAT_SOURCES EXCLUDE REGEX ".*/gen/.*" )
20 | list( FILTER CLANG_FORMAT_SOURCES EXCLUDE REGEX ".*/*.gdextension.in" )
21 | list( FILTER CLANG_FORMAT_SOURCES EXCLUDE REGEX ".*/Version.h.in" )
22 |
23 | add_custom_target( clang-format
24 | COMMAND "${CLANG_FORMAT_PROGRAM}" --style=file -i ${CLANG_FORMAT_SOURCES}
25 | COMMENT "Running clang-format..."
26 | COMMAND_EXPAND_LISTS
27 | VERBATIM
28 | )
29 |
30 | unset( CLANG_FORMAT_VERSION )
31 | unset( CLANG_FORMAT_SOURCES )
32 | endif()
33 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Windows Launch",
9 | "type": "cppvsdbg",
10 | "request": "launch",
11 | "program": "${command:cmake.launchTargetPath}",
12 | "args": [],
13 | "cwd": "${command:cmake.buildDirectory}/bin",
14 | "preLaunchTask": "CMake: build",
15 | "internalConsoleOptions": "openOnSessionStart",
16 | "console": "internalConsole",
17 | "environment": [
18 | {
19 | "name": "Path",
20 | "value": "${env:Path};"
21 | }
22 | ],
23 | "visualizerFile": "${workspaceFolder}/.vscode/godot.natvis"
24 | },
25 | {
26 | "name": "macOS/Linux Launch",
27 | "type": "lldb",
28 | "request": "launch",
29 | "program": "${command:cmake.launchTargetPath}",
30 | "args": [],
31 | "cwd": "${command:cmake.buildDirectory}/bin",
32 | "visualizerFile": "${workspaceFolder}/.vscode/godot.natvis"
33 | }
34 | ]
35 | }
--------------------------------------------------------------------------------
/cmake/GitVersionInfo.cmake:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Unlicense
2 |
3 | find_program( GIT_PROGRAM git )
4 |
5 | if ( GIT_PROGRAM )
6 | # get version information
7 | execute_process(
8 | COMMAND "${GIT_PROGRAM}" --version
9 | OUTPUT_VARIABLE GIT_VERSION
10 | OUTPUT_STRIP_TRAILING_WHITESPACE
11 | )
12 |
13 | message( STATUS "Using git: ${GIT_PROGRAM} (${GIT_VERSION})" )
14 |
15 | include( GetGitRevisionDescription )
16 |
17 | get_git_head_revision( GIT_REFSPEC GIT_SHA1 )
18 | git_describe( GIT_SHORT )
19 |
20 | string( TOUPPER ${PROJECT_NAME} UPPER_PROJECT_NAME )
21 |
22 | set( VERSION_INPUT_FILE "src/Version.h.in" )
23 | set( VERSION_OUTPUT_FILE "${CMAKE_BINARY_DIR}/gen/Version.h" )
24 |
25 | configure_file( "${VERSION_INPUT_FILE}" "${VERSION_OUTPUT_FILE}" )
26 |
27 | target_sources( ${PROJECT_NAME}
28 | PRIVATE
29 | "${VERSION_INPUT_FILE}"
30 | "${VERSION_OUTPUT_FILE}"
31 | )
32 |
33 | get_filename_component( VERSION_OUTPUT_FILE_DIR ${VERSION_OUTPUT_FILE} DIRECTORY )
34 |
35 | target_include_directories( ${PROJECT_NAME}
36 | PRIVATE
37 | ${VERSION_OUTPUT_FILE_DIR}
38 | )
39 |
40 | message( STATUS "${PROJECT_NAME} version: ${GIT_SHORT}" )
41 |
42 | unset( VERSION_INPUT_FILE )
43 | unset( VERSION_OUTPUT_FILE )
44 | unset( VERSION_OUTPUT_FILE_DIR )
45 | unset( GIT_VERSION )
46 | endif()
47 |
--------------------------------------------------------------------------------
/cmake/GetGitRevisionDescription.cmake.in:
--------------------------------------------------------------------------------
1 | #
2 | # Internal file for GetGitRevisionDescription.cmake
3 | #
4 | # Requires CMake 2.6 or newer (uses the 'function' command)
5 | #
6 | # Original Author:
7 | # 2009-2010 Ryan Pavlik
8 | # http://academic.cleardefinition.com
9 | # Iowa State University HCI Graduate Program/VRAC
10 | #
11 | # Copyright 2009-2012, Iowa State University
12 | # Copyright 2011-2015, Contributors
13 | # Distributed under the Boost Software License, Version 1.0.
14 | # (See accompanying file LICENSE_1_0.txt or copy at
15 | # http://www.boost.org/LICENSE_1_0.txt)
16 | # SPDX-License-Identifier: BSL-1.0
17 |
18 | set(HEAD_HASH)
19 |
20 | file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
21 |
22 | string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
23 | if(HEAD_CONTENTS MATCHES "ref")
24 | # named branch
25 | string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
26 | if(EXISTS "@GIT_DIR@/${HEAD_REF}")
27 | configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
28 | else()
29 | configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
30 | file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
31 | if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
32 | set(HEAD_HASH "${CMAKE_MATCH_1}")
33 | endif()
34 | endif()
35 | else()
36 | # detached HEAD
37 | configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
38 | endif()
39 |
40 | if(NOT HEAD_HASH)
41 | file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
42 | string(STRIP "${HEAD_HASH}" HEAD_HASH)
43 | endif()
44 |
--------------------------------------------------------------------------------
/templates/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Unlicense
2 |
3 | add_custom_target( templates
4 | SOURCES
5 | template.debug.gdextension.in
6 | template.release.gdextension.in
7 | )
8 |
9 | add_dependencies( ${PROJECT_NAME} templates )
10 |
11 | # We shouldn't be relying on CMAKE_BUILD_TYPE (see https://github.com/asmaloney/GDExtensionTemplate/issues/25)
12 | # But until we fix it here and in godot-cpp, ensure it's one we expect.
13 | set ( ALLOWED_BUILDS "Debug;Release" )
14 | if ( NOT "${CMAKE_BUILD_TYPE}" IN_LIST ALLOWED_BUILDS )
15 | message( FATAL_ERROR "CMAKE_BUILD_TYPE must be set to Debug or Release" )
16 | endif()
17 |
18 | # Get our gdextension input file name based on build type
19 | string( TOLOWER ${CMAKE_BUILD_TYPE} BUILD_TYPE )
20 | set( GD_EXTENSION_FILE_INPUT template.${BUILD_TYPE}.gdextension.in )
21 |
22 | # Workaround to add the "lib" prefix to the library in our template file if using MSYS2.
23 | if ( MINGW )
24 | set( LIB_PREFIX "lib")
25 | endif()
26 |
27 | # Generate our project's .gdextension file from the template
28 | set( GD_EXTENSION_FILE ${PROJECT_NAME}.gdextension )
29 | configure_file( ${GD_EXTENSION_FILE_INPUT} ${PROJECT_BINARY_DIR}/${PROJECT_NAME}/${GD_EXTENSION_FILE} )
30 |
31 | # Install the gdextension file from the build directory
32 | install(
33 | FILES ${BUILD_OUTPUT_DIR}/${GD_EXTENSION_FILE}
34 | DESTINATION ${INSTALL_DIR}
35 | )
36 |
37 | unset( ALLOWED_BUILDS )
38 | unset( BUILD_TYPE )
39 | unset( GD_EXTENSION_FILE )
40 | unset( GD_EXTENSION_FILE_INPUT )
41 | unset( LIB_PREFIX )
42 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4 |
5 | ## 4.2 - 2023-12-16
6 |
7 | ### New
8 |
9 | - Add CMake preset and VSCode support ([#61](https://github.com/asmaloney/GDExtensionTemplate/pull/61))
10 |
11 | ### Changed
12 |
13 | - Updated example code ([#63](https://github.com/asmaloney/GDExtensionTemplate/pull/63))
14 | - Updated bindings for Godot 4.1 ([#62](https://github.com/asmaloney/GDExtensionTemplate/pull/62))
15 |
16 | ## 4.1 - 2023-07-06
17 |
18 | ### Changed
19 |
20 | - Updated bindings, updated initialization code, and added compatibility in .gdextensions for Godot 4.1 ([#56](https://github.com/asmaloney/GDExtensionTemplate/pull/56))
21 |
22 | ## 4.0.3 - 2023-05-22
23 |
24 | ### Changed
25 |
26 | - Updated bindings for Godot 4.0.3 ([#53](https://github.com/asmaloney/GDExtensionTemplate/pull/53))
27 |
28 | ## 4.0.2 - 2023-04-04
29 |
30 | ### Changed
31 |
32 | - Updated bindings for Godot 4.0.2 ([#52](https://github.com/asmaloney/GDExtensionTemplate/pull/52))
33 |
34 | ## 4.0.1 - 2023-03-25
35 |
36 | ### Changed
37 |
38 | - Updated bindings for Godot 4.0.1 ([#50](https://github.com/asmaloney/GDExtensionTemplate/pull/50))
39 |
40 | ### Fixed
41 |
42 | - {cmake} Don't override the debug posfix if CMAKE_DEBUG_POSTFIX defined. ([#51](https://github.com/asmaloney/GDExtensionTemplate/pull/51))
43 |
44 | ## 4.0 - 2023-03-01
45 |
46 | ### Changed
47 |
48 | - Updated bindings for Godot 4.0 ([#49](https://github.com/asmaloney/GDExtensionTemplate/pull/49))
49 |
--------------------------------------------------------------------------------
/src/GDExtensionTemplate.cpp:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Unlicense
2 |
3 | #include "godot_cpp/core/class_db.hpp"
4 | #include "godot_cpp/core/version.hpp"
5 |
6 | #include "GDExtensionTemplate.h"
7 | #include "Version.h"
8 |
9 | /// @file
10 | /// GDExtensionTemplate example implementation.
11 |
12 | /*!
13 | @brief Get the version string for this extension.
14 |
15 | @details
16 | The version string is generated by cmake using src/Version.h.in.
17 |
18 | It uses the form " -<# commits since last tag>-".
19 | If there are no commits since the last tag, only the tag is shown.
20 |
21 | @return The version string (e.g. "Foo v1.2.3-gdedbd01").
22 | */
23 | godot::String GDExtensionTemplate::version()
24 | {
25 | return VersionInfo::VERSION_STR.data();
26 | }
27 |
28 | /*!
29 | @brief Get the godot-cpp version string for this extension.
30 |
31 | @details
32 | The version string is generated using godot-cpp's core/version.hpp.
33 |
34 | @return The version string (e.g. "godot-cpp v4.2.0-stable").
35 | */
36 | godot::String GDExtensionTemplate::godotCPPVersion()
37 | {
38 | return "godot-cpp v" + godot::uitos( GODOT_VERSION_MAJOR ) + "." +
39 | godot::uitos( GODOT_VERSION_MINOR ) + "." + godot::uitos( GODOT_VERSION_PATCH ) + "-" +
40 | GODOT_VERSION_STATUS;
41 | }
42 |
43 | /// Bind our methods so GDScript can access them.
44 | void GDExtensionTemplate::_bind_methods()
45 | {
46 | godot::ClassDB::bind_static_method( "GDExtensionTemplate", godot::D_METHOD( "version" ),
47 | &GDExtensionTemplate::version );
48 | godot::ClassDB::bind_static_method( "GDExtensionTemplate",
49 | godot::D_METHOD( "godot_cpp_version" ),
50 | &GDExtensionTemplate::godotCPPVersion );
51 | }
52 |
--------------------------------------------------------------------------------
/support_files/icons/Example.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/Version.h.in:
--------------------------------------------------------------------------------
1 | #pragma once
2 | // This file is generated by cmake. Changes will be overwritten.
3 | // clang-format off
4 |
5 | #include
6 |
7 | // Creates a version number for use in macro comparisons.
8 | //
9 | // Example:
10 | //
11 | // // Check if the version is less than 2.1.0
12 | // #if ${UPPER_PROJECT_NAME}_VERSION < ${UPPER_PROJECT_NAME}_VERSION_CHECK(2, 1, 0)
13 | // // do stuff
14 | // #endif
15 | //
16 | // Returns an integer which may be used in comparisons
17 | #define ${UPPER_PROJECT_NAME}_VERSION_CHECK( major, minor, patch ) ( ((major)<<16) | ((minor)<<8) | (patch) )
18 |
19 | #define ${UPPER_PROJECT_NAME}_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}
20 | #define ${UPPER_PROJECT_NAME}_VERSION_MINOR ${PROJECT_VERSION_MINOR}
21 | #define ${UPPER_PROJECT_NAME}_VERSION_PATCH ${PROJECT_VERSION_PATCH}
22 |
23 | // The version number of this extension. Used for #if comparisons.
24 | // This is generated using the version set in the CMake project macro.
25 | #define ${UPPER_PROJECT_NAME}_VERSION ${UPPER_PROJECT_NAME}_VERSION_CHECK( ${PROJECT_VERSION_MAJOR}, ${PROJECT_VERSION_MINOR}, ${PROJECT_VERSION_PATCH} )
26 |
27 | namespace VersionInfo {
28 | // Project name and version as a string.
29 | // This is generated using the project name from the cmake project macro and the current git commit information.
30 | //
31 | // It uses the form " -<# commits since last tag>-".
32 | // If there are no commits since the last tag, only the tag is shown.
33 | constexpr std::string_view VERSION_STR = "${PROJECT_NAME} ${GIT_SHORT}";
34 |
35 | // The version information as a string.
36 | // This is generated using the current git commit information.
37 | //
38 | // It uses the form "-<# commits since last tag>-".
39 | // If there are no commits since the last tag, only the tag is shown.
40 | constexpr std::string_view VERSION_SHORT_STR = "${GIT_SHORT}";
41 |
42 | // The full git SHA1 hash as a string.
43 | // This is generated using the current git commit information.
44 | constexpr std::string_view GIT_SHA1_STR = "${GIT_SHA1}";
45 | }
46 |
--------------------------------------------------------------------------------
/src/RegisterExtension.cpp:
--------------------------------------------------------------------------------
1 | // Copied from godot-cpp/test/src and modified.
2 |
3 | #include "gdextension_interface.h"
4 |
5 | #include "godot_cpp/core/class_db.hpp"
6 | #include "godot_cpp/core/defs.hpp"
7 | #include "godot_cpp/godot.hpp"
8 |
9 | #include "Example.h"
10 | #include "GDExtensionTemplate.h"
11 |
12 | /// @file
13 | /// Register our classes with Godot.
14 |
15 | namespace
16 | {
17 | /// @brief Called by Godot to let us register our classes with Godot.
18 | ///
19 | /// @param p_level the level being initialized by Godot
20 | ///
21 | /// @see GDExtensionInit
22 | void initializeExtension( godot::ModuleInitializationLevel p_level )
23 | {
24 | if ( p_level != godot::MODULE_INITIALIZATION_LEVEL_SCENE )
25 | {
26 | return;
27 | }
28 |
29 | godot::ClassDB::register_class();
30 | godot::ClassDB::register_class();
31 | godot::ClassDB::register_class();
32 | godot::ClassDB::register_class( true );
33 | godot::ClassDB::register_abstract_class();
34 |
35 | godot::ClassDB::register_class();
36 | }
37 |
38 | /// @brief Called by Godot to let us do any cleanup.
39 | ///
40 | /// @see GDExtensionInit
41 | void uninitializeExtension( godot::ModuleInitializationLevel p_level )
42 | {
43 | if ( p_level != godot::MODULE_INITIALIZATION_LEVEL_SCENE )
44 | {
45 | return;
46 | }
47 | }
48 | }
49 |
50 | extern "C"
51 | {
52 | /// @brief This is the entry point for the shared library.
53 | ///
54 | /// @note The name of this function must match the "entry_symbol" in
55 | /// templates/template.*.gdextension.in
56 | ///
57 | /// @param p_get_proc_address the interface (need more info)
58 | /// @param p_library the library (need more info)
59 | /// @param r_initialization the intialization (need more info)
60 | ///
61 | /// @returns GDExtensionBool
62 | GDExtensionBool GDE_EXPORT GDExtensionInit(
63 | GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library,
64 | GDExtensionInitialization *r_initialization )
65 | {
66 | {
67 | godot::GDExtensionBinding::InitObject init_obj( p_get_proc_address, p_library,
68 | r_initialization );
69 |
70 | init_obj.register_initializer( initializeExtension );
71 | init_obj.register_terminator( uninitializeExtension );
72 | init_obj.set_minimum_library_initialization_level(
73 | godot::MODULE_INITIALIZATION_LEVEL_SCENE );
74 |
75 | return init_obj.init();
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/CMakePresets.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 5,
3 | "cmakeMinimumRequired": {
4 | "major": 3,
5 | "minor": 23,
6 | "patch": 0
7 | },
8 | "configurePresets": [
9 | {
10 | "name": "default",
11 | "displayName": "default",
12 | "description": "Default preset that are inherited by all",
13 | "generator": "Ninja",
14 | "hidden": true,
15 | "environment": {
16 | "PROJECT_NAME": "MyGodotExtention"
17 | }
18 | },
19 | {
20 | "name": "windows-debug",
21 | "displayName": "64bit Windows Debug",
22 | "inherits": "default",
23 | "binaryDir": "${sourceDir}/../build_${env:PROJECT_NAME}_Windows-AMD64",
24 | "condition": {
25 | "type": "equals",
26 | "lhs": "${hostSystemName}",
27 | "rhs": "Windows"
28 | },
29 | "toolset": {
30 | "value": "host=x64",
31 | "strategy": "external"
32 | },
33 | "architecture": {
34 | "value": "x64",
35 | "strategy": "external"
36 | },
37 | "cacheVariables": {
38 | "CMAKE_C_COMPILER": "cl.exe",
39 | "CMAKE_CXX_COMPILER": "cl.exe",
40 | "CMAKE_BUILD_TYPE": "Debug"
41 | }
42 | },
43 | {
44 | "name": "windows-release",
45 | "displayName": "64bit Windows Release",
46 | "inherits": "windows-debug",
47 | "cacheVariables": {
48 | "CMAKE_BUILD_TYPE": "Release"
49 | }
50 | },
51 | {
52 | "name": "linux-debug",
53 | "displayName": "64bit Linux Debug",
54 | "inherits": "default",
55 | "binaryDir": "${sourceDir}/../build_${env:PROJECT_NAME}_Linux-x86_64",
56 | "condition": {
57 | "type": "equals",
58 | "lhs": "${hostSystemName}",
59 | "rhs": "Linux"
60 | },
61 | "cacheVariables": {
62 | "CMAKE_CXX_COMPILER": "g++",
63 | "CMAKE_C_COMPILER": "gcc",
64 | "CMAKE_BUILD_TYPE": "Debug"
65 | }
66 | },
67 | {
68 | "name": "linux-release",
69 | "displayName": "64bit Linux Release",
70 | "inherits": "linux-debug",
71 | "cacheVariables": {
72 | "CMAKE_BUILD_TYPE": "Release"
73 | }
74 | },
75 | {
76 | "name": "macOS-debug",
77 | "displayName": "64bit macOS Debug",
78 | "inherits": "default",
79 | "binaryDir": "${sourceDir}/../build_${env:PROJECT_NAME}_Darwin-Universal",
80 | "condition": {
81 | "type": "equals",
82 | "lhs": "${hostSystemName}",
83 | "rhs": "Darwin"
84 | },
85 | "cacheVariables": {
86 | "CMAKE_CXX_COMPILER": "clang++",
87 | "CMAKE_C_COMPILER": "clang",
88 | "CMAKE_BUILD_TYPE": "Debug"
89 | }
90 | },
91 | {
92 | "name": "macOS-release",
93 | "displayName": "64bit macOS Release",
94 | "inherits": "macOS-debug",
95 | "cacheVariables": {
96 | "CMAKE_BUILD_TYPE": "Release"
97 | }
98 | }
99 | ]
100 | }
101 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Main
2 |
3 | on:
4 | push:
5 | paths-ignore:
6 | - '.gitignore'
7 | - '*.md'
8 |
9 | jobs:
10 | lint:
11 | name: 🧹 Lint / 📜 C++
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v3
15 | - name: Run clang-format style check
16 | uses: jidicula/clang-format-action@v4.9.0
17 | with:
18 | clang-format-version: '15'
19 | exclude-regex: 'extern'
20 |
21 | build:
22 | strategy:
23 | fail-fast: false
24 | matrix:
25 | build_type: ['Debug', 'Release']
26 | config:
27 | - { name: '🍏 macOS Clang', short-name: 'macOS', os: macos-latest }
28 | - { name: '🐧 Linux GCC', short-name: 'Linux', os: ubuntu-latest }
29 | - {
30 | name: '🪟 Windows MSVC',
31 | short-name: 'Windows',
32 | os: windows-latest,
33 | }
34 |
35 | name: 🛠 Build / ${{ matrix.config.name }} (${{ matrix.build_type }})
36 | runs-on: ${{ matrix.config.os }}
37 |
38 | steps:
39 | - name: Checkout
40 | uses: actions/checkout@v3
41 | with:
42 | submodules: recursive
43 |
44 | - name: Install Dependencies (macOS)
45 | if: matrix.config.os == 'macos-latest'
46 | run: brew install ccache
47 |
48 | - name: Install Dependencies (Linux)
49 | if: matrix.config.os == 'ubuntu-latest'
50 | run: |
51 | sudo apt-get update
52 | sudo apt-get install -y ccache ninja-build
53 |
54 | - name: Install Dependencies (Windows)
55 | if: matrix.config.os == 'windows-latest'
56 | run: |
57 | choco upgrade ccache ninja
58 |
59 | - name: Setup MSVC (Windows)
60 | if: matrix.config.os == 'windows-latest'
61 | uses: ilammy/msvc-dev-cmd@v1
62 |
63 | - name: ccache
64 | uses: hendrikmuhs/ccache-action@v1.2
65 | with:
66 | max-size: '10G'
67 | key: ${{ matrix.config.os }}-${{ matrix.build_type }}
68 |
69 | - name: Configure
70 | run: >
71 | mkdir GDExtension-build
72 |
73 | cmake
74 | -B GDExtension-build
75 | -G "Ninja"
76 | -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
77 | --install-prefix ${{ github.workspace }}/install-${{ matrix.build_type }}
78 | .
79 |
80 | - name: Build
81 | run: cmake --build GDExtension-build
82 |
83 | - name: Install
84 | run: cmake --install GDExtension-build
85 |
86 | - name: Upload artifact (Debug)
87 | if: matrix.build_type == 'Debug'
88 | uses: actions/upload-artifact@v4
89 | with:
90 | name: ${{ github.event.repository.name }}-${{ matrix.config.short-name }}-Debug
91 | path: |
92 | ${{ github.workspace }}/install-${{ matrix.build_type }}/*
93 |
94 | - name: Upload artifact (Release)
95 | if: matrix.build_type == 'Release'
96 | uses: actions/upload-artifact@v4
97 | with:
98 | name: ${{ github.event.repository.name }}-${{ matrix.config.short-name }}-Release
99 | path: |
100 | ${{ github.workspace }}/install-${{ matrix.build_type }}/*
101 |
--------------------------------------------------------------------------------
/.clang-format:
--------------------------------------------------------------------------------
1 | # Options are listed here:
2 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html
3 | ---
4 | AccessModifierOffset: -4
5 | AlignAfterOpenBracket: Align
6 | AlignConsecutiveAssignments: false
7 | AlignConsecutiveDeclarations: false
8 | AlignEscapedNewlines: Right
9 | AlignOperands: true
10 | AlignTrailingComments: true
11 | AllowAllArgumentsOnNextLine: true
12 | AllowAllConstructorInitializersOnNextLine: true
13 | AllowAllParametersOfDeclarationOnNextLine: true
14 | AllowShortBlocksOnASingleLine: false
15 | AllowShortCaseLabelsOnASingleLine: false
16 | AllowShortFunctionsOnASingleLine: None
17 | AllowShortLambdasOnASingleLine: All
18 | AllowShortIfStatementsOnASingleLine: Never
19 | AllowShortLoopsOnASingleLine: false
20 | AlwaysBreakAfterDefinitionReturnType: None
21 | AlwaysBreakAfterReturnType: None
22 | AlwaysBreakBeforeMultilineStrings: false
23 | AlwaysBreakTemplateDeclarations: MultiLine
24 | BinPackArguments: true
25 | BinPackParameters: true
26 | BitFieldColonSpacing: Both
27 | BraceWrapping:
28 | AfterCaseLabel: true
29 | AfterClass: true
30 | AfterControlStatement: true
31 | AfterEnum: true
32 | AfterFunction: true
33 | AfterNamespace: true
34 | AfterStruct: true
35 | AfterUnion: true
36 | AfterExternBlock: true
37 | BeforeCatch: true
38 | BeforeElse: true
39 | IndentBraces: false
40 | SplitEmptyFunction: true
41 | SplitEmptyRecord: true
42 | SplitEmptyNamespace: true
43 | BreakBeforeBinaryOperators: None
44 | BreakBeforeBraces: Custom
45 | BreakBeforeInheritanceComma: false
46 | BreakInheritanceList: BeforeColon
47 | BreakBeforeTernaryOperators: true
48 | BreakConstructorInitializersBeforeComma: false
49 | BreakConstructorInitializers: AfterColon
50 | BreakStringLiterals: true
51 | ColumnLimit: 100
52 | CompactNamespaces: false
53 | ConstructorInitializerAllOnOneLineOrOnePerLine: false
54 | ConstructorInitializerIndentWidth: 4
55 | ContinuationIndentWidth: 4
56 | Cpp11BracedListStyle: false
57 | DeriveLineEnding: false
58 | DerivePointerAlignment: false
59 | EmptyLineBeforeAccessModifier: LogicalBlock
60 | EmptyLineAfterAccessModifier: Never
61 | FixNamespaceComments: false
62 | IncludeBlocks: Preserve
63 | IncludeCategories:
64 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
65 | Priority: 2
66 | - Regex: '^(<|"(gtest|gmock|isl|json)/)'
67 | Priority: 3
68 | - Regex: '.*'
69 | Priority: 1
70 | IncludeIsMainRegex: '(Test)?$'
71 | IndentCaseLabels: true
72 | IndentPPDirectives: None
73 | IndentWidth: 4
74 | IndentWrappedFunctionNames: true
75 | InsertBraces: true
76 | KeepEmptyLinesAtTheStartOfBlocks: true
77 | Language: Cpp
78 | MaxEmptyLinesToKeep: 1
79 | NamespaceIndentation: All
80 | PenaltyBreakAssignment: 2
81 | PenaltyBreakBeforeFirstCallParameter: 19
82 | PenaltyBreakComment: 300
83 | PenaltyBreakFirstLessLess: 120
84 | PenaltyBreakString: 1000
85 | PenaltyBreakTemplateDeclaration: 10
86 | PenaltyExcessCharacter: 1000000
87 | PenaltyReturnTypeOnItsOwnLine: 1000
88 | PointerAlignment: Right
89 | ReflowComments: true
90 | SortIncludes: true
91 | SortUsingDeclarations: true
92 | SpaceAfterCStyleCast: false
93 | SpaceAfterLogicalNot: false
94 | SpaceAfterTemplateKeyword: true
95 | SpaceBeforeAssignmentOperators: true
96 | SpaceBeforeCaseColon: false
97 | SpaceBeforeCpp11BracedList: false
98 | SpaceBeforeCtorInitializerColon: true
99 | SpaceBeforeInheritanceColon: true
100 | SpaceBeforeParens: ControlStatements
101 | SpaceBeforeRangeBasedForLoopColon: true
102 | SpaceInEmptyParentheses: false
103 | SpacesBeforeTrailingComments: 1
104 | SpacesInAngles: false
105 | SpacesInContainerLiterals: true
106 | SpacesInCStyleCastParentheses: false
107 | SpacesInParentheses: true
108 | SpacesInSquareBrackets: false
109 | Standard: c++17
110 | TabWidth: 4
111 | UseCRLF: false
112 | UseTab: Never
113 |
--------------------------------------------------------------------------------
/cmake/CompilerWarnings.cmake:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Unlicense
2 | # by Andy Maloney
3 |
4 | string( TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPERCASE )
5 |
6 | if ( NOT MSVC )
7 | option( ${PROJECT_NAME_UPPERCASE}_WARN_EVERYTHING "Turn on all warnings (not recommended - used for lib development)" OFF )
8 | endif()
9 |
10 | option( ${PROJECT_NAME_UPPERCASE}_WARNING_AS_ERROR "Treat warnings as errors" ON )
11 |
12 | # Add warnings based on compiler
13 | # Set some helper variables for readability
14 | set( compiler_is_clang "$,$>" )
15 | set( compiler_is_gnu "$" )
16 | set( compiler_is_msvc "$" )
17 |
18 | target_compile_options( ${PROJECT_NAME}
19 | PRIVATE
20 | # MSVC only
21 | $<${compiler_is_msvc}:
22 | /W4
23 |
24 | /w14263 # 'function': member function does not override any base class virtual member function
25 | /w14296 # 'operator': expression is always 'boolean_value'
26 | /w14311 # 'variable': pointer truncation from 'type1' to 'type2'
27 | /w14545 # expression before comma evaluates to a function which is missing an argument list
28 | /w14546 # function call before comma missing argument list
29 | /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect
30 | /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'?
31 | /w14619 # pragma warning: there is no warning number 'number'
32 | /w14640 # thread un-safe static member initialization
33 | /w14905 # wide string literal cast to 'LPSTR'
34 | /w14906 # string literal cast to 'LPWSTR'
35 |
36 | # Disable warnings which bleed through from godot-cpp's macros.
37 | /wd4100 # unreferenced formal parameter
38 | >
39 |
40 | # Clang and GNU
41 | $<$:
42 | -Wall
43 | -Wcast-align
44 | -Wctor-dtor-privacy
45 | -Wextra
46 | -Wformat=2
47 | -Wnon-virtual-dtor
48 | -Wnull-dereference
49 | -Woverloaded-virtual
50 | -Wpedantic
51 | -Wshadow
52 | -Wunused
53 | -Wwrite-strings
54 |
55 | # Disable warnings which bleed through from godot-cpp's macros.
56 | -Wno-unused-parameter
57 | >
58 |
59 | # Clang only
60 | $<${compiler_is_clang}:
61 | -Wdocumentation
62 | -Wimplicit-fallthrough
63 | >
64 |
65 | # GNU only
66 | $<${compiler_is_gnu}:
67 | -Walloc-zero
68 | -Wduplicated-branches
69 | -Wduplicated-cond
70 | -Wlogical-op
71 | >
72 | )
73 |
74 | # Turn on (almost) all warnings on Clang, Apple Clang, and GNU.
75 | # Useful for internal development, but too noisy for general development.
76 | function( set_warn_everything )
77 | message( STATUS "[${PROJECT_NAME}] Turning on (almost) all warnings")
78 |
79 | target_compile_options( ${PROJECT_NAME}
80 | PRIVATE
81 | # Clang and GNU
82 | $<$:
83 | -Weverything
84 | -Wno-c++98-compat
85 | -Wno-c++98-compat-pedantic
86 | -Wno-padded
87 | >
88 | )
89 | endfunction()
90 |
91 | if ( NOT MSVC AND ${PROJECT_NAME_UPPERCASE}_WARN_EVERYTHING )
92 | set_warn_everything()
93 | endif()
94 |
95 | # Treat warnings as errors
96 | function( set_warning_as_error )
97 | message( STATUS "[${PROJECT_NAME}] Treating warnings as errors")
98 |
99 | if ( CMAKE_VERSION VERSION_GREATER_EQUAL "3.24" )
100 | set_target_properties( ${PROJECT_NAME}
101 | PROPERTIES
102 | COMPILE_WARNING_AS_ERROR ON
103 | )
104 | else()
105 | target_compile_options( ${PROJECT_NAME}
106 | PRIVATE
107 | $<${compiler_is_msvc}:/WX>
108 | $<$:-Werror>
109 | )
110 | endif()
111 | endfunction()
112 |
113 | if ( ${PROJECT_NAME_UPPERCASE}_WARNING_AS_ERROR )
114 | set_warning_as_error()
115 | endif()
116 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Unlicense
2 |
3 | cmake_minimum_required( VERSION 3.22 )
4 |
5 | message( STATUS "Using CMake ${CMAKE_VERSION}" )
6 |
7 | # Require out-of-source builds
8 | file( TO_CMAKE_PATH "${PROJECT_BINARY_DIR}/CMakeLists.txt" LOC_PATH )
9 |
10 | if ( EXISTS "${LOC_PATH}" )
11 | message( FATAL_ERROR "You cannot build in the source directory. Please use a build subdirectory." )
12 | endif()
13 |
14 | # Add paths to modules
15 | list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" )
16 |
17 | # Turn on link time optimization for everything
18 | set( CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON )
19 |
20 | # Output compile commands to compile_commands.json (for debugging CMake issues)
21 | set( CMAKE_EXPORT_COMPILE_COMMANDS ON )
22 |
23 | # Build universal lib on macOS
24 | # Note that CMAKE_OSX_ARCHITECTURES must be set before project().
25 | if ( APPLE )
26 | set( CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "" )
27 | endif()
28 |
29 | # Main project information
30 | project( GDExtensionTemplate
31 | LANGUAGES
32 | CXX
33 | VERSION
34 | 0.1.0
35 | )
36 |
37 | # Create our library
38 | add_library( ${PROJECT_NAME} SHARED )
39 |
40 | target_compile_features( ${PROJECT_NAME}
41 | PRIVATE
42 | cxx_std_17
43 | )
44 |
45 | # LIB_ARCH is the architecture being built. It is set to the build system's architecture.
46 | # For macOS, we build a universal library (both arm64 and x86_64).
47 | set( LIB_ARCH ${CMAKE_SYSTEM_PROCESSOR} )
48 | if ( APPLE )
49 | set( LIB_ARCH "universal" )
50 | endif()
51 |
52 | # LIB_DIR is where the actual library ends up. This is used in both the build directory and the
53 | # install directory and needs to be consistent with the paths in the gdextension file.
54 | # e.g. linux.release.x86_64 = "lib/Linux-x86_64/libGDExtensionTemplate.so"
55 | set( LIB_DIR "lib/${CMAKE_SYSTEM_NAME}-${LIB_ARCH}" )
56 |
57 | message( STATUS "Building ${PROJECT_NAME} for ${LIB_ARCH} on ${CMAKE_SYSTEM_NAME}")
58 |
59 | # BUILD_OUTPUT_DIR is where we put the resulting library (in the build directory)
60 | set( BUILD_OUTPUT_DIR "${PROJECT_BINARY_DIR}/${PROJECT_NAME}/" )
61 |
62 | set_target_properties( ${PROJECT_NAME}
63 | PROPERTIES
64 | CXX_VISIBILITY_PRESET hidden
65 | VISIBILITY_INLINES_HIDDEN true
66 | RUNTIME_OUTPUT_DIRECTORY "${BUILD_OUTPUT_DIR}/${LIB_DIR}"
67 | LIBRARY_OUTPUT_DIRECTORY "${BUILD_OUTPUT_DIR}/${LIB_DIR}"
68 | )
69 |
70 | if( NOT DEFINED CMAKE_DEBUG_POSTFIX )
71 | set_target_properties( ${PROJECT_NAME}
72 | PROPERTIES
73 | DEBUG_POSTFIX "-d"
74 | )
75 | endif()
76 |
77 | # Copy over additional files from the support_files directory
78 | add_custom_command(
79 | TARGET ${PROJECT_NAME} POST_BUILD
80 | COMMAND ${CMAKE_COMMAND} -E copy_directory
81 | "${CMAKE_SOURCE_DIR}/support_files"
82 | ${BUILD_OUTPUT_DIR}
83 | )
84 |
85 | # Warnings
86 | include( CompilerWarnings )
87 |
88 | # Create and include version info file from git
89 | include( GitVersionInfo )
90 |
91 | add_subdirectory( src )
92 |
93 | # Install library, extension file, and support files in ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}
94 | set( INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/" )
95 |
96 | message( STATUS "Install directory: ${INSTALL_DIR}")
97 |
98 | install( TARGETS ${PROJECT_NAME}
99 | LIBRARY
100 | DESTINATION ${INSTALL_DIR}/${LIB_DIR}
101 | RUNTIME
102 | DESTINATION ${INSTALL_DIR}/${LIB_DIR}
103 | )
104 |
105 | # Copy over support files
106 | install( DIRECTORY "${CMAKE_SOURCE_DIR}/support_files/"
107 | DESTINATION ${INSTALL_DIR}
108 | PATTERN ".*" EXCLUDE
109 | )
110 |
111 | add_subdirectory( templates )
112 |
113 | # ccache
114 | # Turns on ccache if found
115 | include( ccache )
116 |
117 | # Formatting
118 | # Adds a custom target to format all the code at once
119 | include( ClangFormat )
120 |
121 | # godot-cpp
122 | # From here: https://github.com/godotengine/godot-cpp
123 | if ( NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/extern/godot-cpp/Makefile" )
124 | message(
125 | FATAL_ERROR
126 | "[${PROJECT_NAME}] The godot-cpp submodule was not downloaded. Please update submodules: git submodule update --init --recursive."
127 | )
128 | endif()
129 |
130 | set( GODOT_CPP_SYSTEM_HEADERS ON CACHE BOOL "" FORCE )
131 |
132 | add_subdirectory( extern/godot-cpp )
133 |
134 | set_target_properties( godot-cpp
135 | PROPERTIES
136 | CXX_VISIBILITY_PRESET hidden # visibility needs to be the same as the main library
137 | )
138 |
139 | target_link_libraries( ${PROJECT_NAME}
140 | PRIVATE
141 | godot-cpp
142 | )
143 |
--------------------------------------------------------------------------------
/src/Example.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | // Copied from godot-cpp/test/src and modified.
3 |
4 | #include "godot_cpp/classes/control.hpp"
5 | #include "godot_cpp/classes/global_constants.hpp"
6 | #include "godot_cpp/classes/tile_map.hpp"
7 | #include "godot_cpp/classes/tile_set.hpp"
8 | #include "godot_cpp/classes/viewport.hpp"
9 | #include "godot_cpp/core/binder_common.hpp"
10 | #include "godot_cpp/variant/variant.hpp"
11 |
12 | class ExampleRef : public godot::RefCounted
13 | {
14 | GDCLASS( ExampleRef, RefCounted )
15 |
16 | public:
17 | ExampleRef();
18 | ~ExampleRef() override;
19 |
20 | void setId( int inID );
21 | int getID() const;
22 |
23 | bool wasPostInitialized() const
24 | {
25 | return mPostInitialized;
26 | }
27 |
28 | protected:
29 | static void _bind_methods();
30 |
31 | void _notification( int inWhat );
32 |
33 | private:
34 | static int sInstanceCount;
35 | static int sLastID;
36 |
37 | int mID;
38 | bool mPostInitialized = false;
39 | };
40 |
41 | class ExampleMin : public godot::Control
42 | {
43 | GDCLASS( ExampleMin, Control )
44 |
45 | protected:
46 | static void _bind_methods();
47 | };
48 |
49 | class Example : public godot::Control
50 | {
51 | GDCLASS( Example, godot::Control )
52 |
53 | public:
54 | // Constants.
55 | enum Constants
56 | {
57 | FIRST,
58 | ANSWER_TO_EVERYTHING = 42,
59 | };
60 |
61 | enum
62 | {
63 | CONSTANT_WITHOUT_ENUM = 314,
64 | };
65 |
66 | enum Flags
67 | {
68 | FLAG_ONE = 1,
69 | FLAG_TWO = 2,
70 | };
71 |
72 | Example();
73 | ~Example() override;
74 |
75 | // Functions.
76 | void simpleFunc();
77 | void simpleConstFunc() const;
78 | godot::String returnSomething( const godot::String &inBase );
79 | godot::Viewport *returnSomethingConst() const;
80 | godot::Ref returnEmptyRef() const;
81 | ExampleRef *returnExtendedRef() const;
82 | godot::Ref extendedRefChecks( godot::Ref inRef ) const;
83 | godot::Variant varargsFunc( const godot::Variant **inArgs, GDExtensionInt inArgCount,
84 | GDExtensionCallError &outError );
85 | int varargsFuncNonVoidReturn( const godot::Variant **inArgs, GDExtensionInt inArgCount,
86 | GDExtensionCallError &outError );
87 | void varargsFuncVoidReturn( const godot::Variant **inArgs, GDExtensionInt inArgCount,
88 | GDExtensionCallError &outError );
89 |
90 | void emitCustomSignal( const godot::String &inName, int inValue );
91 | int defArgs( int inA = 100, int inB = 200 ) const;
92 |
93 | godot::Array testArray() const;
94 | void testTypedArrayArg( const godot::TypedArray &inArray );
95 | godot::TypedArray testTypedArray() const;
96 | godot::Dictionary testDictionary() const;
97 | Example *testNodeArgument( Example *inNode ) const;
98 | godot::String testStringOps() const;
99 | godot::String testStrUtility() const;
100 | bool testStringIsFortyTwo( const godot::String &inString ) const;
101 | godot::String testStringResize( godot::String ioString ) const;
102 | int testVectorOps() const;
103 |
104 | bool testObjectCastToNode( godot::Object *inObject ) const;
105 | bool testObjectCastToControl( godot::Object *inObject ) const;
106 | bool testObjectCastToExample( godot::Object *inObject ) const;
107 |
108 | godot::Vector2i testVariantVector2iConversion( const godot::Variant &inVariant ) const;
109 | int testVariantIntConversion( const godot::Variant &inVariant ) const;
110 | float testVariantFloatConversion( const godot::Variant &inVariant ) const;
111 | godot::Variant testVariantCall( godot::Variant inVariant );
112 | godot::Variant testVariantIterator( const godot::Variant &inVariant );
113 |
114 | void testAddChild( godot::Node *inNode );
115 | void testSetTileset( godot::TileMap *inTilemap,
116 | const godot::Ref &inTileset ) const;
117 |
118 | godot::Callable testCallableMP();
119 | godot::Callable testCallableMPRet();
120 | godot::Callable testCallableMPRetC() const;
121 | godot::Callable testCallableMPStatic() const;
122 | godot::Callable testCallableMPStaticRet() const;
123 | godot::Callable testCustomCallable() const;
124 |
125 | void callableBind();
126 |
127 | void unboundMethod1( godot::Object *inObject, godot::String inString, int inInt );
128 | godot::String unboundMethod2( godot::Object *inObject, godot::String inString, int inInt );
129 | godot::String unboundMethod3( godot::Object *inObject, godot::String inString,
130 | int inInt ) const;
131 | static void unboundStaticMethod1( Example *inObject, godot::String inString, int inInt );
132 | static godot::String unboundStaticMethod2( godot::Object *inObject, godot::String inString,
133 | int inInt );
134 |
135 | godot::BitField testBitfield( godot::BitField inFlags );
136 |
137 | // RPC
138 | void testRPC( int inValue );
139 | void testSendRPC( int inValue );
140 | int returnLastRPCArg();
141 |
142 | // Property
143 | void setCustomPosition( const godot::Vector2 &inPos );
144 | godot::Vector2 getCustomPosition() const;
145 | godot::Vector4 getV4() const;
146 |
147 | bool testPostInitialize() const;
148 |
149 | // Static method.
150 | static int testStatic( int inA, int inB );
151 | static void testStatic2();
152 |
153 | // Virtual function override (no need to bind manually).
154 | virtual bool _has_point( const godot::Vector2 &inPoint ) const override;
155 |
156 | protected:
157 | static void _bind_methods();
158 |
159 | void _notification( int inWhat );
160 | bool _set( const godot::StringName &inName, const godot::Variant &inValue );
161 | bool _get( const godot::StringName &inName, godot::Variant &outReturn ) const;
162 | void _get_property_list( godot::List *outList ) const;
163 | bool _property_can_revert( const godot::StringName &inName ) const;
164 | bool _property_get_revert( const godot::StringName &inName, godot::Variant &outProperty ) const;
165 | void _validate_property( godot::PropertyInfo &inProperty ) const;
166 |
167 | godot::String _to_string() const;
168 |
169 | private:
170 | godot::Vector2 mCustomPosition;
171 | godot::Vector3 mPropertyFromList;
172 | godot::Vector2 mDProp[3];
173 |
174 | int mLastRPCArg = 0;
175 | };
176 |
177 | VARIANT_ENUM_CAST( Example::Constants );
178 | VARIANT_BITFIELD_CAST( Example::Flags );
179 |
180 | enum EnumWithoutClass
181 | {
182 | OUTSIDE_OF_CLASS = 512
183 | };
184 | VARIANT_ENUM_CAST( EnumWithoutClass );
185 |
186 | class ExampleVirtual : public godot::Object
187 | {
188 | GDCLASS( ExampleVirtual, godot::Object )
189 |
190 | protected:
191 | static void _bind_methods();
192 | };
193 |
194 | class ExampleAbstract : public godot::Object
195 | {
196 | GDCLASS( ExampleAbstract, godot::Object )
197 |
198 | protected:
199 | static void _bind_methods();
200 | };
201 |
--------------------------------------------------------------------------------
/cmake/GetGitRevisionDescription.cmake:
--------------------------------------------------------------------------------
1 | # - Returns a version string from Git
2 | #
3 | # These functions force a re-configure on each git commit so that you can
4 | # trust the values of the variables in your build system.
5 | #
6 | # get_git_head_revision( [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR])
7 | #
8 | # Returns the refspec and sha hash of the current head revision
9 | #
10 | # git_describe( [ ...])
11 | #
12 | # Returns the results of git describe on the source tree, and adjusting
13 | # the output so that it tests false if an error occurs.
14 | #
15 | # git_describe_working_tree( [ ...])
16 | #
17 | # Returns the results of git describe on the working tree (--dirty option),
18 | # and adjusting the output so that it tests false if an error occurs.
19 | #
20 | # git_get_exact_tag( [ ...])
21 | #
22 | # Returns the results of git describe --exact-match on the source tree,
23 | # and adjusting the output so that it tests false if there was no exact
24 | # matching tag.
25 | #
26 | # git_local_changes()
27 | #
28 | # Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes.
29 | # Uses the return code of "git diff-index --quiet HEAD --".
30 | # Does not regard untracked files.
31 | #
32 | # Requires CMake 2.6 or newer (uses the 'function' command)
33 | #
34 | # Original Author:
35 | # 2009-2020 Ryan Pavlik
36 | # http://academic.cleardefinition.com
37 | #
38 | # Copyright 2009-2013, Iowa State University.
39 | # Copyright 2013-2020, Ryan Pavlik
40 | # Copyright 2013-2020, Contributors
41 | # SPDX-License-Identifier: BSL-1.0
42 | # Distributed under the Boost Software License, Version 1.0.
43 | # (See accompanying file LICENSE_1_0.txt or copy at
44 | # http://www.boost.org/LICENSE_1_0.txt)
45 |
46 | if(__get_git_revision_description)
47 | return()
48 | endif()
49 | set(__get_git_revision_description YES)
50 |
51 | # We must run the following at "include" time, not at function call time,
52 | # to find the path to this module rather than the path to a calling list file
53 | get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
54 |
55 | # Function _git_find_closest_git_dir finds the next closest .git directory
56 | # that is part of any directory in the path defined by _start_dir.
57 | # The result is returned in the parent scope variable whose name is passed
58 | # as variable _git_dir_var. If no .git directory can be found, the
59 | # function returns an empty string via _git_dir_var.
60 | #
61 | # Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and
62 | # neither foo nor bar contain a file/directory .git. This wil return
63 | # C:/bla/.git
64 | #
65 | function(_git_find_closest_git_dir _start_dir _git_dir_var)
66 | set(cur_dir "${_start_dir}")
67 | set(git_dir "${_start_dir}/.git")
68 | while(NOT EXISTS "${git_dir}")
69 | # .git dir not found, search parent directories
70 | set(git_previous_parent "${cur_dir}")
71 | get_filename_component(cur_dir "${cur_dir}" DIRECTORY)
72 | if(cur_dir STREQUAL git_previous_parent)
73 | # We have reached the root directory, we are not in git
74 | set(${_git_dir_var}
75 | ""
76 | PARENT_SCOPE)
77 | return()
78 | endif()
79 | set(git_dir "${cur_dir}/.git")
80 | endwhile()
81 | set(${_git_dir_var}
82 | "${git_dir}"
83 | PARENT_SCOPE)
84 | endfunction()
85 |
86 | function(get_git_head_revision _refspecvar _hashvar)
87 | _git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR)
88 |
89 | if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR")
90 | set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE)
91 | else()
92 | set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE)
93 | endif()
94 | if(NOT "${GIT_DIR}" STREQUAL "")
95 | file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}"
96 | "${GIT_DIR}")
97 | if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR)
98 | # We've gone above the CMake root dir.
99 | set(GIT_DIR "")
100 | endif()
101 | endif()
102 | if("${GIT_DIR}" STREQUAL "")
103 | set(${_refspecvar}
104 | "GITDIR-NOTFOUND"
105 | PARENT_SCOPE)
106 | set(${_hashvar}
107 | "GITDIR-NOTFOUND"
108 | PARENT_SCOPE)
109 | return()
110 | endif()
111 |
112 | # Check if the current source dir is a git submodule or a worktree.
113 | # In both cases .git is a file instead of a directory.
114 | #
115 | if(NOT IS_DIRECTORY ${GIT_DIR})
116 | # The following git command will return a non empty string that
117 | # points to the super project working tree if the current
118 | # source dir is inside a git submodule.
119 | # Otherwise the command will return an empty string.
120 | #
121 | execute_process(
122 | COMMAND "${GIT_EXECUTABLE}" rev-parse
123 | --show-superproject-working-tree
124 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
125 | OUTPUT_VARIABLE out
126 | ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
127 | if(NOT "${out}" STREQUAL "")
128 | # If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule
129 | file(READ ${GIT_DIR} submodule)
130 | string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE
131 | ${submodule})
132 | string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
133 | get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
134 | get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE}
135 | ABSOLUTE)
136 | set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
137 | else()
138 | # GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree
139 | file(READ ${GIT_DIR} worktree_ref)
140 | # The .git directory contains a path to the worktree information directory
141 | # inside the parent git repo of the worktree.
142 | #
143 | string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir
144 | ${worktree_ref})
145 | string(STRIP ${git_worktree_dir} git_worktree_dir)
146 | _git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR)
147 | set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD")
148 | endif()
149 | else()
150 | set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
151 | endif()
152 | set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
153 | if(NOT EXISTS "${GIT_DATA}")
154 | file(MAKE_DIRECTORY "${GIT_DATA}")
155 | endif()
156 |
157 | if(NOT EXISTS "${HEAD_SOURCE_FILE}")
158 | return()
159 | endif()
160 | set(HEAD_FILE "${GIT_DATA}/HEAD")
161 | configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY)
162 |
163 | configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
164 | "${GIT_DATA}/grabRef.cmake" @ONLY)
165 | include("${GIT_DATA}/grabRef.cmake")
166 |
167 | set(${_refspecvar}
168 | "${HEAD_REF}"
169 | PARENT_SCOPE)
170 | set(${_hashvar}
171 | "${HEAD_HASH}"
172 | PARENT_SCOPE)
173 | endfunction()
174 |
175 | function(git_describe _var)
176 | if(NOT GIT_FOUND)
177 | find_package(Git QUIET)
178 | endif()
179 | get_git_head_revision(refspec hash)
180 | if(NOT GIT_FOUND)
181 | set(${_var}
182 | "GIT-NOTFOUND"
183 | PARENT_SCOPE)
184 | return()
185 | endif()
186 | if(NOT hash)
187 | set(${_var}
188 | "HEAD-HASH-NOTFOUND"
189 | PARENT_SCOPE)
190 | return()
191 | endif()
192 |
193 | # TODO sanitize
194 | #if((${ARGN}" MATCHES "&&") OR
195 | # (ARGN MATCHES "||") OR
196 | # (ARGN MATCHES "\\;"))
197 | # message("Please report the following error to the project!")
198 | # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
199 | #endif()
200 |
201 | #message(STATUS "Arguments to execute_process: ${ARGN}")
202 |
203 | execute_process(
204 | COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN}
205 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
206 | RESULT_VARIABLE res
207 | OUTPUT_VARIABLE out
208 | ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
209 | if(NOT res EQUAL 0)
210 | set(out "${out}-${res}-NOTFOUND")
211 | endif()
212 |
213 | set(${_var}
214 | "${out}"
215 | PARENT_SCOPE)
216 | endfunction()
217 |
218 | function(git_describe_working_tree _var)
219 | if(NOT GIT_FOUND)
220 | find_package(Git QUIET)
221 | endif()
222 | if(NOT GIT_FOUND)
223 | set(${_var}
224 | "GIT-NOTFOUND"
225 | PARENT_SCOPE)
226 | return()
227 | endif()
228 |
229 | execute_process(
230 | COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN}
231 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
232 | RESULT_VARIABLE res
233 | OUTPUT_VARIABLE out
234 | ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
235 | if(NOT res EQUAL 0)
236 | set(out "${out}-${res}-NOTFOUND")
237 | endif()
238 |
239 | set(${_var}
240 | "${out}"
241 | PARENT_SCOPE)
242 | endfunction()
243 |
244 | function(git_get_exact_tag _var)
245 | git_describe(out --exact-match ${ARGN})
246 | set(${_var}
247 | "${out}"
248 | PARENT_SCOPE)
249 | endfunction()
250 |
251 | function(git_local_changes _var)
252 | if(NOT GIT_FOUND)
253 | find_package(Git QUIET)
254 | endif()
255 | get_git_head_revision(refspec hash)
256 | if(NOT GIT_FOUND)
257 | set(${_var}
258 | "GIT-NOTFOUND"
259 | PARENT_SCOPE)
260 | return()
261 | endif()
262 | if(NOT hash)
263 | set(${_var}
264 | "HEAD-HASH-NOTFOUND"
265 | PARENT_SCOPE)
266 | return()
267 | endif()
268 |
269 | execute_process(
270 | COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD --
271 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
272 | RESULT_VARIABLE res
273 | OUTPUT_VARIABLE out
274 | ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
275 | if(res EQUAL 0)
276 | set(${_var}
277 | "CLEAN"
278 | PARENT_SCOPE)
279 | else()
280 | set(${_var}
281 | "DIRTY"
282 | PARENT_SCOPE)
283 | endif()
284 | endfunction()
285 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](LICENSE) 
2 |
3 | # GDExtensionTemplate
4 |
5 | This project is meant as a starting point for creating new C++/CMake-based Godot 4 extensions. The goal is to lower the barrier to entry to building a GDExtension using [CMake](https://cmake.org).
6 |
7 | It is currently set up to work with the **[Godot 4.2](https://github.com/godotengine/godot/releases/tag/4.2-stable)** release (see [tags](https://github.com/asmaloney/GDExtensionTemplate/tags) for previous versions).
8 |
9 | Since the majority of C++ open source projects use CMake, I wanted to offer an alternative to the _scons_ system for building Godot extensions (if you use _scons_, check out Nathan Franke's [gdextension](https://github.com/nathanfranke/gdextension) template or Patrick's [GDExtensionSummator](https://github.com/paddy-exe/GDExtensionSummator) template).
10 |
11 | > **Note:** This project is not meant to be a dependency. It is intended to be copied (not forked) and made into your own project. Git itself doesn't provide a nice way to do this (as far as I can tell), but GitHub provides a **Use this template** button (beside where you clone a repo). This will [create a copy for you](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template) without all the history.
12 |
13 | ## Features
14 |
15 | This template project sets up a lot of the build details so you can get started and focus on your code:
16 |
17 | - includes **[godot-cpp](https://github.com/godotengine/godot-cpp) as a submodule** and links it statically to your shared library
18 | - creates `.gdextension` files based on your project name
19 | - copies over any support files from the `support_files` directory into your extension directory
20 | - includes example of adding custom icons to your classes/nodes ([see below](#custom-node-icons))
21 | - automatically generates a _**Version.h**_ header file which:
22 | - includes a preprocessor macro for conditional compilation
23 | ```cpp
24 | #if GDEXTENSIONTEMPLATE_VERSION < GDEXTENSIONTEMPLATE_VERSION_CHECK(2, 1, 0)
25 | // do stuff
26 | #endif
27 | ```
28 | - includes git information in the version strings (e.g. `GDExtensionTemplate v1.0.1-gf6446f8`)
29 | - includes an example which exposes the version string to GDScript so you can call it like this
30 | ```py
31 | print( GDExtensionTemplate.version() )
32 | ```
33 | - keeps itself up-to-date when the git branch/tag/HEAD changes
34 | - uses **[ccache](https://ccache.dev/)** (if available) for faster rebuilds
35 | - builds **universal library** (x86_64 and arm64) on macOS
36 | - provides **cmake targets**:
37 | - _install_: install all files with the correct structure to `CMAKE_INSTALL_PREFIX`
38 | - _clang-format_: runs `clang-format` on all sources
39 | - includes **GitHub workflows** (CI) for:
40 | - building the extension on **Linux x86_64** (gcc), **macOS universal** (clang), and **Windows x86_64** (MSVC)
41 | - generating debug & release packages on each commit
42 | - using `ccache` to improve CI build times when available
43 | - checking code formatting using `clang-format`
44 |
45 | ## Prerequisites
46 |
47 | To use this locally on your machine, you will need the following:
48 |
49 | - **[CMake](https://cmake.org/)** v3.22+
50 | - C++ Compiler with at least **C++17** support (any recent compiler)
51 | - (optional) **[ccache](https://ccache.dev/)** for faster rebuilds
52 | - (optional) **[clang-format](https://clang.llvm.org/docs/ClangFormat.html)** for linting and automatic code formatting (CI uses clang-format version 15)
53 |
54 | The GitHub actions (CI) are set up to include all of these tools. To see how to download them on your platform, take a look at the [workflow](.github/workflows/main.yml) file.
55 |
56 | ## How To Use
57 |
58 | ### Setup
59 |
60 | To use this for your own project:
61 |
62 | - _copy_ this repository and rename the directory to the name of your extension
63 | > GitHub provides a **Use this template** button (beside where you clone a repo). This will [create a copy for you](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template) without all the history.
64 | - in _CMakeLists.txt_, change `GDExtensionTemplate` in the `project` macro to the name of your extension
65 | ```cmake
66 | project(
67 | LANGUAGES
68 | CXX
69 | VERSION
70 | 0.1.0
71 | )
72 | ```
73 | If you also have plain C files in your project, add `C` to the languages.
74 | - replace the example code in `src` with your own (I would suggest keeping _RegisterExtension.cpp_ and using it to register your classes)
75 | > **Note:** If you change the entry symbol (`GDExtensionInit`) in _RegisterExtension.cpp_, you will need to update your `templates/*.gdextension.in` files.
76 | - replace `CHANGELOG.md` with your own (I would encourage adhering to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and the use of [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) )
77 | - replace this `README.md` with your own
78 | - replace custom node icon ([see below](#custom-node-icons))
79 |
80 | Optional:
81 |
82 | - [contribute to the project](#how-to-contribute) (it's not just 💰!)
83 | - change the platforms/architectures you want to support:
84 | - edit the gdextension templates (`templates/*.gdextension.in`)
85 | - change the GitHub workflows to build the right stuff
86 | - change the `.clang-format` config file to fit your C++ style ([option documentation](https://clang.llvm.org/docs/ClangFormatStyleOptions.html))
87 | - change the compiler warnings you want to enforce (see [CompilerWarnings.cmake](cmake/CompilerWarnings.cmake))
88 | - change the LICENSE
89 |
90 | ### Custom Node Icons
91 |
92 | I have included a custom icon for the `Example` node (icon is [CC0](https://creativecommons.org/public-domain/cc0/) from [SVGRepo](https://www.svgrepo.com/svg/207485/gardening-autumn)), so you will want to remove or modify it for your own classes/nodes.
93 |
94 | The icon itself is in `support_files/icons` it is referenced in the `templates/*.gdextension.in` files.
95 |
96 | To add an icon for your custom node:
97 |
98 | - add the icon file to `support_files/icons` and name it after your node (e.g. `MyNode.svg`)
99 | - in each of the `templates/*.gdextension.in` files add an entry for your node in the `[icons]` section:
100 | ```
101 | MyNode = "icons/MyNode.svg"
102 | ```
103 |
104 | Everything in the `support_files` directory is copied into your extension, so if you don't want to use icons, remove that directory and remove the `[icons]` section from the `templates/*.gdextension.in` files.
105 |
106 | ### Build & Install
107 |
108 | Here's an example of how to build & install a release version (use the terminal to run the following commands in the parent directory of this repo):
109 |
110 | #### Not MSVC
111 |
112 | ```sh
113 | $ cmake -B GDExtensionTemplate-build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=GDExtensionTemplate-install GDExtensionTemplate
114 | $ cmake --build GDExtensionTemplate-build --parallel
115 | $ cmake --install GDExtensionTemplate-build
116 | ```
117 |
118 | #### MSVC
119 |
120 | ```sh
121 | $ cmake -B GDExtensionTemplate-build -G"Visual Studio 17 2022" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=GDExtensionTemplate-install GDExtensionTemplate
122 | $ cmake --build GDExtensionTemplate-build --config Release
123 | $ cmake --install GDExtensionTemplate-build
124 | ```
125 |
126 | This tells CMake to use `Visual Studio 2022`. There is a list of Visual Studio generators [on the CMake site](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html#visual-studio-generators) - pick the one you are using.
127 |
128 | ### Cmake Options
129 |
130 | This template defines the following additional CMake options:
131 |
132 | | Option | Description | Default |
133 | | ------------------------------------------------------------------------ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
134 | | `CCACHE_PROGRAM` | Path to `ccache` for faster rebuilds | This is automatically set **ON** if `ccache` is found. If you do not want to use it, set this to "". |
135 | | `CLANG_FORMAT_PROGRAM` | Path to `clang-format` for code formatting. | This is automatically set **ON** if `clang-format` is on. If you do not want to use it, set this to "". |
136 | | `${PROJECT_NAME_UPPERCASE}_WARN_EVERYTHING` (e.g. FOO_WARN_EVERYTHING) | Turns on all warnings. (Not available for MSVC.) | **OFF** (too noisy, but can be useful sometimes) |
137 | | `${PROJECT_NAME_UPPERCASE}_WARNING_AS_ERROR` (e.g. FOO_WARNING_AS_ERROR) | Turns warnings into errors. | **ON** |
138 |
139 | ## Ongoing Updates
140 |
141 | Once your project is up and running you might want to keep up with newer versions of Godot & godot-cpp.
142 |
143 | The key thing is that the version of _godot-cpp_ must match the version of Godot you are using (see the [godot-cpp Versioning section](https://github.com/godotengine/godot-cpp#versioning)). So if you want to use _Godot 4.0 stable_, then you need to match that with the [correct tag in godot-cpp](https://github.com/godotengine/godot-cpp/tags).
144 |
145 | Once you know the correct version of godot-cpp, change the submodule (_extern/godot-cpp_) in your extension to point at that version.
146 |
147 | Updating the submodule and making any necessary changes to your code due to changes in the API are the only things you need to pin to a specific version of Godot.
148 |
149 | ## How To Contribute
150 |
151 | These are some of the things you can do to contribute to the project:
152 |
153 | ### ✍ Write About The Project
154 |
155 | If you find the project useful, spread the word! Articles, mastodon posts, tweets, blog posts, instagram photos - whatever you're into.
156 |
157 | ### ⭐️ Add a Star
158 |
159 | If you found this project useful, please consider starring it! It helps me gauge how useful this project is.
160 |
161 | ### ☝ Raise Issues
162 |
163 | If you run into something which doesn't work as expected, raising [an issue](https://github.com/asmaloney/GDExtensionTemplate/issues) with all the relevant information to reproduce it would be helpful.
164 |
165 | ### 🐞 Bug Fixes & 🧪 New Things
166 |
167 | I am happy to review any [pull requests](https://github.com/asmaloney/GDExtensionTemplate/pulls). Please keep them as short as possible. Each pull request should be atomic and only address one issue. This helps with the review process.
168 |
169 | Note that I will not accept everything, but I welcome discussion. If you are proposing a big change, please raise it as [an issue](https://github.com/asmaloney/GDExtensionTemplate/issues) first for discussion.
170 |
171 | ### 💰 Financial
172 |
173 | Given that I'm an independent developer without funding, financial support is always appreciated. If you would like to support the project financially, you can use [GitHub sponsors](https://github.com/sponsors/asmaloney) or [Ko-fi](https://ko-fi.com/asmaloney) for one-off or recurring support. Thank you!
174 |
--------------------------------------------------------------------------------
/.vscode/godot.natvis:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | - _cowdata._ptr ? (((const unsigned int *)(_cowdata._ptr))[-1]) : 0
6 |
7 | _cowdata._ptr ? (((const unsigned int *)(_cowdata._ptr))[-1]) : 0
8 | _cowdata._ptr
9 |
10 |
11 |
12 |
13 |
14 |
15 | - count
16 |
17 | count
18 | data
19 |
20 |
21 |
22 |
23 |
24 |
25 | - _data ? (_data->size_cache) : 0
26 |
27 | _data ? (_data->size_cache) : 0
28 | _data->first
29 | next_ptr
30 | value
31 |
32 |
33 |
34 |
35 |
36 |
37 | - num_elements
38 |
39 | num_elements
40 | head_element
41 | next
42 | data
43 |
44 |
45 |
46 |
47 |
48 |
49 | - *(reinterpret_cast<int*>(_cowdata._ptr) - 1)
50 |
51 | *(reinterpret_cast<int*>(_cowdata._ptr) - 1)
52 | reinterpret_cast<VMap<$T1,$T2>::Pair*>(_cowdata._ptr)
53 |
54 |
55 |
56 |
57 |
58 | {dynamic_cast<CallableCustomMethodPointerBase*>(key.custom)->text}
59 |
60 |
61 |
66 |
67 |
68 | nil
69 | {_data._bool}
70 | {_data._int}
71 | {_data._float}
72 | {_data._transform2d}
73 | {_data._aabb}
74 | {_data._basis}
75 | {_data._transform3d}
76 | {_data._projection}
77 | {*(String *)_data._mem}
78 | {*(Vector2 *)_data._mem}
79 | {*(Rect2 *)_data._mem}
80 | {*(Vector3 *)_data._mem}
81 | {*(Vector4 *)_data._mem}
82 | {*(Plane *)_data._mem}
83 | {*(Quaternion *)_data._mem}
84 | {*(Color *)_data._mem}
85 | {*(NodePath *)_data._mem}
86 | {*(::RID *)_data._mem}
87 | {*(Object *)_data._mem}
88 | {*(Dictionary *)_data._mem}
89 | {*(Array *)_data._mem}
90 | {reinterpret_cast<const Variant::PackedArrayRef<unsigned char>*>(_data.packed_array)->array}
91 | {reinterpret_cast<const Variant::PackedArrayRef<int>*>(_data.packed_array)->array}
92 |
95 | {reinterpret_cast<const Variant::PackedArrayRef<float>*>(_data.packed_array)->array}
96 | {reinterpret_cast<const Variant::PackedArrayRef<double>*>(_data.packed_array)->array}
97 | {reinterpret_cast<const Variant::PackedArrayRef<String>*>(_data.packed_array)->array}
98 | {reinterpret_cast<const Variant::PackedArrayRef<Vector2>*>(_data.packed_array)->array}
99 | {reinterpret_cast<const Variant::PackedArrayRef<Vector3>*>(_data.packed_array)->array}
100 | {reinterpret_cast<const Variant::PackedArrayRef<Color>*>(_data.packed_array)->array}
101 |
102 | ((String *)(_data._mem))->_cowdata._ptr,s32
103 |
104 |
105 | - _data._bool
106 | - _data._int
107 | - _data._float
108 | - _data._transform2d
109 | - _data._aabb
110 | - _data._basis
111 | - _data._transform3d
112 | - *(String *)_data._mem
113 | - *(Vector2 *)_data._mem
114 | - *(Rect2 *)_data._mem
115 | - *(Vector3 *)_data._mem
116 | - *(Plane *)_data._mem
117 | - *(Quaternion *)_data._mem
118 | - *(Color *)_data._mem
119 | - *(NodePath *)_data._mem
120 | - *(::RID *)_data._mem
121 | - *(Object *)_data._mem
122 | - *(Dictionary *)_data._mem
123 | - *(Array *)_data._mem
124 | - reinterpret_cast<const Variant::PackedArrayRef<unsigned char>*>(_data.packed_array)->array
125 | - *(PackedInt32Array *)_data._mem
126 | - *(PackedInt64Array *)_data._mem
127 | - *(PackedFloat32Array *)_data._mem
128 | - *(PackedFloat64Array *)_data._mem
129 | - *(PackedStringArray *)_data._mem
130 | - *(PackedVector2Array *)_data._mem
131 | - *(PackedVector3Array *)_data._mem
132 | - *(PackedColorArray *)_data._mem
133 |
134 |
135 |
136 |
137 | [empty]
138 | {_cowdata._ptr,s32}
139 | _cowdata._ptr,s32
140 |
141 |
142 |
143 | {*reinterpret_cast<void**>(opaque),s32}
144 |
145 | - *reinterpret_cast<void**>(opaque)
146 | - *reinterpret_cast<void**>(opaque),s32
147 |
148 |
149 |
150 |
151 | {_data->cname}
152 | {_data->name,s32}
153 | [empty]
154 | _data->cname
155 | _data->name,s32
156 |
157 |
158 |
159 |
160 | {(*reinterpret_cast<const char***>(opaque))[1],s8}
161 | {(*reinterpret_cast<const char***>(opaque))[2],s32}
162 |
163 | - *reinterpret_cast<void**>(opaque)
164 | - (*reinterpret_cast<const char***>(opaque))+1
165 | - (*reinterpret_cast<const char***>(opaque))[1],s8
166 |
167 |
168 |
169 |
170 | "{user.name}" {slot_map}
171 | "{slot_map}
172 |
173 |
174 |
175 | {{{x},{y}}}
176 |
177 | - x
178 | - y
179 |
180 |
181 |
182 |
183 | {{{x},{y},{z}}}
184 |
185 | - x
186 | - y
187 | - z
188 |
189 |
190 |
191 |
192 | Quaternion {{{x},{y},{z},{w}}}
193 |
194 | - x
195 | - y
196 | - z
197 | - w
198 |
199 |
200 |
201 |
202 | Color {{{r},{g},{b},{a}}}
203 |
204 | - r
205 | - g
206 | - b
207 | - a
208 |
209 |
210 |
211 |
--------------------------------------------------------------------------------
/src/Example.cpp:
--------------------------------------------------------------------------------
1 | // Copied from godot-cpp/test/src and modified.
2 |
3 | #include "godot_cpp/classes/global_constants.hpp"
4 | #include "godot_cpp/classes/label.hpp"
5 | #include "godot_cpp/core/class_db.hpp"
6 | #include "godot_cpp/variant/utility_functions.hpp"
7 |
8 | #include "Example.h"
9 |
10 | // Used to mark unused parameters to indicate intent and suppress warnings.
11 | #define UNUSED( expr ) (void)( expr )
12 |
13 | namespace
14 | {
15 | constexpr int MAGIC_NUMBER = 42;
16 |
17 | class MyCallableCustom : public godot::CallableCustom
18 | {
19 | public:
20 | virtual uint32_t hash() const
21 | {
22 | return 27;
23 | }
24 |
25 | virtual godot::String get_as_text() const
26 | {
27 | return "";
28 | }
29 |
30 | static bool compare_equal_func( const CallableCustom *inA, const CallableCustom *inB )
31 | {
32 | return inA == inB;
33 | }
34 |
35 | virtual CompareEqualFunc get_compare_equal_func() const
36 | {
37 | return &MyCallableCustom::compare_equal_func;
38 | }
39 |
40 | static bool compare_less_func( const CallableCustom *inA, const CallableCustom *inB )
41 | {
42 | return reinterpret_cast( inA ) < reinterpret_cast( inB );
43 | }
44 |
45 | virtual CompareLessFunc get_compare_less_func() const
46 | {
47 | return &MyCallableCustom::compare_less_func;
48 | }
49 |
50 | bool is_valid() const
51 | {
52 | return true;
53 | }
54 |
55 | virtual godot::ObjectID get_object() const
56 | {
57 | return godot::ObjectID();
58 | }
59 |
60 | virtual void call( const godot::Variant **inArguments, int inArgcount,
61 | godot::Variant &outReturnValue,
62 | GDExtensionCallError &outCallError ) const
63 | {
64 | UNUSED( inArguments );
65 | UNUSED( inArgcount );
66 |
67 | outReturnValue = "Hi";
68 | outCallError.error = GDEXTENSION_CALL_OK;
69 | }
70 | };
71 | }
72 |
73 | //// ExampleRef
74 |
75 | int ExampleRef::sInstanceCount = 0;
76 | int ExampleRef::sLastID = 0;
77 |
78 | ExampleRef::ExampleRef()
79 | {
80 | mID = ++sLastID;
81 | sInstanceCount++;
82 |
83 | godot::UtilityFunctions::print(
84 | "ExampleRef ", godot::itos( mID ),
85 | " created, current instance count: ", godot::itos( sInstanceCount ) );
86 | }
87 |
88 | ExampleRef::~ExampleRef()
89 | {
90 | sInstanceCount--;
91 | godot::UtilityFunctions::print(
92 | "ExampleRef ", godot::itos( mID ),
93 | " destroyed, current instance count: ", godot::itos( sInstanceCount ) );
94 | }
95 |
96 | void ExampleRef::setId( int inID )
97 | {
98 | mID = inID;
99 | }
100 |
101 | int ExampleRef::getID() const
102 | {
103 | return mID;
104 | }
105 |
106 | void ExampleRef::_bind_methods()
107 | {
108 | godot::ClassDB::bind_method( godot::D_METHOD( "set_id", "id" ), &ExampleRef::setId );
109 | godot::ClassDB::bind_method( godot::D_METHOD( "get_id" ), &ExampleRef::getID );
110 |
111 | godot::ClassDB::bind_method( godot::D_METHOD( "was_post_initialized" ),
112 | &ExampleRef::wasPostInitialized );
113 | }
114 |
115 | void ExampleRef::_notification( int inWhat )
116 | {
117 | if ( inWhat == NOTIFICATION_POSTINITIALIZE )
118 | {
119 | mPostInitialized = true;
120 | }
121 | }
122 |
123 | //// ExampleMin
124 |
125 | void ExampleMin::_bind_methods()
126 | {
127 | }
128 |
129 | //// Example
130 |
131 | Example::Example()
132 | {
133 | godot::UtilityFunctions::print( "Constructor." );
134 | }
135 |
136 | Example::~Example()
137 | {
138 | godot::UtilityFunctions::print( "Destructor." );
139 | }
140 |
141 | // Methods.
142 | void Example::simpleFunc()
143 | {
144 | godot::UtilityFunctions::print( " Simple func called." );
145 | }
146 |
147 | void Example::simpleConstFunc() const
148 | {
149 | godot::UtilityFunctions::print( " Simple const func called." );
150 | }
151 |
152 | godot::String Example::returnSomething( const godot::String &inBase )
153 | {
154 | godot::UtilityFunctions::print( " Return something called." );
155 |
156 | return inBase;
157 | }
158 |
159 | godot::Viewport *Example::returnSomethingConst() const
160 | {
161 | godot::UtilityFunctions::print( " Return something const called." );
162 |
163 | if ( is_inside_tree() )
164 | {
165 | godot::Viewport *result = get_viewport();
166 |
167 | return result;
168 | }
169 |
170 | return nullptr;
171 | }
172 |
173 | godot::Ref Example::returnEmptyRef() const
174 | {
175 | godot::Ref ref;
176 | return ref;
177 | }
178 |
179 | ExampleRef *Example::returnExtendedRef() const
180 | {
181 | // You can instance and return a refcounted object like this, but keep in mind that refcounting
182 | // starts with the returned object and it will be destroyed when all references are destroyed.
183 | // If you store this pointer you run the risk of having a pointer to a destroyed object.
184 | return memnew( ExampleRef() );
185 | }
186 |
187 | godot::Ref Example::extendedRefChecks( godot::Ref inRef ) const
188 | {
189 | // This is the preferred way of instancing and returning a refcounted object:
190 | godot::Ref ref;
191 | ref.instantiate();
192 |
193 | godot::UtilityFunctions::print(
194 | " Example ref checks called with value: ", inRef->get_instance_id(),
195 | ", returning value: ", ref->get_instance_id() );
196 |
197 | return ref;
198 | }
199 |
200 | godot::Variant Example::varargsFunc( const godot::Variant **inArgs, GDExtensionInt inArgCount,
201 | GDExtensionCallError &outError )
202 | {
203 | UNUSED( inArgs );
204 | UNUSED( outError );
205 |
206 | godot::UtilityFunctions::print( " Varargs (Variant return) called with ",
207 | godot::String::num_int64( inArgCount ), " arguments" );
208 |
209 | return inArgCount;
210 | }
211 |
212 | int Example::varargsFuncNonVoidReturn( const godot::Variant **inArgs, GDExtensionInt inArgCount,
213 | GDExtensionCallError &outError )
214 | {
215 | UNUSED( inArgs );
216 | UNUSED( outError );
217 |
218 | godot::UtilityFunctions::print( " Varargs (int return) called with ",
219 | godot::String::num_int64( inArgCount ), " arguments" );
220 |
221 | return MAGIC_NUMBER;
222 | }
223 |
224 | void Example::varargsFuncVoidReturn( const godot::Variant **inArgs, GDExtensionInt inArgCount,
225 | GDExtensionCallError &outError )
226 | {
227 | UNUSED( inArgs );
228 | UNUSED( outError );
229 |
230 | godot::UtilityFunctions::print( " Varargs (no return) called with ",
231 | godot::String::num_int64( inArgCount ), " arguments" );
232 | }
233 |
234 | void Example::emitCustomSignal( const godot::String &inName, int inValue )
235 | {
236 | emit_signal( "custom_signal", inName, inValue );
237 | }
238 |
239 | int Example::defArgs( int inA, int inB ) const
240 | {
241 | return inA + inB;
242 | }
243 |
244 | godot::Array Example::testArray() const
245 | {
246 | godot::Array arr;
247 |
248 | arr.resize( 2 );
249 | arr[0] = godot::Variant( 1 );
250 | arr[1] = godot::Variant( 2 );
251 |
252 | return arr;
253 | }
254 |
255 | void Example::testTypedArrayArg( const godot::TypedArray &inArray )
256 | {
257 | for ( int i = 0; i < inArray.size(); ++i )
258 | {
259 | godot::UtilityFunctions::print( inArray[i] );
260 | }
261 | }
262 |
263 | godot::TypedArray Example::testTypedArray() const
264 | {
265 | godot::TypedArray arr;
266 |
267 | arr.resize( 2 );
268 | arr[0] = godot::Vector2( 1, 2 );
269 | arr[1] = godot::Vector2( 2, 3 );
270 |
271 | return arr;
272 | }
273 |
274 | godot::Dictionary Example::testDictionary() const
275 | {
276 | godot::Dictionary dict;
277 |
278 | dict["hello"] = "world";
279 | dict["foo"] = "bar";
280 |
281 | return dict;
282 | }
283 |
284 | Example *Example::testNodeArgument( Example *inNode ) const
285 | {
286 | // This should use godot::String::num_uint64(), but it is currently broken:
287 | // https://github.com/godotengine/godot-cpp/issues/1014
288 | godot::UtilityFunctions::print(
289 | " Test node argument called with ",
290 | ( inNode != nullptr )
291 | ? godot::String::num_int64( static_cast( inNode->get_instance_id() ) )
292 | : "null" );
293 | return inNode;
294 | }
295 |
296 | godot::String Example::testStringOps() const
297 | {
298 | godot::String s = godot::String( "A" );
299 | s += "B";
300 | s += "C";
301 | s += char32_t( 0x010E );
302 | s = s + "E";
303 |
304 | return s;
305 | }
306 |
307 | godot::String Example::testStrUtility() const
308 | {
309 | return godot::UtilityFunctions::str( "Hello, ", "World", "! The answer is ", 42 );
310 | }
311 |
312 | bool Example::testStringIsFortyTwo( const godot::String &inString ) const
313 | {
314 | return strcmp( inString.utf8().ptr(), "forty two" ) == 0;
315 | }
316 |
317 | godot::String Example::testStringResize( godot::String ioString ) const
318 | {
319 | int64_t orig_len = ioString.length();
320 |
321 | // This cast is to fix an issue with the API.
322 | // See: https://github.com/godotengine/godot-cpp/issues/1338
323 | ioString.resize( static_cast( orig_len ) + 3 );
324 |
325 | char32_t *data = ioString.ptrw();
326 | data[orig_len + 0] = '!';
327 | data[orig_len + 1] = '?';
328 | data[orig_len + 2] = '\0';
329 |
330 | return ioString;
331 | }
332 |
333 | int Example::testVectorOps() const
334 | {
335 | godot::PackedInt32Array arr;
336 | arr.push_back( 10 );
337 | arr.push_back( 20 );
338 | arr.push_back( 30 );
339 | arr.push_back( 45 );
340 |
341 | int ret = 0;
342 | for ( const int32_t &E : arr )
343 | {
344 | ret += E;
345 | }
346 |
347 | return ret;
348 | }
349 |
350 | bool Example::testObjectCastToNode( godot::Object *inObject ) const
351 | {
352 | return godot::Object::cast_to( inObject ) != nullptr;
353 | }
354 |
355 | bool Example::testObjectCastToControl( godot::Object *inObject ) const
356 | {
357 | return godot::Object::cast_to( inObject ) != nullptr;
358 | }
359 |
360 | bool Example::testObjectCastToExample( godot::Object *inObject ) const
361 | {
362 | return godot::Object::cast_to( inObject ) != nullptr;
363 | }
364 |
365 | godot::Vector2i Example::testVariantVector2iConversion( const godot::Variant &inVariant ) const
366 | {
367 | return inVariant;
368 | }
369 |
370 | int Example::testVariantIntConversion( const godot::Variant &inVariant ) const
371 | {
372 | return inVariant;
373 | }
374 |
375 | float Example::testVariantFloatConversion( const godot::Variant &inVariant ) const
376 | {
377 | return inVariant;
378 | }
379 |
380 | godot::Variant Example::testVariantCall( godot::Variant inVariant )
381 | {
382 | return inVariant.call( "test", "hello" );
383 | }
384 |
385 | godot::Variant Example::testVariantIterator( const godot::Variant &inVariant )
386 | {
387 | godot::Array output;
388 | godot::Variant iter;
389 |
390 | bool is_init_valid = true;
391 |
392 | if ( !inVariant.iter_init( iter, is_init_valid ) )
393 | {
394 | if ( !is_init_valid )
395 | {
396 | return "iter_init: not valid";
397 | }
398 |
399 | return output;
400 | }
401 |
402 | bool is_iter_next_valid = true;
403 | bool is_iter_get_valid = true;
404 |
405 | do
406 | {
407 | if ( !is_iter_next_valid )
408 | {
409 | return "iter_next: not valid";
410 | }
411 |
412 | godot::Variant value = inVariant.iter_get( iter, is_iter_get_valid );
413 |
414 | if ( !is_iter_get_valid )
415 | {
416 | return "iter_get: not valid";
417 | }
418 |
419 | output.push_back( ( static_cast( value ) ) + 5 );
420 |
421 | } while ( inVariant.iter_next( iter, is_iter_next_valid ) );
422 |
423 | if ( !is_iter_next_valid )
424 | {
425 | return "iter_next: not valid";
426 | }
427 |
428 | return output;
429 | }
430 |
431 | void Example::testAddChild( godot::Node *inNode )
432 | {
433 | add_child( inNode );
434 | }
435 |
436 | void Example::testSetTileset( godot::TileMap *inTilemap,
437 | const godot::Ref &inTileset ) const
438 | {
439 | inTilemap->set_tileset( inTileset );
440 | }
441 |
442 | godot::Callable Example::testCallableMP()
443 | {
444 | return callable_mp( this, &Example::unboundMethod1 );
445 | }
446 |
447 | godot::Callable Example::testCallableMPRet()
448 | {
449 | return callable_mp( this, &Example::unboundMethod2 );
450 | }
451 |
452 | godot::Callable Example::testCallableMPRetC() const
453 | {
454 | return callable_mp( this, &Example::unboundMethod3 );
455 | }
456 |
457 | godot::Callable Example::testCallableMPStatic() const
458 | {
459 | return callable_mp_static( &Example::unboundStaticMethod1 );
460 | }
461 |
462 | godot::Callable Example::testCallableMPStaticRet() const
463 | {
464 | return callable_mp_static( &Example::unboundStaticMethod2 );
465 | }
466 |
467 | godot::Callable Example::testCustomCallable() const
468 | {
469 | return godot::Callable( memnew( MyCallableCustom ) );
470 | }
471 |
472 | void Example::callableBind()
473 | {
474 | godot::Callable c = godot::Callable( this, "emit_custom_signal" ).bind( "bound", 11 );
475 | c.call();
476 | }
477 |
478 | void Example::unboundMethod1( godot::Object *inObject, godot::String inString, int inInt )
479 | {
480 | godot::String test = "unbound_method1: ";
481 | test += inObject->get_class();
482 | test += " - " + inString;
483 | emitCustomSignal( test, inInt );
484 | }
485 |
486 | godot::String Example::unboundMethod2( godot::Object *inObject, godot::String inString, int inInt )
487 | {
488 | godot::String test = "unbound_method2: ";
489 | test += inObject->get_class();
490 | test += " - " + inString;
491 | test += " - " + godot::itos( inInt );
492 | return test;
493 | }
494 |
495 | godot::String Example::unboundMethod3( godot::Object *inObject, godot::String inString,
496 | int inInt ) const
497 | {
498 | godot::String test = "unbound_method3: ";
499 | test += inObject->get_class();
500 | test += " - " + inString;
501 | test += " - " + godot::itos( inInt );
502 | return test;
503 | }
504 |
505 | void Example::unboundStaticMethod1( Example *inObject, godot::String inString, int inInt )
506 | {
507 | godot::String test = "unbound_static_method1: ";
508 | test += inObject->get_class();
509 | test += " - " + inString;
510 | inObject->emitCustomSignal( test, inInt );
511 | }
512 |
513 | godot::String Example::unboundStaticMethod2( godot::Object *inObject, godot::String inString,
514 | int inInt )
515 | {
516 | godot::String test = "unbound_static_method2: ";
517 | test += inObject->get_class();
518 | test += " - " + inString;
519 | test += " - " + godot::itos( inInt );
520 | return test;
521 | }
522 |
523 | godot::BitField Example::testBitfield( godot::BitField inFlags )
524 | {
525 | godot::UtilityFunctions::print( " Got BitField: ", godot::String::num_int64( inFlags ) );
526 | return inFlags;
527 | }
528 |
529 | // RPC
530 | void Example::testRPC( int inValue )
531 | {
532 | mLastRPCArg = inValue;
533 | }
534 |
535 | void Example::testSendRPC( int inValue )
536 | {
537 | rpc( "test_rpc", inValue );
538 | }
539 |
540 | int Example::returnLastRPCArg()
541 | {
542 | return mLastRPCArg;
543 | }
544 |
545 | // Properties
546 | void Example::setCustomPosition( const godot::Vector2 &inPos )
547 | {
548 | mCustomPosition = inPos;
549 | }
550 |
551 | godot::Vector2 Example::getCustomPosition() const
552 | {
553 | return mCustomPosition;
554 | }
555 |
556 | godot::Vector4 Example::getV4() const
557 | {
558 | return { 1.2f, 3.4f, 5.6f, 7.8f };
559 | }
560 |
561 | bool Example::testPostInitialize() const
562 | {
563 | godot::Ref new_example_ref;
564 |
565 | new_example_ref.instantiate();
566 |
567 | return new_example_ref->wasPostInitialized();
568 | }
569 |
570 | // Static methods
571 | int Example::testStatic( int inA, int inB )
572 | {
573 | return inA + inB;
574 | }
575 |
576 | void Example::testStatic2()
577 | {
578 | godot::UtilityFunctions::print( " void static" );
579 | }
580 |
581 | // Virtual function override.
582 | bool Example::_has_point( const godot::Vector2 &inPoint ) const
583 | {
584 | auto *label = godot::Control::get_node( "Label" );
585 |
586 | label->set_text( "Got point: " + godot::Variant( inPoint ).stringify() );
587 |
588 | return false;
589 | }
590 |
591 | void Example::_bind_methods()
592 | {
593 | // Methods.
594 | godot::ClassDB::bind_method( godot::D_METHOD( "simple_func" ), &Example::simpleFunc );
595 | godot::ClassDB::bind_method( godot::D_METHOD( "simple_const_func" ),
596 | &Example::simpleConstFunc );
597 | godot::ClassDB::bind_method( godot::D_METHOD( "return_something" ), &Example::returnSomething );
598 | godot::ClassDB::bind_method( godot::D_METHOD( "return_something_const" ),
599 | &Example::returnSomethingConst );
600 | godot::ClassDB::bind_method( godot::D_METHOD( "return_empty_ref" ), &Example::returnEmptyRef );
601 | godot::ClassDB::bind_method( godot::D_METHOD( "return_extended_ref" ),
602 | &Example::returnExtendedRef );
603 | godot::ClassDB::bind_method( godot::D_METHOD( "extended_ref_checks", "ref" ),
604 | &Example::extendedRefChecks );
605 |
606 | godot::ClassDB::bind_method( godot::D_METHOD( "test_array" ), &Example::testArray );
607 | godot::ClassDB::bind_method( godot::D_METHOD( "test_tarray_arg", "array" ),
608 | &Example::testTypedArrayArg );
609 | godot::ClassDB::bind_method( godot::D_METHOD( "test_tarray" ), &Example::testTypedArray );
610 | godot::ClassDB::bind_method( godot::D_METHOD( "test_dictionary" ), &Example::testDictionary );
611 | godot::ClassDB::bind_method( godot::D_METHOD( "test_node_argument" ),
612 | &Example::testNodeArgument );
613 |
614 | godot::ClassDB::bind_method( godot::D_METHOD( "test_string_ops" ), &Example::testStringOps );
615 | godot::ClassDB::bind_method( godot::D_METHOD( "test_str_utility" ), &Example::testStrUtility );
616 | godot::ClassDB::bind_method( godot::D_METHOD( "test_string_is_forty_two" ),
617 | &Example::testStringIsFortyTwo );
618 | godot::ClassDB::bind_method( godot::D_METHOD( "test_string_resize" ),
619 | &Example::testStringResize );
620 |
621 | godot::ClassDB::bind_method( godot::D_METHOD( "test_vector_ops" ), &Example::testVectorOps );
622 |
623 | godot::ClassDB::bind_method( godot::D_METHOD( "test_object_cast_to_node", "object" ),
624 | &Example::testObjectCastToNode );
625 | godot::ClassDB::bind_method( godot::D_METHOD( "test_object_cast_to_control", "object" ),
626 | &Example::testObjectCastToControl );
627 | godot::ClassDB::bind_method( godot::D_METHOD( "test_object_cast_to_example", "object" ),
628 | &Example::testObjectCastToExample );
629 |
630 | godot::ClassDB::bind_method( godot::D_METHOD( "test_variant_vector2i_conversion", "variant" ),
631 | &Example::testVariantVector2iConversion );
632 | godot::ClassDB::bind_method( godot::D_METHOD( "test_variant_int_conversion", "variant" ),
633 | &Example::testVariantIntConversion );
634 | godot::ClassDB::bind_method( godot::D_METHOD( "test_variant_float_conversion", "variant" ),
635 | &Example::testVariantFloatConversion );
636 | godot::ClassDB::bind_method( godot::D_METHOD( "test_variant_call", "variant" ),
637 | &Example::testVariantCall );
638 | godot::ClassDB::bind_method( godot::D_METHOD( "test_variant_iterator", "input" ),
639 | &Example::testVariantIterator );
640 |
641 | godot::ClassDB::bind_method( godot::D_METHOD( "test_add_child", "node" ),
642 | &Example::testAddChild );
643 | godot::ClassDB::bind_method( godot::D_METHOD( "test_set_tileset", "tilemap", "tileset" ),
644 | &Example::testSetTileset );
645 |
646 | godot::ClassDB::bind_method( godot::D_METHOD( "test_callable_mp" ), &Example::testCallableMP );
647 | godot::ClassDB::bind_method( godot::D_METHOD( "test_callable_mp_ret" ),
648 | &Example::testCallableMPRet );
649 | godot::ClassDB::bind_method( godot::D_METHOD( "test_callable_mp_retc" ),
650 | &Example::testCallableMPRetC );
651 | godot::ClassDB::bind_method( godot::D_METHOD( "test_callable_mp_static" ),
652 | &Example::testCallableMPStatic );
653 | godot::ClassDB::bind_method( godot::D_METHOD( "test_callable_mp_static_ret" ),
654 | &Example::testCallableMPStaticRet );
655 | godot::ClassDB::bind_method( godot::D_METHOD( "test_custom_callable" ),
656 | &Example::testCustomCallable );
657 |
658 | godot::ClassDB::bind_method( godot::D_METHOD( "test_bitfield", "flags" ),
659 | &Example::testBitfield );
660 |
661 | godot::ClassDB::bind_method( godot::D_METHOD( "test_rpc", "value" ), &Example::testRPC );
662 | godot::ClassDB::bind_method( godot::D_METHOD( "test_send_rpc", "value" ),
663 | &Example::testSendRPC );
664 | godot::ClassDB::bind_method( godot::D_METHOD( "return_last_rpc_arg" ),
665 | &Example::returnLastRPCArg );
666 |
667 | godot::ClassDB::bind_method( godot::D_METHOD( "def_args", "a", "b" ), &Example::defArgs,
668 | DEFVAL( 100 ), DEFVAL( 200 ) );
669 | godot::ClassDB::bind_method( godot::D_METHOD( "callable_bind" ), &Example::callableBind );
670 | godot::ClassDB::bind_method( godot::D_METHOD( "test_post_initialize" ),
671 | &Example::testPostInitialize );
672 |
673 | godot::ClassDB::bind_static_method( "Example", godot::D_METHOD( "test_static", "a", "b" ),
674 | &Example::testStatic );
675 | godot::ClassDB::bind_static_method( "Example", godot::D_METHOD( "test_static2" ),
676 | &Example::testStatic2 );
677 |
678 | {
679 | godot::MethodInfo mi;
680 | mi.arguments.emplace_back( godot::Variant::STRING, "some_argument" );
681 | mi.name = "varargs_func";
682 |
683 | godot::ClassDB::bind_vararg_method( godot::METHOD_FLAGS_DEFAULT, "varargs_func",
684 | &Example::varargsFunc, mi );
685 | }
686 |
687 | {
688 | godot::MethodInfo mi;
689 | mi.arguments.emplace_back( godot::Variant::STRING, "some_argument" );
690 | mi.name = "varargs_func_nv";
691 |
692 | godot::ClassDB::bind_vararg_method( godot::METHOD_FLAGS_DEFAULT, "varargs_func_nv",
693 | &Example::varargsFuncNonVoidReturn, mi );
694 | }
695 |
696 | {
697 | godot::MethodInfo mi;
698 | mi.arguments.emplace_back( godot::Variant::STRING, "some_argument" );
699 | mi.name = "varargs_func_void";
700 |
701 | godot::ClassDB::bind_vararg_method( godot::METHOD_FLAGS_DEFAULT, "varargs_func_void",
702 | &Example::varargsFuncVoidReturn, mi );
703 | }
704 |
705 | // Properties.
706 | ADD_GROUP( "Test group", "group_" );
707 | ADD_SUBGROUP( "Test subgroup", "group_subgroup_" );
708 |
709 | godot::ClassDB::bind_method( godot::D_METHOD( "get_custom_position" ),
710 | &Example::getCustomPosition );
711 | godot::ClassDB::bind_method( godot::D_METHOD( "get_v4" ), &Example::getV4 );
712 | godot::ClassDB::bind_method( godot::D_METHOD( "set_custom_position", "position" ),
713 | &Example::setCustomPosition );
714 | ADD_PROPERTY( godot::PropertyInfo( godot::Variant::VECTOR2, "group_subgroup_custom_position" ),
715 | "set_custom_position", "get_custom_position" );
716 |
717 | // Signals.
718 | ADD_SIGNAL( godot::MethodInfo( "custom_signal",
719 | godot::PropertyInfo( godot::Variant::STRING, "name" ),
720 | godot::PropertyInfo( godot::Variant::INT, "value" ) ) );
721 | godot::ClassDB::bind_method( godot::D_METHOD( "emit_custom_signal", "name", "value" ),
722 | &Example::emitCustomSignal );
723 |
724 | // Constants.
725 | BIND_ENUM_CONSTANT( FIRST )
726 | BIND_ENUM_CONSTANT( ANSWER_TO_EVERYTHING )
727 |
728 | BIND_BITFIELD_FLAG( FLAG_ONE );
729 | BIND_BITFIELD_FLAG( FLAG_TWO );
730 |
731 | BIND_CONSTANT( CONSTANT_WITHOUT_ENUM );
732 | BIND_ENUM_CONSTANT( OUTSIDE_OF_CLASS );
733 | }
734 |
735 | void Example::_notification( int inWhat )
736 | {
737 | godot::UtilityFunctions::print( "Notification: ", godot::String::num( inWhat ) );
738 | }
739 |
740 | bool Example::_set( const godot::StringName &inName, const godot::Variant &inValue )
741 | {
742 | godot::String name = inName;
743 |
744 | if ( name.begins_with( "dproperty" ) )
745 | {
746 | int64_t index = name.get_slicec( '_', 1 ).to_int();
747 | mDProp[index] = inValue;
748 |
749 | return true;
750 | }
751 |
752 | if ( name == "property_from_list" )
753 | {
754 | mPropertyFromList = inValue;
755 |
756 | return true;
757 | }
758 |
759 | return false;
760 | }
761 |
762 | bool Example::_get( const godot::StringName &inName, godot::Variant &outReturn ) const
763 | {
764 | godot::String name = inName;
765 |
766 | if ( name.begins_with( "dproperty" ) )
767 | {
768 | int64_t index = name.get_slicec( '_', 1 ).to_int();
769 | outReturn = mDProp[index];
770 |
771 | return true;
772 | }
773 |
774 | if ( name == "property_from_list" )
775 | {
776 | outReturn = mPropertyFromList;
777 |
778 | return true;
779 | }
780 |
781 | return false;
782 | }
783 |
784 | void Example::_get_property_list( godot::List *outList ) const
785 | {
786 | outList->push_back( godot::PropertyInfo( godot::Variant::VECTOR3, "property_from_list" ) );
787 |
788 | for ( int i = 0; i < 3; ++i )
789 | {
790 | outList->push_back(
791 | godot::PropertyInfo( godot::Variant::VECTOR2, "dproperty_" + godot::itos( i ) ) );
792 | }
793 | }
794 |
795 | bool Example::_property_can_revert( const godot::StringName &inName ) const
796 | {
797 | if ( inName == godot::StringName( "property_from_list" ) &&
798 | mPropertyFromList != godot::Vector3( MAGIC_NUMBER, MAGIC_NUMBER, MAGIC_NUMBER ) )
799 | {
800 | return true;
801 | }
802 |
803 | return false;
804 | };
805 |
806 | bool Example::_property_get_revert( const godot::StringName &inName,
807 | godot::Variant &outProperty ) const
808 | {
809 | if ( inName == godot::StringName( "property_from_list" ) )
810 | {
811 | outProperty = godot::Vector3( MAGIC_NUMBER, MAGIC_NUMBER, MAGIC_NUMBER );
812 |
813 | return true;
814 | }
815 |
816 | return false;
817 | };
818 |
819 | void Example::_validate_property( godot::PropertyInfo &inProperty ) const
820 | {
821 | godot::String name = inProperty.name;
822 |
823 | // Test hiding the "mouse_filter" property from the editor.
824 | if ( name == "mouse_filter" )
825 | {
826 | inProperty.usage = godot::PROPERTY_USAGE_NO_EDITOR;
827 | }
828 | }
829 |
830 | godot::String Example::_to_string() const
831 | {
832 | return "[ GDExtension::Example <--> Instance ID:" + godot::uitos( get_instance_id() ) + " ]";
833 | }
834 |
835 | //// ExampleVirtual
836 |
837 | void ExampleVirtual::_bind_methods()
838 | {
839 | }
840 |
841 | //// ExampleAbstract
842 |
843 | void ExampleAbstract::_bind_methods()
844 | {
845 | }
846 |
--------------------------------------------------------------------------------