diff --git a/getting_started/step_by_step/scripting_languages.rst b/getting_started/step_by_step/scripting_languages.rst index b0f4fe665ff2..b4ab1e23d203 100644 --- a/getting_started/step_by_step/scripting_languages.rst +++ b/getting_started/step_by_step/scripting_languages.rst @@ -124,10 +124,10 @@ or ClojureCLR. However, C# is the only officially supported .NET option. in GDScript, C#, or C++ won't have a significant impact on performance. -C and C++ via GDExtension +C++ via GDExtension ~~~~~~~~~~~~~~~~~~~~~~~~~ -GDExtension allows you to write game code in C or C++ without needing to recompile +GDExtension allows you to write game code in C++ without needing to recompile or even restart Godot. .. image:: img/scripting_cpp.png diff --git a/tutorials/scripting/gdextension/gdextension_c_example.rst b/tutorials/scripting/gdextension/gdextension_c_example.rst deleted file mode 100644 index 3c0262457578..000000000000 --- a/tutorials/scripting/gdextension/gdextension_c_example.rst +++ /dev/null @@ -1,555 +0,0 @@ -.. _doc_gdnative_c_example: - -GDExtension C example -================== - -Introduction ------------- - -This tutorial introduces the bare minimum required to create GDNative -modules. This should be your starting point into the world of GDNative. -Understanding the contents of this tutorial will help you in understanding all -that is to come after this. - -Before we begin, you can download the source code to the example object we -describe below in the `GDNative-demos repository -`_. - -This example project also contains a SConstruct file that makes compiling a -little easier, but in this tutorial we'll be doing things by hand to -understand the process. - -:ref:`GDNative ` can be used to create several types of -additions to Godot, using interfaces such as -:ref:`PluginScript ` or -:ref:`ARVRInterfaceGDNative `. In this tutorial we -are going to look at creating a :ref:`NativeScript ` -module. NativeScript allows you to write logic in C or C++ in a similar fashion -as you would write a GDScript file. We'll be creating the C equivalent of this -GDScript: - -:: - - extends Reference - - var data - - func _ready(): - data = "World from GDScript!" - - func get_data(): - return data - -Future tutorials will focus on the other types of GDNative modules and explain -when and how to use each of them. - -Prerequisites -------------- - -Before we start you'll need a few things: - -1) A Godot executable for your target version. -2) A C compiler. On Linux, install ``gcc`` or ``clang`` from your package - manager. On macOS, you can install Xcode from the Mac App Store. On Windows, - you can use Visual Studio 2015 or later, or MinGW-w64. -3) A Git clone of the `godot-headers - repository `_: these are - the C headers for Godot's public API exposed to GDNative. - -For the latter, we suggest that you create a dedicated folder for this GDNative -example project, open a terminal in that folder and execute: - -.. code-block:: none - - git clone https://github.com/godotengine/godot-headers.git - -This will download the required files into that folder. - -.. tip:: - - If you plan to use Git for your GDNative project, you can also add - ``godot-headers`` as a Git submodule. - -.. note:: - - The ``godot-headers`` repository has different branches. As Godot evolves, - so does GDNative. While we try to preserve compatibility between version, - you should always build your GDNative module against headers matching the - Godot stable branch (e.g. ``3.1``) and ideally actual release (e.g. - ``3.1.1-stable``) that you use. - GDNative modules built against older versions of the Godot headers *may* - work with newer versions of the engine, but not the other way around. - -The ``master`` branch of the ``godot-headers`` repository is kept in line with -the ``master`` branch of Godot and thus contains the GDNative class and -structure definitions that will work with the latest development builds. - -If you want to write a GDNative module for a stable version of Godot, look at -the available Git tags (with ``git tags``) for the one matching your engine -version. In the ``godot-headers`` repository, such tags are prefixed with -``godot-``, so you can e.g. checkout the ``godot-3.1.1-stable`` tag for use with -Godot 3.1.1. In your cloned repository, you can do: - -.. code-block:: none - - git checkout godot-3.1.1-stable - -If a tag matching your stable release is missing for any reason, you can fall -back to the matching stable branch (e.g. ``3.1``), which you would also check -out with ``git checkout 3.1``. - -If you are building Godot from source with your own changes that impact -GDNative, you can find the updated class and structure definition in -``/modules/gdnative/include`` - -Our C source ------------- - -Let's start by writing our main code. Eventually, we want to end up with a file -structure that looks along those lines: - -.. code-block:: none - - + - + godot-headers - - - + simple - + bin - - libsimple.dll/so/dylib - - libsimple.gdnlib - - simple.gdns - main.tscn - project.godot - + src - - simple.c - -Open up Godot and create a new project called "simple" alongside your -``godot-headers`` Git clone. This will create the ``simple`` folder and -``project.godot`` file. Then manually create a ``src`` folder alongside the -``simple`` folder, and a ``bin`` subfolder in the ``simple`` folder. - -We're going to start by having a look at what our ``simple.c`` file contains. -Now, for our example here we're making a single C source file without a header -to keep things simple. Once you start writing bigger projects it is advisable -to break your project up into multiple files. That however falls outside of the -scope of this tutorial. - -We'll be looking at the source code bit by bit so all the parts below should all -be put together into one big file. Each section will be explained as we add it. - -.. code-block:: C - - #include - - #include - - const godot_gdnative_core_api_struct *api = NULL; - const godot_gdnative_ext_nativescript_api_struct *nativescript_api = NULL; - -The above code includes the GDNative API struct header and a standard header -that we will use further down for string operations. -It then defines two pointers to two different structs. GDNative supports a large -collection of functions for calling back into the main Godot executable. In -order for your module to have access to these functions, GDNative provides your -application with a struct containing pointers to all these functions. - -To keep this implementation modular and easily extendable, the core functions -are available directly through the "core" API struct, but additional functions -have their own "GDNative structs" that are accessible through extensions. - -In our example, we access one of these extension to gain access to the functions -specifically needed for NativeScript. - -A NativeScript behaves like any other script in Godot. Because the NativeScript -API is rather low level, it requires the library to specify many things more -verbosely than other scripting systems, such as GDScript. When a NativeScript -instance gets created, a library-given constructor gets called. When that -instance gets destroyed, the given destructor will be executed. - -.. code-block:: C - - void *simple_constructor(godot_object *p_instance, void *p_method_data); - void simple_destructor(godot_object *p_instance, void *p_method_data, void *p_user_data); - godot_variant simple_get_data(godot_object *p_instance, void *p_method_data, - void *p_user_data, int p_num_args, godot_variant **p_args); - -These are forward declarations for the functions we'll be implementing for our -object. A constructor and destructor is needed. Additionally, the object will -have a single method called ``get_data``. - -Next up is the first of the entry points Godot will call when our dynamic -library is loaded. These methods are all prefixed with ``godot_`` (you can -change this later on) followed by their name. ``gdnative_init`` is a function -that initializes our dynamic library. Godot will give it a pointer to a -structure that contains various bits of information we may find useful among -which the pointers to our API structures. - -For any additional API structures we need to loop through our extensions array -and check the type of extension. - -.. code-block:: C - - void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *p_options) { - api = p_options->api_struct; - - // Now find our extensions. - for (int i = 0; i < api->num_extensions; i++) { - switch (api->extensions[i]->type) { - case GDNATIVE_EXT_NATIVESCRIPT: { - nativescript_api = (godot_gdnative_ext_nativescript_api_struct *)api->extensions[i]; - }; break; - default: break; - } - } - } - -Next up is ``gdnative_terminate`` which is called before the library is -unloaded. Godot will unload the library when no object uses it anymore. Here, -you can do any cleanup you may need to do. For our example, we're simply going -to clear our API pointers. - -.. code-block:: C - - void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *p_options) { - api = NULL; - nativescript_api = NULL; - } - -Finally, we have ``nativescript_init`` which is the most important function we'll -need today. This function will be called by Godot as part of loading a GDNative -library and communicates back to the engine what objects we make available. - -.. code-block:: C - - void GDN_EXPORT godot_nativescript_init(void *p_handle) { - godot_instance_create_func create = { NULL, NULL, NULL }; - create.create_func = &simple_constructor; - - godot_instance_destroy_func destroy = { NULL, NULL, NULL }; - destroy.destroy_func = &simple_destructor; - - nativescript_api->godot_nativescript_register_class(p_handle, "SIMPLE", "Reference", - create, destroy); - - godot_instance_method get_data = { NULL, NULL, NULL }; - get_data.method = &simple_get_data; - - godot_method_attributes attributes = { GODOT_METHOD_RPC_MODE_DISABLED }; - - nativescript_api->godot_nativescript_register_method(p_handle, "SIMPLE", "get_data", - attributes, get_data); - } - -We first tell the engine which classes are implemented by calling -``nativescript_register_class``. The first parameter here is the handle pointer -given to us. The second is the name of our object class. The third is the type -of object in Godot that we 'inherit' from; this is not true inheritance but it's -close enough. Finally, our fourth and fifth parameters are descriptions for our -constructor and destructor. - -We then tell Godot about our methods (well our one method in this case), by -calling ``nativescript_register_method`` for each method of our class. In our -case, that is just ``get_data``. Our first parameter is yet again our handle -pointer. The second is again the name of the object class we're registering. The -third is the name of our function as it will be known to GDScript. The fourth is -our attributes setting (see ``godot_method_rpc_mode`` enum in -``godot-headers/nativescript/godot_nativescript.h`` for possible values). The -fifth and final parameter is a description of which function to call when the -method gets called. - -The description struct ``instance_method`` contains the function pointer to the -function itself as first field. The other two fields in these structs are for -specifying per-method userdata. The second is the ``method_data`` field which is -passed on every function call as the ``p_method_data`` argument. This is useful -to reuse one function for different methods on possibly multiple different -script-classes. If the ``method_data`` value is a pointer to memory that needs -to be freed, the third ``free_func`` field can contain a pointer to a function -that will free that memory. That free function gets called when the script -itself (not instance!) gets unloaded (so usually at library-unload time). - -Now, it's time to start working on the functions of our object. First, we define -a structure that we use to store the member data of an instance of our GDNative -class. - -.. code-block:: C - - typedef struct user_data_struct { - char data[256]; - } user_data_struct; - -And then, we define our constructor. All we do in our constructor is allocate -memory for our structure and fill it with some data. Note that we use Godot's -memory functions so the memory gets tracked and then return the pointer to our -new structure. This pointer will act as our instance identifier in case multiple -objects are instantiated. - -This pointer will be passed to any of our functions related to our object as a -parameter called ``p_user_data``, and can both be used to identify our instance -and to access its member data. - -.. code-block:: C - - void *simple_constructor(godot_object *p_instance, void *p_method_data) { - user_data_struct *user_data = api->godot_alloc(sizeof(user_data_struct)); - strcpy(user_data->data, "World from GDNative!"); - - return user_data; - } - -Our destructor is called when Godot is done with our object and we free our -instances' member data. - -.. code-block:: C - - void simple_destructor(godot_object *p_instance, void *p_method_data, void *p_user_data) { - api->godot_free(p_user_data); - } - -And finally, we implement our ``get_data`` function. Data is always sent and -returned as variants so in order to return our data, which is a string, we first -need to convert our C string to a Godot string object, and then copy that string -object into the variant we are returning. - -.. code-block:: C - - godot_variant simple_get_data(godot_object *p_instance, void *p_method_data, - void *p_user_data, int p_num_args, godot_variant **p_args) { - godot_string data; - godot_variant ret; - user_data_struct *user_data = (user_data_struct *)p_user_data; - - api->godot_string_new(&data); - api->godot_string_parse_utf8(&data, user_data->data); - api->godot_variant_new_string(&ret, &data); - api->godot_string_destroy(&data); - - return ret; - } - -Strings are heap-allocated in Godot, so they have a destructor which frees the -memory. Destructors are named ``godot_TYPENAME_destroy``. When a Variant gets -created with a String, it references the String. That means that the original -String can be "destroyed" to decrease the ref-count. If that does not happen the -String memory will leak since the ref-count will never be zero and the memory -never deallocated. The returned variant gets automatically destroyed by Godot. - -.. note:: - - In more complex operations it can be confusing the keep track of which value - needs to be deallocated and which does not. As a general rule: call - ``godot_TYPENAME_destroy`` when a C++ destructor would be called instead. - The String destructor would be called in C++ after the Variant was created, - so the same is necessary in C. - -The variant we return is destroyed automatically by Godot. - -And that is the whole source code of our module. - -Compiling ---------- - -We now need to compile our source code. As mentioned our example project on -GitHub contains a SCons configuration that does all the hard work for you, but -for our tutorial here we are going to call the compilers directly. - -Assuming you are sticking to the folder structure suggested above, it is best to -open a terminal session in the ``src`` folder and execute the commands from -there. Make sure to create the ``bin`` folder before you proceed. - -On Linux: - -.. code-block:: none - - gcc -std=c11 -fPIC -c -I../godot-headers simple.c -o simple.o - gcc -rdynamic -shared simple.o -o ../simple/bin/libsimple.so - -On macOS: - -.. code-block:: none - - clang -std=c11 -fPIC -c -I../godot-headers simple.c -o simple.os - clang -dynamiclib simple.os -o ../simple/bin/libsimple.dylib - -On Windows: - -.. code-block:: none - - cl /Fosimple.obj /c simple.c /nologo -EHsc -DNDEBUG /MD /I. /I..\godot-headers - link /nologo /dll /out:..\simple\bin\libsimple.dll /implib:..\simple\bin\libsimple.lib simple.obj - -.. note:: - - On the Windows build you also end up with a ``libsimple.lib`` library. This - is a library that you can compile into a project to provide access to the - DLL. We get it as a byproduct and we do not need it :) - When exporting your game for release this file will be ignored. - -Creating the GDNativeLibrary (``.gdnlib``) file ------------------------------------------------ - -With our module compiled, we now need to create a corresponding -:ref:`GDNativeLibrary ` resource with ``.gdnlib`` -extension which we place alongside our dynamic libraries. This file tells Godot -what dynamic libraries are part of our module and need to be loaded per -platform. - -We can use Godot to generate this file, so open the "simple" project in the -editor. - -Start by clicking the create resource button in the Inspector: - -.. image:: img/new_resource.gif - -And select ``GDNativeLibrary``: - -.. image:: img/gdnativelibrary_resource.png - -You should see a contextual editor appear in the bottom panel. Use the "Expand -Bottom Panel" button in the bottom right to expand it to full height: - -.. image:: img/gdnativelibrary_editor.png - -General properties -~~~~~~~~~~~~~~~~~~ - -In the Inspector, you have various properties to control loading the library. - -If *Load Once* is enabled, our library is loaded only once and each individual -script that uses our library will use the same data. Any variable you define -globally will be accessible from any instance of your object you create. If -*Load Once* is disabled, a new copy of the library is loaded into memory each -time a script accesses the library. - -If *Singleton* is enabled, our library is automatically loaded and a function -called ``godot_gdnative_singleton`` is called. We'll leave that for another -tutorial. - -The *Symbol Prefix* is a prefix for our core functions, such as ``godot_`` in -``godot_nativescript_init`` seen earlier. If you use multiple GDNative libraries -that you wish to statically link, you will have to use different prefixes. This -again is a subject to dive into deeper in a separate tutorial, it is only needed -at this time for deployment to iOS as this platform does not like dynamic -libraries. - -*Reloadable* defines whether the library should be reloaded when the editor -loses and gains focus, typically to pick up new or modified symbols from any -change made to the library externally. - -Platform libraries -~~~~~~~~~~~~~~~~~~ - -The GDNativeLibrary editor plugin lets you configure two things for each -platform and architecture that you aim to support. - -The *Dynamic Library* column (``entry`` section in the saved file) tells us for -each platform and feature combination which dynamic library has to be loaded. -This also informs the exporter which files need to be exported when exporting to -a specific platform. - -The *Dependencies* column (also ``dependencies`` section) tells Godot what other -files need to be exported for each platform in order for our library to work. -Say that your GDNative module uses another DLL to implement functionality from a -3rd party library, this is where you list that DLL. - -For our example, we only built libraries for Linux, macOS and/or Windows, so you -can link them in the relevant fields by clicking the folder button. If you built -all three libraries, you should have something like this: - -.. image:: img/gdnativelibrary_editor_complete.png - -Saving the resource -~~~~~~~~~~~~~~~~~~~ - -We can then save our GDNativeLibrary resource as ``bin/libsimple.gdnlib`` with -the Save button in the Inspector: - -.. image:: img/gdnativelibrary_save.png - -The file is saved in a text-based format and should have contents similar to -this: - -.. code-block:: none - - [general] - - singleton=false - load_once=true - symbol_prefix="godot_" - reloadable=true - - [entry] - - Linux.64="res://bin/libsimple.so" - OSX.64="res://bin/libsimple.dylib" - OSX.32="res://bin/libsimple.dylib" - Windows.64="res://bin/libsimple.dll" - - [dependencies] - - Linux.64=[ ] - OSX.64=[ ] - OSX.32=[ ] - Windows.64=[ ] - -Creating the NativeScript (``.gdns``) file ------------------------------------------- - -With our ``.gdnlib`` file we've told Godot how to load our library, now we need -to tell it about our "SIMPLE" object class. We do this by creating a -:ref:`NativeScript ` resource file with ``.gdns`` extension. - -Like done for the GDNativeLibrary resource, click the button to create a new -resource in the Inspector and select ``NativeScript``: - -.. image:: img/nativescript_resource.png - -The inspector will show a few properties that we need to fill. As *Class Name* -we enter "SIMPLE" which is the object class name that we declared in our C -source when calling ``godot_nativescript_register_class``. We also need to -select our ``.gdnlib`` file by clicking on *Library* and selecting *Load*: - -.. image:: img/nativescript_library.png - -.. note:: - - The *Class Name* must have the same spelling as the one given in ``godot_nativescript_init`` - when registering the class. - - -Finally, click on the save icon and save this as ``bin/simple.gdns``: - -.. image:: img/save_gdns.gif - -Now it's time to build our scene. Add a Control node to your scene as your root -and call it ``main``. Then add a Button and a Label as child nodes. Place them -somewhere nice on screen and give your button a name. - -.. image:: img/c_main_scene_layout.png - -Select the control node and attach a script to it: - -.. image:: img/add_main_script.gif - -Next link up the ``pressed`` signal on the button to your script: - -.. image:: img/connect_button_signal.gif - -Don't forget to save your scene, call it ``main.tscn``. - -Now we can implement our ``main.gd`` code: - -:: - - extends Control - - # load the Simple library - @onready var data = preload("res://bin/simple.gdns").new() - - func _on_Button_pressed(): - $Label.text = "Data = " + data.get_data() - -After all that, our project should work. The first time you run it Godot will -ask you what your main scene is and you select your ``main.tscn`` file and -presto: - -.. image:: img/c_sample_result.png diff --git a/tutorials/scripting/gdextension/gdextension_cpp_example.rst b/tutorials/scripting/gdextension/gdextension_cpp_example.rst index 3b1d709bc430..824a4026d7e9 100644 --- a/tutorials/scripting/gdextension/gdextension_cpp_example.rst +++ b/tutorials/scripting/gdextension/gdextension_cpp_example.rst @@ -6,10 +6,6 @@ GDExtension C++ example Introduction ------------ -This tutorial builds on top of the information given in the -:ref:`GDExtension C example `, so we highly recommend you -read that first. - The C++ bindings for GDExtension are built on top of the C GDExtension API and provide a nicer way to "extend" nodes in Godot using C++. This new system allows the extension of Godot to nearly the same level as statically linked C++ modules. @@ -118,7 +114,7 @@ is the number of CPU threads you have on your system. The example below uses 4 t .. code-block:: none cd godot-cpp - scons platform= generate_bindings=yes -j4 + scons platform= -j4 cd .. This step will take a while. When it is completed, you should have static @@ -164,7 +160,7 @@ GDExtension node we'll be creating. We will name it ``gdexample.h``: private: float time_passed; -protected: + protected: static void _bind_methods(); public: @@ -189,7 +185,7 @@ within this namespace. Then we have our class definition, which inherits from our Sprite2D through a container class. We'll see a few side effects of this later on. The -``GD_CLASS`` macro sets up a few internal things for us. +``GDCLASS`` macro sets up a few internal things for us. After that, we declare a single member variable called ``time_passed``. @@ -235,17 +231,13 @@ Let's implement our functions by creating our ``gdexample.cpp`` file: } This one should be straightforward. We're implementing each method of our class -that we defined in our header file. Note that the ``bind_method`` call -**must** expose the ``_process`` method, otherwise Godot will not be able to use -it. However, we do not have to tell Godot about our constructor, destructor and -``_init`` functions. - -The other method of note is our ``_process`` function, which keeps track -of how much time has passed and calculates a new position for our sprite using a -sine and cosine function. What stands out is calling -``owner->set_position`` to call one of the built-in methods of our Sprite2D. This -is because our class is a container class; ``owner`` points to the actual Sprite2D -node our script relates to. +that we defined in our header file. + +Note our ``_process`` function, which keeps track of how much time has passed +and calculates a new position for our sprite using a sine and cosine function. +What stands out is calling ``owner->set_position`` to call one of the built-in +methods of our Sprite2D. This is because our class is a container +class; ``owner`` points to the actual Sprite2D node our script relates to. There is one more C++ file we need; we'll name it ``register_types.cpp``. Our GDExtension plugin can contain multiple classes, each with their own header @@ -305,6 +297,20 @@ We first call a function in our bindings library that does its usual stuff. After that, we call the function ``register_class`` for each of our classes in our library. +At last, we need the header file for the ``register_types.cpp`` named +``register_types.h``. + +.. code-block:: C++ + + #ifndef GDEXAMPLE_REGISTER_TYPES_H + #define GDEXAMPLE_REGISTER_TYPES_H + + void initialize_example_module(); + void uninitialize_example_module(); + + #endif // GDEXAMPLE_REGISTER_TYPES_H + + Compiling the plugin -------------------- @@ -333,7 +339,7 @@ You should now be able to find the module in ``demo/bin/``. Here, we've compiled both godot-cpp and our gdexample library as debug builds. For optimized builds, you should compile them using the - ``target=release`` switch. + ``target=template_release`` switch. Using the GDExtension module ------------------------- @@ -352,13 +358,12 @@ loaded for each platform and the entry function for the module. It is called ``g [libraries] - linux.64="res://bin/linux/libgdexample.so" - Windows.x86_64="res://bin/win64/libgdexample.dll" - macos="res://bin/osx/libgdexample.framework" + linux.64="res://bin/linux/libgdexample.linux.64.so" + windows.x86_64="res://bin/win64/libgdexample.windows.x86_64.dll" + macos="res://bin/osx/libgdexample.macos.framework" This file contains a ``configuration`` section that controls the entry function of the module. -TODO: check up on the correctness of the last sentence The ``libraries`` section is the important bit: it tells Godot the location of the dynamic library in the project's filesystem for each supported platform. It will also result in *just* that file being exported when you export the project, @@ -372,19 +377,18 @@ plugin implements someone else's library and requires you to supply a third-party dynamic library with your project. Time to jump back into Godot. We load up the main scene we created way back in -the beginning and now add a Sprite2D to our scene: +the beginning and now add a newly available GDExample node to the scene: -.. image:: img/gdnative_cpp_nodes.png +.. image:: img/gdextension_cpp_nodes.png -We're going to assign the Godot logo to this sprite as our texture, disable the -``centered`` property and drag our ``gdexample.gdns`` file onto the ``script`` -property of the sprite: +We're going to assign the Godot logo to this node as our texture, disable the +``centered`` property: -.. image:: img/gdnative_cpp_sprite.png +.. image:: img/gdextension_cpp_sprite.png We're finally ready to run the project: -.. image:: img/gdnative_cpp_animated.gif +.. image:: img/gdextension_cpp_animated.gif Adding properties ----------------- diff --git a/tutorials/scripting/gdextension/img/gdextension_cpp_animated.gif b/tutorials/scripting/gdextension/img/gdextension_cpp_animated.gif new file mode 100644 index 000000000000..9a5488889fba Binary files /dev/null and b/tutorials/scripting/gdextension/img/gdextension_cpp_animated.gif differ diff --git a/tutorials/scripting/gdextension/img/gdextension_cpp_nodes.png b/tutorials/scripting/gdextension/img/gdextension_cpp_nodes.png new file mode 100644 index 000000000000..c4a93cb088ad Binary files /dev/null and b/tutorials/scripting/gdextension/img/gdextension_cpp_nodes.png differ diff --git a/tutorials/scripting/gdextension/img/gdextension_cpp_sprite.png b/tutorials/scripting/gdextension/img/gdextension_cpp_sprite.png new file mode 100644 index 000000000000..2ad007126f3a Binary files /dev/null and b/tutorials/scripting/gdextension/img/gdextension_cpp_sprite.png differ diff --git a/tutorials/scripting/gdextension/what_is_gdextension.rst b/tutorials/scripting/gdextension/what_is_gdextension.rst index adf2ecb537aa..a47d5db64ffb 100644 --- a/tutorials/scripting/gdextension/what_is_gdextension.rst +++ b/tutorials/scripting/gdextension/what_is_gdextension.rst @@ -100,4 +100,5 @@ compatibility requirements as it relies on low-level *ptrcalls* to function.) -> GDExtension add-ons compiled for a given Godot version are only guaranteed to work with the same minor release series. For example, a GDExtension add-on compiled for -Godot 4.0 will only work with Godot 4.0, 4.0.1, 4.0.2… but not Godot 3.5 or 3.6. +Godot 4.0 will only work with Godot 4.0, 4.0.1, 4.0.2. In addition, GDExtension is +not compatible with Godot 3.x.