Using an external function from a dll with C++ code events

Using C++ code events, you can load a DLL (or a shared library on Ubuntu) and call a function exported by this DLL.

Beware! This kind of feature is obviously compatible only with native games!
You should try to avoid using DLL or external shared library as much as possible to ensure that your game will remain compatible with newer version of GDevelop.

Activate external C++ sources for your game

First, make sure that external C++ sources are activated for your game: In the project manager, edit the properties of your game and check the corresponding option.

Add .cpp and .h files

Add two files: a cpp source file and a header source file, to your project.

This can be done by activating C++ code of your project in the Project Manager then adding under the tab of Source File. In the present example, let's name them Functions.cpp and Functions.h.

As usual when working with C++, the .cpp file will contains the implementation of functions (that will be called from the events), and the .h file will only contains the declaration of these functions.

Creating the functions

In the header file, enter this code:

class RuntimeScene; //This is a forward declaration: It avoids including the entire RuntimeScene header file.

void MyFunction(RuntimeScene & scene); //The prototype of the function

Then, in the .cpp file, enter this code:

#include <GDCpp/Runtime/RuntimeScene.h>

void MyFunction(RuntimeScene & scene)
{
    //For now, we're just changing a variable to check if the function works.
    scene.GetVariables().Get("Result") = "It works!";
}

Calling the function from the events

In a scene of your game, add a C++ code event.
Edit it, add “Functions.h” (with the quotes) in the list of includes files. Check Functions.cpp in the list of dependencies (but not Functions.h) and, finally, replace the code of the function by:

if (scene.IsFirstLoop())
    MyFunction(scene);

Testing the code

You can launch the scene and check in the debugger that the variable has been modified.
If the scene does not compile properly, open the C++ tools (Ribbon General > C++ tools) and check the error.

If there is an undefined error, be sure that you saved the changes in Functions.cpp and Functions.h. Then, double click on the C++ code event, and simply click on Ok to trigger a recompilation of the files.

Calling an external library

Dynamic libraries can be loaded using C++ functions specific to Windows or Linux, but GDevelop offers ready to use functions that are hiding the platform specific code.
For testing, you can download this DLL: http://www.gamemaker.fr/pages/download/DLL/shaman_hash_dll.zip (It's a dll offering hashing functions, which was used with Game Maker).

The DLL must be put next to the game project (or the game executable when the game is compiled).

Replace the code inside Functions.cpp by this new code:

#include <GDCpp/RuntimeScene.h>
#define WINDOWS //As I'm testing it on Windows. Replace it by LINUX if you are under Ubuntu and want to open a shared library
#include <GDCpp/DynamicLibrariesTools.h>
#include <stdint.h>

//Here, we want to get a pointer to a function returning an int
//and taking a uint8_t, uint8_t and a double as parameters
//(Adapt this according to what function you want to call).
typedef int (*FuncType)(uint8_t* buf, uint8_t *cstr, double hash);

void MyFunction(RuntimeScene & scene)
{
    //Open the library
    Handle lib = gd::OpenLibrary("shaman.dll");
    if(!lib) {
        scene.GetVariables().Get("Result") = gd::DynamicLibraryLastError();
        return;
    }

    //Get a function from this library
    FuncType func = (FuncType)gd::GetSymbol(lib, "ShaCryptString");
    if (!func) {
        scene.GetVariables().Get("Result") = gd::DynamicLibraryLastError();
        return;
    }

    //Call the function
    uint8_t output[512] = {0};
    func(output, (uint8_t*)"Hello", 0);

    scene.GetVariables().Get("Result") = (const char*)output;

    //Close the library
    gd::CloseLibrary(lib);
}

Here are some explanations:

  • The functions provided by GDevelop to open the external libraries are in GDCpp/DynamicLibrariesTools.h. You need to define WINDOWS or LINUX before including this header.
  • Here, the example is using shaman.dll, which provide a function called ShaCryptString. Read the documentation of the DLL, or the source code of the DLL to see all the functions available and their parameters.
  • Each function in a C++ code has a signature: It represents the parameters of the functions and its return type. Here, the function we want to call has specific parameters: We declare it after the includes.
  • Then, in the function called by GDevelop, we open the library, get the function to call (ShaCryptString) and we call it.

Here, we create an empty array of integer (uint8_t output[512] = {0};) and we call the function of the library, passing it the array and a string to hash (Hello). 0 specifies we want to use the SHA1 hashing function. Refer to the documentation or the source code of the DLL for more information.

Other example

#include <GDCpp/RuntimeScene.h>
#define WINDOWS //As I'm testing it on Windows. Replace it by LINUX if you are under Ubuntu and want to open a shared library
#include <GDCpp/DynamicLibrariesTools.h>
#include <stdint.h>

//Here, we want to call a function returning an integer and taking
//a double and C-string as parameter
typedef int (*FuncType)(double parameter1, const char * parameter2);

void MyFunction(RuntimeScene & scene)
{
    //Open the library
    Handle lib = gd::OpenLibrary("my_library.dll");
    if(!lib) {
        scene.GetVariables().Get("Result") = gd::DynamicLibraryLastError();
        return;
    }

    //Get a function from this library
    FuncType func = (FuncType)gd::GetSymbol(lib, "AFunctionInTheLibrary");
    if (!func) {
        scene.GetVariables().Get("Result") = gd::DynamicLibraryLastError();
        return;
    }

    //Call the function with arbitrary parameters
    scene.GetVariables().Get("Result") = func(5, "Test");

    //Close the library
    gd::CloseLibrary(lib);
}