diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4665ed94a..6a9db4347 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,6 +109,11 @@ jobs: run: | scons platform=${{ matrix.platform }} target=template_debug ${{ matrix.flags }} + - name: Build example without rebuilding godot-cpp (debug) + run: | + cd example + scons platform=${{ matrix.platform }} target=template_debug ${{ matrix.flags }} build_library=no + - name: Build test without rebuilding godot-cpp (debug) run: | cd test diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 000000000..c6d839430 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,25 @@ +# Generated directories with binaries +build +bin + +# Godot 4+ specific ignores +.godot/ + +# Godot-specific ignores +.import/ +export.cfg +export_presets.cfg +# Dummy HTML5 export presets file for continuous integration +!.github/dist/export_presets.cfg + +# Imported translations (automatically generated from CSV files) +*.translation + +# Mono-specific ignores +.mono/ +data_*/ +mono_crash.*.json + +# System/tool-specific ignores +.directory +*~ diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 000000000..f229253ed --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,150 @@ +project(godot-cpp-test) +cmake_minimum_required(VERSION 3.6) + +set(GODOT_HEADERS_PATH ../godot-headers/ CACHE STRING "Path to Godot headers") +set(CPP_BINDINGS_PATH ../ CACHE STRING "Path to C++ bindings") + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(TARGET_PATH x11) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(TARGET_PATH win64) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(TARGET_PATH macos) +else() + message(FATAL_ERROR "Not implemented support for ${CMAKE_SYSTEM_NAME}") +endif() + +# Change the output directory to the bin directory +set(BUILD_PATH ${CMAKE_SOURCE_DIR}/bin/${TARGET_PATH}) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${BUILD_PATH}") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${BUILD_PATH}") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${BUILD_PATH}") +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${BUILD_PATH}") +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${BUILD_PATH}") +SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${BUILD_PATH}") +SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${BUILD_PATH}") +SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${BUILD_PATH}") +SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${BUILD_PATH}") + +# Set the c++ standard to c++17 +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(GODOT_COMPILE_FLAGS ) +set(GODOT_LINKER_FLAGS ) + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + # using Visual Studio C++ + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /EHsc /WX") # /GF /MP + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /DTYPED_METHOD_BIND") + + if(CMAKE_BUILD_TYPE MATCHES Debug) + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /MDd") # /Od /RTC1 /Zi + else() + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /MD /O2") # /Oy /GL /Gy + STRING(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) + endif(CMAKE_BUILD_TYPE MATCHES Debug) + + # Disable conversion warning, truncation, unreferenced var, signed mismatch + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /wd4244 /wd4305 /wd4101 /wd4018 /wd4267") + + add_definitions(-DNOMINMAX) + + # Unkomment for warning level 4 + #if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + # string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + #endif() + +else() + +#elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # using Clang +#elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + # using GCC and maybe MinGW? + + set(GODOT_LINKER_FLAGS "-static-libgcc -static-libstdc++ -Wl,-R,'$$ORIGIN'") + + # Hmm.. maybe to strikt? + set(GODOT_COMPILE_FLAGS "-fPIC -g -Wwrite-strings") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wchar-subscripts -Wcomment -Wdisabled-optimization") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wformat -Wformat=2 -Wformat-security -Wformat-y2k") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wimport -Winit-self -Winline -Winvalid-pch -Werror") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wmissing-braces -Wmissing-format-attribute") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wmissing-include-dirs -Wmissing-noreturn -Wpacked -Wpointer-arith") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wredundant-decls -Wreturn-type -Wsequence-point") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wswitch -Wswitch-enum -Wtrigraphs") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wuninitialized -Wunknown-pragmas -Wunreachable-code -Wunused-label") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wunused-value -Wvariadic-macros -Wvolatile-register-var -Wno-error=attributes") + + # -Wshadow -Wextra -Wall -Weffc++ -Wfloat-equal -Wstack-protector -Wunused-parameter -Wsign-compare -Wunused-variable -Wcast-align + # -Wunused-function -Wstrict-aliasing -Wstrict-aliasing=2 -Wmissing-field-initializers + + if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wno-ignored-attributes") + endif() + + if(CMAKE_BUILD_TYPE MATCHES Debug) + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0") + else() + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -O3") + endif(CMAKE_BUILD_TYPE MATCHES Debug) +endif() + +# Get Sources +file(GLOB_RECURSE SOURCES src/*.c**) +file(GLOB_RECURSE HEADERS include/*.h**) + +# Define our godot-cpp library +add_library(${PROJECT_NAME} SHARED ${SOURCES} ${HEADERS}) + +target_include_directories(${PROJECT_NAME} SYSTEM + PRIVATE + ${CPP_BINDINGS_PATH}/include + ${CPP_BINDINGS_PATH}/gen/include + ${GODOT_HEADERS_PATH} +) + +# Create the correct name (godot.os.build_type.system_bits) +# Synchronized with godot-cpp's CMakeLists.txt + +set(BITS 32) +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(BITS 64) +endif(CMAKE_SIZEOF_VOID_P EQUAL 8) + +if(CMAKE_BUILD_TYPE MATCHES Debug) + set(GODOT_CPP_BUILD_TYPE Debug) +else() + set(GODOT_CPP_BUILD_TYPE Release) +endif() + +string(TOLOWER ${CMAKE_SYSTEM_NAME} SYSTEM_NAME) +string(TOLOWER ${GODOT_CPP_BUILD_TYPE} BUILD_TYPE) + +if(ANDROID) + # Added the android abi after system name + set(SYSTEM_NAME ${SYSTEM_NAME}.${ANDROID_ABI}) +endif() + +if(CMAKE_VERSION VERSION_GREATER "3.13") + target_link_directories(${PROJECT_NAME} + PRIVATE + ${CPP_BINDINGS_PATH}/bin/ + ) + + target_link_libraries(${PROJECT_NAME} + godot-cpp.${SYSTEM_NAME}.${BUILD_TYPE}$<$>:.${BITS}> + ) +else() + target_link_libraries(${PROJECT_NAME} + ${CPP_BINDINGS_PATH}/bin/libgodot-cpp.${SYSTEM_NAME}.${BUILD_TYPE}$<$>:.${BITS}>.a + ) +endif() + +# Add the compile flags +set_property(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY COMPILE_FLAGS ${GODOT_COMPILE_FLAGS}) +set_property(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY LINK_FLAGS ${GODOT_LINKER_FLAGS}) + +set_property(TARGET ${PROJECT_NAME} PROPERTY OUTPUT_NAME "gdexample") diff --git a/example/README.md b/example/README.md new file mode 100644 index 000000000..ac8554bc1 --- /dev/null +++ b/example/README.md @@ -0,0 +1,34 @@ +# godot-cpp example / integration test + +This project is used to perform integration testing of the godot-cpp +extension, to validate PRs and implemented APIs. + +It can also be used as a quick example of how to set up a godot-cpp +project, both on the C++ side and in the Godot project itself. + +## License + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/example/SConstruct b/example/SConstruct new file mode 100644 index 000000000..72527bb64 --- /dev/null +++ b/example/SConstruct @@ -0,0 +1,32 @@ +#!/usr/bin/env python +import os +import sys + +env = SConscript("../SConstruct") + +# For the reference: +# - CCFLAGS are compilation flags shared between C and C++ +# - CFLAGS are for C-specific compilation flags +# - CXXFLAGS are for C++-specific compilation flags +# - CPPFLAGS are for pre-processor flags +# - CPPDEFINES are for pre-processor defines +# - LINKFLAGS are for linking flags + +# tweak this if you want to use different folders, or more folders, to store your source code in. +env.Append(CPPPATH=["src/"]) +sources = Glob("src/*.cpp") + +if env["platform"] == "macos": + library = env.SharedLibrary( + "demo/bin/libgdexample.{}.{}.framework/libgdexample.{}.{}".format( + env["platform"], env["target"], env["platform"], env["target"] + ), + source=sources, + ) +else: + library = env.SharedLibrary( + "demo/bin/libgdexample{}{}".format(env["suffix"], env["SHLIBSUFFIX"]), + source=sources, + ) + +Default(library) diff --git a/test/demo/bin/libgdexample.osx.template_debug.framework/Resources/Info.plist b/example/demo/bin/libgdexample.osx.template_debug.framework/Resources/Info.plist similarity index 100% rename from test/demo/bin/libgdexample.osx.template_debug.framework/Resources/Info.plist rename to example/demo/bin/libgdexample.osx.template_debug.framework/Resources/Info.plist diff --git a/test/demo/bin/libgdexample.osx.template_release.framework/Resources/Info.plist b/example/demo/bin/libgdexample.osx.template_release.framework/Resources/Info.plist similarity index 100% rename from test/demo/bin/libgdexample.osx.template_release.framework/Resources/Info.plist rename to example/demo/bin/libgdexample.osx.template_release.framework/Resources/Info.plist diff --git a/example/demo/default_env.tres b/example/demo/default_env.tres new file mode 100644 index 000000000..770cd8537 --- /dev/null +++ b/example/demo/default_env.tres @@ -0,0 +1,7 @@ +[gd_resource type="Environment" load_steps=2 format=3 uid="uid://dtd3q2x2ulcsi"] + +[sub_resource type="Sky" id="1"] + +[resource] +background_mode = 2 +sky = SubResource( "1" ) diff --git a/example/demo/example.gdextension b/example/demo/example.gdextension new file mode 100644 index 000000000..6d5340567 --- /dev/null +++ b/example/demo/example.gdextension @@ -0,0 +1,22 @@ +[configuration] + +entry_symbol = "example_library_init" + +[libraries] + +macos.debug = "res://bin/libgdexample.macos.template_debug.framework" +macos.release = "res://bin/libgdexample.macos.template_release.framework" +windows.debug.x86_32 = "res://bin/libgdexample.windows.template_debug.x86_32.dll" +windows.release.x86_32 = "res://bin/libgdexample.windows.template_release.x86_32.dll" +windows.debug.x86_64 = "res://bin/libgdexample.windows.template_debug.x86_64.dll" +windows.release.x86_64 = "res://bin/libgdexample.windows.template_release.x86_64.dll" +linux.debug.x86_64 = "res://bin/libgdexample.linux.template_debug.x86_64.so" +linux.release.x86_64 = "res://bin/libgdexample.linux.template_release.x86_64.so" +linux.debug.arm64 = "res://bin/libgdexample.linux.template_debug.arm64.so" +linux.release.arm64 = "res://bin/libgdexample.linux.template_release.arm64.so" +linux.debug.rv64 = "res://bin/libgdexample.linux.template_debug.rv64.so" +linux.release.rv64 = "res://bin/libgdexample.linux.template_release.rv64.so" +android.debug.x86_64 = "res://bin/libgdexample.android.template_debug.x86_64.so" +android.release.x86_64 = "res://bin/libgdexample.android.template_release.x86_64.so" +android.debug.arm64 = "res://bin/libgdexample.android.template_debug.arm64.so" +android.release.arm64 = "res://bin/libgdexample.android.template_release.arm64.so" diff --git a/example/demo/icon.png b/example/demo/icon.png new file mode 100644 index 000000000..c98fbb601 Binary files /dev/null and b/example/demo/icon.png differ diff --git a/example/demo/icon.png.import b/example/demo/icon.png.import new file mode 100644 index 000000000..36d7be279 --- /dev/null +++ b/example/demo/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cswr8vy4lt7dt" +path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.png" +dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/bptc_ldr=0 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/example/demo/main.gd b/example/demo/main.gd new file mode 100644 index 000000000..7d36ef1ff --- /dev/null +++ b/example/demo/main.gd @@ -0,0 +1,64 @@ +extends Node + +func _ready(): + # Bind signals + prints("Signal bind") + $Button.button_up.connect($Example.emit_custom_signal.bind("Button", 42)) + + prints("") + + # To string. + prints("To string") + prints(" Example --> ", $Example.to_string()) + prints(" ExampleMin --> ", $Example/ExampleMin.to_string()) + + # Call static methods. + prints("Static method calls") + prints(" static (109)", Example.test_static(9, 100)); + Example.test_static2(); + + # Property list. + prints("Property list") + $Example.property_from_list = Vector3(100, 200, 300) + prints(" property value ", $Example.property_from_list) + + # Call methods. + prints("Instance method calls") + $Example.simple_func() + ($Example as Example).simple_const_func() # Force use of ptrcall + prints(" returned", $Example.return_something("some string")) + prints(" returned const", $Example.return_something_const()) + prints(" returned ref", $Example.return_extended_ref()) + prints(" returned ", $Example.get_v4()) + + prints("VarArg method calls") + var ref = ExampleRef.new() + prints(" sending ref: ", ref.get_instance_id(), "returned ref: ", $Example.extended_ref_checks(ref).get_instance_id()) + prints(" vararg args", $Example.varargs_func("some", "arguments", "to", "test")) + prints(" vararg_nv ret", $Example.varargs_func_nv("some", "arguments", "to", "test")) + $Example.varargs_func_void("some", "arguments", "to", "test") + + prints("Method calls with default values") + prints(" defval (300)", $Example.def_args()) + prints(" defval (250)", $Example.def_args(50)) + prints(" defval (150)", $Example.def_args(50, 100)) + + prints("Array and Dictionary") + prints(" test array", $Example.test_array()) + prints(" test tarray", $Example.test_tarray()) + prints(" test dictionary", $Example.test_dictionary()) + var array: Array[int] = [1, 2, 3] + $Example.test_tarray_arg(array) + + prints("Properties") + prints(" custom position is", $Example.group_subgroup_custom_position) + $Example.group_subgroup_custom_position = Vector2(50, 50) + prints(" custom position now is", $Example.group_subgroup_custom_position) + + prints("Constnts") + prints(" FIRST", $Example.FIRST) + prints(" ANSWER_TO_EVERYTHING", $Example.ANSWER_TO_EVERYTHING) + prints(" CONSTANT_WITHOUT_ENUM", $Example.CONSTANT_WITHOUT_ENUM) + +func _on_Example_custom_signal(signal_name, value): + prints("Example emitted:", signal_name, value) diff --git a/example/demo/main.tscn b/example/demo/main.tscn new file mode 100644 index 000000000..b2a1eaea1 --- /dev/null +++ b/example/demo/main.tscn @@ -0,0 +1,23 @@ +[gd_scene load_steps=2 format=3 uid="uid://dmx2xuigcpvt4"] + +[ext_resource type="Script" path="res://main.gd" id="1_c326s"] + +[node name="Node" type="Node"] +script = ExtResource( "1_c326s" ) + +[node name="Example" type="Example" parent="."] + +[node name="ExampleMin" type="ExampleMin" parent="Example"] + +[node name="Label" type="Label" parent="Example"] +offset_left = 194.0 +offset_top = -2.0 +offset_right = 234.0 +offset_bottom = 21.0 + +[node name="Button" type="Button" parent="."] +offset_right = 79.0 +offset_bottom = 29.0 +text = "Click me!" + +[connection signal="custom_signal" from="Example" to="." method="_on_Example_custom_signal"] diff --git a/example/demo/project.godot b/example/demo/project.godot new file mode 100644 index 000000000..cd174d2fc --- /dev/null +++ b/example/demo/project.godot @@ -0,0 +1,24 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="GDExtension Test Project" +run/main_scene="res://main.tscn" +config/features=PackedStringArray("4.0") +config/icon="res://icon.png" + +[native_extensions] + +paths=["res://example.gdextension"] + +[rendering] + +environment/defaults/default_environment="res://default_env.tres" diff --git a/example/src/example.cpp b/example/src/example.cpp new file mode 100644 index 000000000..890319d20 --- /dev/null +++ b/example/src/example.cpp @@ -0,0 +1,272 @@ +/* godot-cpp integration testing project. + * + * This is free and unencumbered software released into the public domain. + */ + +#include "example.h" + +#include + +#include +#include +#include + +using namespace godot; + +ExampleRef::ExampleRef() { + UtilityFunctions::print("ExampleRef created."); +} + +ExampleRef::~ExampleRef() { + UtilityFunctions::print("ExampleRef destroyed."); +} + +int Example::test_static(int p_a, int p_b) { + return p_a + p_b; +} + +void Example::test_static2() { + UtilityFunctions::print(" void static"); +} + +int Example::def_args(int p_a, int p_b) { + return p_a + p_b; +} + +void Example::_notification(int p_what) { + UtilityFunctions::print("Notification: ", String::num(p_what)); +} + +bool Example::_set(const StringName &p_name, const Variant &p_value) { + String name = p_name; + if (name.begins_with("dproperty")) { + int index = name.get_slicec('_', 1).to_int(); + dprop[index] = p_value; + return true; + } + if (name == "property_from_list") { + property_from_list = p_value; + return true; + } + return false; +} + +bool Example::_get(const StringName &p_name, Variant &r_ret) const { + String name = p_name; + if (name.begins_with("dproperty")) { + int index = name.get_slicec('_', 1).to_int(); + r_ret = dprop[index]; + return true; + } + if (name == "property_from_list") { + r_ret = property_from_list; + return true; + } + return false; +} + +String Example::_to_string() const { + return "[ GDExtension::Example <--> Instance ID:" + itos(get_instance_id()) + " ]"; +} + +void Example::_get_property_list(List *p_list) const { + p_list->push_back(PropertyInfo(Variant::VECTOR3, "property_from_list")); + for (int i = 0; i < 3; i++) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, "dproperty_" + itos(i))); + } +} + +bool Example::_property_can_revert(const StringName &p_name) const { + if (p_name == StringName("property_from_list") && property_from_list != Vector3(42, 42, 42)) { + return true; + } else { + return false; + } +}; + +bool Example::_property_get_revert(const StringName &p_name, Variant &r_property) const { + if (p_name == StringName("property_from_list")) { + r_property = Vector3(42, 42, 42); + return true; + } else { + return false; + } +}; + +void Example::_bind_methods() { + // Methods. + ClassDB::bind_method(D_METHOD("simple_func"), &Example::simple_func); + ClassDB::bind_method(D_METHOD("simple_const_func"), &Example::simple_const_func); + ClassDB::bind_method(D_METHOD("return_something"), &Example::return_something); + ClassDB::bind_method(D_METHOD("return_something_const"), &Example::return_something_const); + ClassDB::bind_method(D_METHOD("return_extended_ref"), &Example::return_extended_ref); + ClassDB::bind_method(D_METHOD("extended_ref_checks"), &Example::extended_ref_checks); + + ClassDB::bind_method(D_METHOD("test_array"), &Example::test_array); + ClassDB::bind_method(D_METHOD("test_tarray_arg", "array"), &Example::test_tarray_arg); + ClassDB::bind_method(D_METHOD("test_tarray"), &Example::test_tarray); + ClassDB::bind_method(D_METHOD("test_dictionary"), &Example::test_dictionary); + + ClassDB::bind_method(D_METHOD("def_args", "a", "b"), &Example::def_args, DEFVAL(100), DEFVAL(200)); + + ClassDB::bind_static_method("Example", D_METHOD("test_static", "a", "b"), &Example::test_static); + ClassDB::bind_static_method("Example", D_METHOD("test_static2"), &Example::test_static2); + + { + MethodInfo mi; + mi.arguments.push_back(PropertyInfo(Variant::STRING, "some_argument")); + mi.name = "varargs_func"; + ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "varargs_func", &Example::varargs_func, mi); + } + + { + MethodInfo mi; + mi.arguments.push_back(PropertyInfo(Variant::STRING, "some_argument")); + mi.name = "varargs_func_nv"; + ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "varargs_func_nv", &Example::varargs_func_nv, mi); + } + + { + MethodInfo mi; + mi.arguments.push_back(PropertyInfo(Variant::STRING, "some_argument")); + mi.name = "varargs_func_void"; + ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "varargs_func_void", &Example::varargs_func_void, mi); + } + + // Properties. + ADD_GROUP("Test group", "group_"); + ADD_SUBGROUP("Test subgroup", "group_subgroup_"); + + ClassDB::bind_method(D_METHOD("get_custom_position"), &Example::get_custom_position); + ClassDB::bind_method(D_METHOD("get_v4"), &Example::get_v4); + ClassDB::bind_method(D_METHOD("set_custom_position", "position"), &Example::set_custom_position); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "group_subgroup_custom_position"), "set_custom_position", "get_custom_position"); + + // Signals. + ADD_SIGNAL(MethodInfo("custom_signal", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::INT, "value"))); + ClassDB::bind_method(D_METHOD("emit_custom_signal", "name", "value"), &Example::emit_custom_signal); + + // Constants. + BIND_ENUM_CONSTANT(FIRST); + BIND_ENUM_CONSTANT(ANSWER_TO_EVERYTHING); + + BIND_CONSTANT(CONSTANT_WITHOUT_ENUM); +} + +Example::Example() { + UtilityFunctions::print("Constructor."); +} + +Example::~Example() { + UtilityFunctions::print("Destructor."); +} + +// Methods. +void Example::simple_func() { + UtilityFunctions::print(" Simple func called."); +} + +void Example::simple_const_func() const { + UtilityFunctions::print(" Simple const func called."); +} + +String Example::return_something(const String &base) { + UtilityFunctions::print(" Return something called."); + return base; +} + +Viewport *Example::return_something_const() const { + UtilityFunctions::print(" Return something const called."); + if (is_inside_tree()) { + Viewport *result = get_viewport(); + return result; + } + return nullptr; +} + +ExampleRef *Example::return_extended_ref() const { + return memnew(ExampleRef()); +} + +Ref Example::extended_ref_checks(Ref p_ref) const { + Ref ref; + ref.instantiate(); + // TODO the returned value gets dereferenced too early and return a null object otherwise. + ref->reference(); + UtilityFunctions::print(" Example ref checks called with value: ", p_ref->get_instance_id(), ", returning value: ", ref->get_instance_id()); + return ref; +} + +Variant Example::varargs_func(const Variant **args, GDNativeInt arg_count, GDNativeCallError &error) { + UtilityFunctions::print(" Varargs (Variant return) called with ", String::num((double)arg_count), " arguments"); + return arg_count; +} + +int Example::varargs_func_nv(const Variant **args, GDNativeInt arg_count, GDNativeCallError &error) { + UtilityFunctions::print(" Varargs (int return) called with ", String::num((double)arg_count), " arguments"); + return 42; +} + +void Example::varargs_func_void(const Variant **args, GDNativeInt arg_count, GDNativeCallError &error) { + UtilityFunctions::print(" Varargs (no return) called with ", String::num((double)arg_count), " arguments"); +} + +void Example::emit_custom_signal(const String &name, int value) { + emit_signal("custom_signal", name, value); +} + +Array Example::test_array() const { + Array arr; + + arr.resize(2); + arr[0] = Variant(1); + arr[1] = Variant(2); + + return arr; +} + +void Example::test_tarray_arg(const TypedArray &p_array) { + for (int i = 0; i < p_array.size(); i++) { + UtilityFunctions::print(p_array[i]); + } +} + +TypedArray Example::test_tarray() const { + TypedArray arr; + + arr.resize(2); + arr[0] = Vector2(1, 2); + arr[1] = Vector2(2, 3); + + return arr; +} + +Dictionary Example::test_dictionary() const { + Dictionary dict; + + dict["hello"] = "world"; + dict["foo"] = "bar"; + + return dict; +} + +// Properties. +void Example::set_custom_position(const Vector2 &pos) { + custom_position = pos; +} + +Vector2 Example::get_custom_position() const { + return custom_position; +} + +Vector4 Example::get_v4() const { + return Vector4(1.2, 3.4, 5.6, 7.8); +} + +// Virtual function override. +bool Example::_has_point(const Vector2 &point) const { + Label *label = get_node