In this article, I’m going to present you the real advantages of hot reloading when creating a game with raylib, and show you how you can do it yourself quite easily.

 

First, what is raylib ?

 

Raylib is something I (really) always wanted. When I discovered it, back last year, it was a blast for me.

I always wanted to create games (or prototypes) easily, just by coding.
Without fancy editor in which you can spend hours.
Without extra « features that are so awesome you certainly need hours to learn how to use them, by watching deadly long tutorial videos » like in traditionnal game engines (Unity, Unreal Engine, Lumberyard…). Just to be clear, I used both Unity and Unreal, and they are great. You can do really good stuff with them, quickly, and they both have a great code design.
But you know, I’m a programmer, and what I really enjoy is actually writting code without beeing forced to search for hours in documentations how to use some components. I also like to have a total freedom regarding the architecture of the projects I work on, on my free time (if I want to experiment a full data-oriented and/or functional oriented design architecture, I can do it without any constraint). And precisely, because it’s on my free time, I think, the really first thing that matters is to enjoy doing it, no ? Otherwise it makes no sense (at least for me). And with that state of mind, raylib was just the framework I needed!

So raylib is an old plain C (C99) game framework, which works with opengl3.3 (or opengl ES). It has everything you need to create simple (or not so simple actually…) games including abstractions for dealing with sounds, images, meshes, shaders…

The best part is that it’s written in C, and you know that C is awesome when it comes to create bindings! So there are lots of bindings for lots of other programming languages like Go, Lua, Rust… In fact, it’s quite easy to create your own binding, because raylib depends on really few other external libraries. Actually, there is just GLFW to handle the window creation.

Raylib includes emscripter and the android headers + GLES, which signifies that you can build your game for the web, or for android if you want. Actually, there are a quite numbers of targets you can build to, including Windows (of course, you even can build your game as a W10 app), Linux, OSX, FreeBSD, and even Raspberry!

And the best part of raylib, is that the API is really tiny. There are no functions you don’t need. Everything is well named. You just need the cheatsheet, and you are good to go! No need to look for documentation during hours! You actually can spend most of your time on writting the game instead of looking for how to use some components. Told you, it’s a blast!

With all of these reasons, I think you understand now why I love raylib! It’s tiny, in old plain C, open source, with a very permissive licence. That gives a really solid framework for beginners (and advanced programmers) to create games, or whatever other graphic application you want to create.

 

So now, let’s dive into the main topic : hot reloading!

 

First, some obvious questions you may be able to ask if you are developing your game, or whatever software, are : what is hot reload, and why should I ever want to add this feature to my program ?

So, hot reloading consists of reloading some parts (or even huge parts) of your code, at runtime. By default, raylib is written in C, and you know that C (like any other system language) is compiled into an executable file. So, traditionnaly, if you want to make even quick changes to your program, you always need to go through these steps :

  • shut down the program
  • do your changes
  • recompile it (and make sure you don’t have any error)
  • relaunch your program
  • come back where you were before the changes
  • look if your changes are correct

For short software, that probably not take a long time. But believe me, for more complicated programs, like games, or whatever other software with lots of UI included, for example, it actually can become a huge pain in the ass really quickly.

Why do you think developers came to love the web, with HTML/CSS and JS or even PHP ? The answer is quite simple : you make a change, you reload the page, and you see your change in realtime!

It’s a huge time savier when developing whatever kind of software, if you can iterate as fast as you can, without taking the cost of wasting time by recompiling and relaunching the project, every single time you modify some piece of code.

For this reason, lots of interpreted languages have emerged like Lua, Python, Ruby… Now, the most popular programming language out there is actually an interpreted language as well : JS (with the huge impact of NodeJS in the programming world).

In the game development industry, Lua was a classical choice until few years (and still is in some studios). But many choosed to build their own « visually scripting language », which seem to be a norm now. Like the Blueprint system of Unreal Engine 4 for example (every huge game studio in the world has it’s own « Blueprint » system inside its game engine, from Guerilla Games with the Decima Engine, to Massive and its SnowDrop Engine…). These visual scripting languages give the possibility to non-programmers to make some quick changes to the code without having to ask to programmers to do it, and see in realtime their modifications. It’s a killer feature to prototype faster!

So, why don’t we use one of these language, make binding for it, or even create our own scripting language ?

The answer for me is quite simple : first, (personnaly), even if that sounds incredibly interesting, I’ve no time to build a scripting language. I don’t have enough free time, and all I want is to create some basics games with raylib, not languages. Then, a scripting language is, by it’s core nature, like I said, « interpreted ». Which means that, at runtime, the program needs to go through the scripting code, and « translate » it into instcructions, on the fly. It has a cost.

But you may answer, for us, with raylib, and if all we want is to create a simple game, this runtime cost is not a big deal. And you are right. But a scripting language brings an other problem, I think, which is more problematic. By using one of them, for specific part of your game (like IA for example) you will have two pieces of software to maintain : the core code of your program (in C/C++ in our case), and the scripting code.

And, if you screw up at some point on your bindings with the scripting language, you’ll have horrible bugs to resolve.

On the other hand, what we can do, to keep beeing as productive as we can, and iterate quickly, is to implement hot reload, with dll files (on windows, so files on Linux). And it’s a real chance that raylib is written in C (instead of C++ for example), because it’s much more easier to do with old plain C, where you have no classes, just a bunch of structures and functions.

 

It’s time to show some code example!

 

So, how to do it ? First, let’s create a really simple program using raylib and raygui (which is an « addon » of raylib for creating GUI items quickly).

Here’s is our file named « main.c », which contains the main function :

#include "raylib.h"
#define RAYGUI_IMPLEMENTATION
#include "raygui.h"

int main(void){
    InitWindow(1280, 720, "Test"); // 1280x720 window named "Test"
    while(!WindowShouldClose()){
        BeginDrawing();
            ClearBackground(RED);
        EndDrawing();
    }
    CloseWindow();
    return 0;
}

 

We can compile it with this command on Windows using mingw, assuming raylib.h and raygui.h are in the include folder, and that raylib + glfw3 are available as static libraries inside the libs folder:

gcc main.c -I./include -L./libs -lglfw3 -lraylib -lopengl32 -lgdi32 -o main.exe 

Finally, we got the main.exe executable file which just creates a window, and fill it with red, which we can launch with this command :

./main.exe

Really simple.

So now, let’s add some GUI. Let’s add a button, by using raygui, to this boring red window:

#include "raylib.h"
#define RAYGUI_IMPLEMENTATION
#include "raygui.h"

int main(void){
    InitWindow(1280, 720, "Test"); // 1280x720 window named "Test"
    while(!WindowShouldClose()){
        BeginDrawing();
            ClearBackground(RED);
            if(GuiButton((Rectangle){ 500, 200, 250, 60 }, "TEST BUTTON")) {
                puts("Button pressed\n");
            }
        EndDrawing();
    }
    CloseWindow();
    return 0;
}

Again, it’s really simple, we are just adding a button with the text TEST BUTTON by using the GuiButton function from raygui. This button will be positioned at x=500, y=200, and it will be 250px long and 60px large.
If the button is pressed, we show on the console :
Button pressed

To be cleaner, I cutted the definitions part from the code of raygui.h (which contained by default declarations and definitions, it’s a header-only file), and I compiled it into a static library which I copied/pasted into the libs folder.

So now, we can compile the code with the same previous gcc command, just think about adding -lraygui statement as a parameter, before -lglfw3. And then execute the program to test our code, like before.

gcc main.c -I./include -L./libs -lraygui -lglfw3 -lraylib -lopengl32 -lgdi32 -o main.exe 

So now, you agree that we have a simple program which just contained a window and a button. But if we want to modify everything related to this GUI, we need to recompile it every single time, and relaunch it. Yeah, boring stuff…

It’s time to dive into the fun part with hot reloading!

First, let’s split our code, by creating some abstraction here, with a new file simply called core.c in our case (which will be inside the src folder, and for clarity, we move main.c inside this folder too), associated with its core.h header file (which is inside the include folder).

// inside core.h

#pragma once

void core_create_window(
    const unsigned short in_width,
    const unsigned short in_height,
    const char* in_title
);
void core_execute_loop();
bool core_window_should_close();
void core_close_window();

// inside core.c

#include "core.h"
#include "raylib.h" 
#define RAYLIB_IMPLEMENTATION 
#include "raygui.h"

void core_create_window(
    const unsigned short in_width,
    const unsigned short in_height,
    const char* in_title
){
    InitWindow(in_width, in_height, in_title);
}

void core_execute_loop(){
    BeginDrawing();
        ClearBackground(RED);
        if(GuiButton((Rectangle){ 500, 200, 250, 60 }, "TEST BUTTON")) {
            puts("Button pressed\n");
        }
    EndDrawing();
}

bool core_window_should_close(){
    return WindowShouldClose();
}

void core_close_window(){
    CloseWindow();
}

Which signify that our main.c file just includes core.h now, with no need of raylib header files. And it simply call functions from core.c (our abstraction layer).

// inside main.c

#include "core.h"

int main(void){
    core_init_window(1280, 720, "Test"); // 1280x720 window named "Test"
    while(!core_window_should_close()){
        core_execute_loop();
    }
    core_close_window();
    return 0;
}

So, if we compile our program with the following commands, it should do the exact same thing as before :

// Compiling the core.c file into an object file
gcc -c ./src/core.c -I./include -o core.o

// Compiling the full project to get the executable file
gcc ./src/main.c core.o -I./include -L./libs -lraygui -lglfw3 -lraylib -lopengl32 -lgdi32 -o main.exe

The result is a final main.exe executable file, which does the exact same thing as previously.

But until now, we can’t modify our program at runtime, without relaunching it. So it’s time to add the hot reload feature!

Hot reload on windows means dll files. So, in fact, with our example here, we need to transform our core.o file into a dll file, and get its functions by adress in our main.c file to be able to call them. Basically, it’s all we need.

But first, we need to add some definitions in our core.h header file, to tell the compiler to build core.c as a shared library (dll), when passing a specific option as parameter at the compiling step (in our case, that will be BUILD_LIBTYPE_SHARED).

// inside core.h

#pragma once

#if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED)
    #define CORE __declspec(dllexport) // We are building core as a Win32 shared library (.dll)
#elif defined(_WIN32) && defined(USE_CORE_LIB_SHARED)
    #define CORE __declspec(dllimport) // We are using core as a Win32 shared library (.dll)
#else
    #define CORE // We are building or using core as a static library
#endif

void core_create_window(
    const unsigned short in_width,
    const unsigned short in_height,
    const char* in_title
);
void core_execute_loop();
bool core_window_should_close();
void core_close_window();

Now these definitions are added, we can compile the code.c file as a dynamic library (dll).

To do so, with gcc, we just need to add one command : gcc -shared file.o -o file.dll, after creating the core.o file, to assemble it into a dll file :

// Compiling the core.c file into an object file
// The option BUILD_LIBTYPE_SHARED indicates that we
// are building the core.c file to be used as a dll
gcc -c -DBUILD_LIBTYPE_SHARED core.c -I./include -o core.o

// Assembling the core.o object file into a dll file
gcc -shared core.o -o core.dll

Finally, we have an independant piece of code in C, which can be used in our main program (inside main.c).
But how can we do that ?

The first thing to do, is to include the libloaderapi.h file in the main.c file, but to be as clean as possible, we will include it only if we are using core.c as a dll file. If we are using our core.c file traditionnaly, simply as an object file, we don’t want to include this libloaderapi.h file in our project. Remember, you always want to include the less dependencies you can (it’s really important for compilation speed).

You may probably ask why we are using this header file specifically, instead of just using the classic windows.h. In fact, there are two reasons:

  • First, the obvious one, by including windows.h, you are actually including a lot of code in your project. Lot’s of code that you probably will never use. So again, I repeat myself but it’s important, keep in mind, whenever you can import as less as needed code in your project, do it! It’s really a time savier when compiling the entire project.
  • On the other hand, raylib defines some basic structures for its needs, and you can have terrible issues by importing windows.h due to the fact that there are some structures defined in windows.h which share the same name as the one from raylib. For example, raylib defines a structure named Rectangle, which is defined as an other structure inside windows.h (for gdi purpuses). So, except if you want to modify raylib, and rename some structures and functions by yourself, don’t use windows.h.

Let’s come back to the topic.

The other problem we face, which let us choose between two choices, is whether we link to raylib (the static library) inside our dll, or inside the executable file.

Both decisions could be made, but they bring very different solutions to resolve the loading functions part.
In one case, we need to retrieve the state of the program from the dll, to the main.c file, to be able to restore it whenever we recompile the dll.
In the other case, we need to give to the dll pointers to raylib functions, to be able to use them from our core.c file.

The method I choose is the second one, because it’s less difficult to do (at least I think). If raylib was initialized on the heap instead of the stack, I probably had choose the first one. Because, when you initialize variables on the stack, whenever you recompile the program/dll, all of these variables are reset to their default values. But if it’s initialize on the heap, the variables don’t changes when recompiling the dll. They already are in memory, we just need to squizz the initialization step, which already has been done, and we are good to go!

So, keep in mind I choosed the second option, the one which consists of linking raylib in our main program, and making a bridge to give the functions pointers of raylib to our dll.
Now it’s said, let’s continue the code example!

The first thing to do, is to prepare a function in our core.c, only available when we are building this file as a dll, which will do the bridge between the raylib functions from our main.c file, to the dll.

So in order to be able to use these functions (from raylib and raygui), we also need to prepare some functions pointers variables, to store them in the dll file, to be able to use them later. Because remember, we don’t link raylib and raygui as static libraries in our dll file. We need brige functions.

I created a function called core_load_raylib_functions to achieve this goal inside core.c, and because it’s a function that will only be used when we are compiling core.c as a dll, there is no need to declare it inside the core.h header file.

Finally, I removed the includes of raylib and raygui inside core.c to place them in the main program, not in the dll (so inside main.c).

// inside core.c

#include "core.h"

// Only for Hot Reload
#if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED)
    /* Below are the functions pointers we need to make the bridge
    between the main.c (which contains raylib and raygui)
    and this core.c which will be converted to a dll.*/

    /* To be short, it's to be able to call raylib and raygui
    functions even if we are not linking them as static libraries
    into this dll. */

    // Raylib functions
    void (*raylib_init_window)(int width, int height, const char* title);
    void (*raylib_close_window)();
    bool (*raylib_window_should_close)();
    void (*raylib_begin_drawing)();
    void (*raylib_end_drawing)();
    void (*raylib_clear_background)(Color color);

    // Raygui functions
    bool (*raygui_gui_button)(Rectangle bounds, const char *text);
    void (*raygui_gui_set_style)(int control, int property, int value);

    // This function will be one of the first one to be load 
    // from this dll in main.c. It's role is actually to initialize
    // our raylib and raygui functions pointers, to create the bridge.
    CORE void core_load_raylib_functions(
        void (*const in_init_window)(int width, int height, const char* title),
        void (*const in_close_window)(),
        bool (*const in_window_should_close)(),
        void (*const in_begin_drawing)(),
        void (*const in_end_drawing)(),
        void (*const in_clear_background)(Color color),
        bool (*const in_gui_button)(Rectangle bounds, const char *text),
        void (*const in_gui_set_style)(int control, int property, int value)
    ){
        // Raylib functions
        raylib_init_window = in_init_window;
        raylib_close_window = in_close_window;
        raylib_window_should_close = in_window_should_close;
        raylib_begin_drawing = in_begin_drawing;
        raylib_end_drawing = in_end_drawing;
        raylib_clear_background = in_clear_background;

        // Raygui functions
        raygui_gui_button = in_gui_button;
        raygui_gui_set_style = in_gui_set_style;
    }
#else
    // I redefine the raylib and raygui functions
    // I use here, because whenever we are using the dll
    // or not, I want to be able to have one single code,
    // without #if/#else in the functions below.
    
    // Raylib functions
    #define raylib_init_window InitWindow
    #define raylib_close_window CloseWindow
    #define raylib_window_should_close WindowShouldClose
    #define raylib_begin_drawing BeginDrawing
    #define raylib_end_drawing EndDrawing
    #define raylib_clear_background ClearBackground

    // Raygui functions
    #define raygui_gui_button GuiButton
    #define raygui_gui_set_style GuiSetStyle
#endif

void core_create_window(
    const unsigned short in_width,
    const unsigned short in_height,
    const char* in_title
){
    raylib_init_window(in_width, in_height, in_title);
}

void core_execute_loop(){
    raylib_begin_drawing();
        raylib_clear_background(RED);
        if(raygui_gui_button((Rectangle){ 500, 200, 250, 60 }, "TEST BUTTON")) {
            puts("Button pressed\n");
        }
    raylib_end_drawing();
}

bool core_window_should_close(){
    return raylib_window_should_close();
}

void core_close_window(){
    raylib_close_window();
}
// inside main.c

#include "core.h"

// Whenever we will pass this option to the compiler, we will be able to use our core.c code as a dll
#if defined(CORE_USE_LIBTYPE_SHARED) 
    #include <libloaderapi.h>
    #include "raylib.h"
    #include "raygui.h"
#endif

int main(){
    // This code is necessary only if we are using hot reload
    #if defined(CORE_USE_LIBTYPE_SHARED)
        HINSTANCE dll_handle = NULL; // Declare a pointer to the dll resource

        // We need to initialize some function pointers, because, 
        // as mentioned before, by using a dll, we will load every functions
        // we need to use from raylib, to our dll, (think of it as a bridge), 
        // because our dll is not aware of raylib. 
        // We are not linking its static library in our dll file.
        void (*core_load_raylib_functions_func)(
            void (*const in_init_window)(int width, int height, const char* title),
            void (*const in_close_window)(),
            void (*const in_begin_drawing)(),
            void (*const in_end_drawing)(),
            void (*const in_clear_background)(Color color),
            int (*const in_get_key_pressed)(),
            bool (*const in_is_key_down)(int key),
            bool (*const in_gui_button)(Rectangle bounds, const char *text),
        );

        // Functions pointers to be able to store and call functions from 
        // our dll file (the initialization of the window, the exec loop function...)
        void (*core_init_window_func)();
        void (*core_execute_loop_func)();
        void (*core_exit_func)();

        // Load our core.dll file as a dynamic library.
        // Regarding to the official windows documentation, the second parameter of 'LoadLibraryExA'
        // is always NULL (just here for future need), and the last one corresponds to a flag
        // which determine the action to be taken when loading the module. In our case, we use the 
        // default action (so the flag is 0)
        dll_handle = LoadLibraryExA("./core.dll", NULL, 0);
        if(dll_handle != NULL){
            // Load the 'core_load_raylib_functions' function from the dll into the
            // 'core_load_raylib_functions_func' function pointer
            core_load_raylib_functions_func = (void*) GetProcAddress(dll_handle, "core_load_raylib_functions");
            if (NULL == core_load_raylib_functions_func){
                printf("Can't call core_load_raylib_functions dll function");
                exit(1);
            }else{
                // If the function is correclty loaded, then we call it, 
                // and we pass the functions from raylib and raygui
                // we need.
                // It's the bridge between our main.c and our dll, to 
                // retrieve the raylib and raygui functions.
                core_load_raylib_functions_func(
                    &InitWindow,
                    &CloseWindow,
                    &BeginDrawing,
                    &EndDrawing,
                    &ClearBackground,
                    &GuiButton,
                    &GuiSetStyle
                );
            }
            core_init_window_func = (void*) GetProcAddress(dll_handle, "core_init_window");
            if (NULL == core_init_window_func){
                printf("Can't call core_init_window dll function");
                exit(1);
            }else{
                // If the 'core_init_window' function is correclty
                // loaded from the dll, we call it right now to
                // initialize the window.
                core_init_window_func();
            }
            core_execute_loop_func = (void*) GetProcAddress(dll_handle, "core_execute_loop");
            if (NULL == core_execute_loop_func){
                printf("Can't call core_execute_loop dll function");
                exit(1);
            }
            core_exit_func = (void*) GetProcAddress(dll_handle, "core_exit");
            if (NULL == core_exit_func){
                printf("Can't call core_execute_loop dll function");
                exit(1);
            }
        }else{
            // A problem occured when trying to load the dll file.
            // The most common mistake is a wrong given path to
            // the dll file.
            printf("Can't load the dll file.\n");
            exit(1);
        }
    #else
        // If we are not using the dll configuration, 
        // then we just need to call the 'core_init_window' function
        // from core.c
        core_init_window();
    #endif

    // Main loop
    while(1){
        #if defined(CORE_USE_LIBTYPE_SHARED)
            // We call the function loaded from the dll
            core_execute_loop_func();
        #else
            // If we are not using the dll configuration, 
            // we direclty call the function from core.c 
            core_exec_loop();
        #endif
    }
    #if defined(CORE_USE_LIBTYPE_SHARED)
        core_exit_func();
        // Important line here, we need to free the dll
        FreeLibrary(dll_handle);
    #else
        // If we are not using the dll configuration,
        // simply call the 'core_exit' function from core.c
        core_exit();
    #endif
    return 0;
}

Okay, now it’s done, we are able to compile it by using the dll file.

To do so, here are the commands we need :

// Compiling the core.c file into an object file 
// (the option USE_CORE_LIB_SHARED is passed to be able to have proper functions 
// which can be called from a dll file)
gcc -c core.c -I./include -DUSE_CORE_LIB_SHARED -o core.o

// Assembling the core.o object file into a dll file
gcc -shared core.o -o core.dll

Then, we can compile our main.c file, and create the executable:

// Compiling the full project to get the executable file
gcc main.c -I./include -L./libs -lraygui -lglfw3 -lraylib -lopengl32 -lgdi32 -o main.exe

As you probably noticed, we removed the core.o file from this command, because we are not using it anymore (we actually are loading and using at runtime the dll file which was assembled from this core.o file).

And now, if we run the application:

./main.exe

We got the exact same result as before. So our dll has correclty been loaded, and everything is properly working!

But, we are not able to load our code at runtime yet. We don’t have hot reload available so far.

I think you probably can figure out what we need to do to be able to do it now.
Personnaly, I like to be able to press on a key to pause the program, make some changes, recompile the dll file, and just press enter in the terminal to continue the program with my changes loaded on the fly.

To do so, we need to make an other bridge function between our core.c (dll) and main.c file (main program), to be able to know when the touch is pressed, and tell the main loop that we need to pause the program until we make our changes.

// inside core.c

#include "core.h"

// Only for Hot Reload
#if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED)
    /* Below are the functions pointers we need to make the bridge
    between the main.c (which contains raylib and raygui)
    and this core.c which will be converted to a dll.*/

    /* To be short, it's to be able to call raylib and raygui
    functions even if we are not linking them as static libraries
    into this dll. */

    // Raylib functions
    void (*raylib_init_window)(int width, int height, const char* title);
    void (*raylib_close_window)();
    bool (*raylib_window_should_close)();
    void (*raylib_begin_drawing)();
    void (*raylib_end_drawing)();
    void (*raylib_clear_background)(Color color);

    // Raygui functions
    bool (*raygui_gui_button)(Rectangle bounds, const char *text);
    void (*raygui_gui_set_style)(int control, int property, int value);

    // This function will be one of the first one to be load 
    // from this dll in main.c. It's role is actually to initialize
    // our raylib and raygui functions pointers, to create the bridge.
    CORE void core_load_raylib_functions(
        void (*const in_init_window)(int width, int height, const char* title),
        void (*const in_close_window)(),
        bool (*const in_window_should_close)(),
        void (*const in_begin_drawing)(),
        void (*const in_end_drawing)(),
        void (*const in_clear_background)(Color color),
        bool (*const in_gui_button)(Rectangle bounds, const char *text),
        void (*const in_gui_set_style)(int control, int property, int value)
    ){
        // Raylib functions
        raylib_init_window = in_init_window;
        raylib_close_window = in_close_window;
        raylib_window_should_close = in_window_should_close;
        raylib_begin_drawing = in_begin_drawing;
        raylib_end_drawing = in_end_drawing;
        raylib_clear_background = in_clear_background;

        // Raygui functions
        raygui_gui_button = in_gui_button;
        raygui_gui_set_style = in_gui_set_style;
    }

    // This variable determines if we need to activate hot reload
    // function or not from the main loop, inside main.c
    // That could be a bool, of course, but I'm not including
    // stdbool.h. So, 0 if false, 1 if true.
    char core_active_hot_reload = 0;
    CORE void core_get_value_hot_reload(char* out_index){
        *out_index = core_active_hot_reload;
    }
#else
    // I redefine the raylib and raygui functions
    // I use here, because whenever we are using the dll
    // or not, I want to be able to have one single code,
    // without #if/#else in the functions below.
    
    // Raylib functions
    #define raylib_init_window InitWindow
    #define raylib_close_window CloseWindow
    #define raylib_window_should_close WindowShouldClose
    #define raylib_begin_drawing BeginDrawing
    #define raylib_end_drawing EndDrawing
    #define raylib_clear_background ClearBackground

    // Raygui functions
    #define raygui_gui_button GuiButton
    #define raygui_gui_set_style GuiSetStyle
#endif

void core_create_window(
    const unsigned short in_width,
    const unsigned short in_height,
    const char* in_title
){
    raylib_init_window(in_width, in_height, in_title);
}

void core_execute_loop(){
    // Only for Hot Reload
    #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED)
        unsigned int key_pressed = raylib_get_key_pressed();
        // In my case, if the key pressed is the 'R', I perform
        // hot reload
        if(key_pressed == 114){
            core_active_hot_reload = 1;
        }
    #endif

    raylib_begin_drawing();
        raylib_clear_background(RED);
        if(raygui_gui_button((Rectangle){ 500, 200, 250, 60 }, "TEST BUTTON")) {
            puts("Button pressed\n");
        }
    raylib_end_drawing();
}

bool core_window_should_close(){
    return raylib_window_should_close();
}

void core_close_window(){
    raylib_close_window();
}

So now, in every single loop of the main loop, inside main.c, we just need to call this core_get_value_hot_reload, and if the value returned is 1 (= true), we need to write code to free the library, wait for enter key beeing pressed, reload the dll, and continue the loop!

Of course, to do so, we need to initialize a new function pointer to load this core_get_value_hot_reload at the begining of the program, or we won’t be able to call it from the main loop.

In fact, we don’t need to reload every single function from core.c when reloading the dll. For example, we don’t need our core_init_window function, because the window is already initialized. We just need functions that will be used during the main loop, or at the end of our program (core_exit for example).

// inside main.c

#include "core.h"

// Whenever we will pass this option to the compiler, we will be able to use our core.c code as a dll
#if defined(CORE_USE_LIBTYPE_SHARED) 
    #include <libloaderapi.h>
    #include "raylib.h"
    #include "raygui.h"
#endif

int main(){
    // This code is necessary only if we are using hot reload
    #if defined(CORE_USE_LIBTYPE_SHARED)
        HINSTANCE dll_handle = NULL; // Declare a pointer to the dll resource

        // We need to initialize some function pointers, because, 
        // as mentioned before, by using a dll, we will load every functions
        // we need to use from raylib, to our dll, (think of it as a bridge), 
        // because our dll is not aware of raylib. 
        // We are not linking its static library in our dll file.
        void (*core_load_raylib_functions_func)(
            void (*const in_init_window)(int width, int height, const char* title),
            void (*const in_close_window)(),
            void (*const in_begin_drawing)(),
            void (*const in_end_drawing)(),
            void (*const in_clear_background)(Color color),
            int (*const in_get_key_pressed)(),
            bool (*const in_is_key_down)(int key),
            bool (*const in_gui_button)(Rectangle bounds, const char *text),
        );

        // Functions pointers to be able to store and call functions from 
        // our dll file (the initialization of the window, the exec loop function...)
        void (*core_init_window_func)();
        void (*core_execute_loop_func)();
        void (*core_get_hot_reload_func)(char* out_index);
        void (*core_exit_func)();
 
        // To be able to know if we need to perform hot reload
        // (0 = false, 1 = true)
        char activate_hot_reload = 0;

        // Load our core.dll file as a dynamic library.
        // Regarding to the official windows documentation, the second parameter of 'LoadLibraryExA'
        // is always NULL (just here for future need), and the last one corresponds to a flag
        // which determine the action to be taken when loading the module. In our case, we use the 
        // default action (so the flag is 0)
        dll_handle = LoadLibraryExA("./core.dll", NULL, 0);
        if(dll_handle != NULL){
            // Load the 'core_load_raylib_functions' function from the dll into the
            // 'core_load_raylib_functions_func' function pointer
            core_load_raylib_functions_func = (void*) GetProcAddress(dll_handle, "core_load_raylib_functions");
            if (NULL == core_load_raylib_functions_func){
                printf("Can't call core_load_raylib_functions dll function");
                exit(1);
            }else{
                // If the function is correclty loaded, then we call it, 
                // and we pass the functions from raylib and raygui
                // we need.
                // It's the bridge between our main.c and our dll, to 
                // retrieve the raylib and raygui functions.
                core_load_raylib_functions_func(
                    &InitWindow,
                    &CloseWindow,
                    &BeginDrawing,
                    &EndDrawing,
                    &ClearBackground,
                    &GuiButton,
                    &GuiSetStyle
                );
            }
            core_init_window_func = (void*) GetProcAddress(dll_handle, "core_init_window");
            if (NULL == core_init_window_func){
                printf("Can't call core_init_window dll function");
                exit(1);
            }else{
                // If the 'core_init_window' function is correclty
                // loaded from the dll, we call it right now to
                // initialize the window.
                core_init_window_func();
            }
            core_execute_loop_func = (void*) GetProcAddress(dll_handle, "core_execute_loop");
            if (NULL == core_execute_loop_func){
                printf("Can't call core_execute_loop dll function");
                exit(1);
            }
            core_get_hot_reload_func = (void*) GetProcAddress(dll_handle, "core_get_hot_reload");
            if(NULL == core_get_hot_reload_func){
                printf("Can't call core_get_hot_reload dll function");
                exit(1);
            }
            core_exit_func = (void*) GetProcAddress(dll_handle, "core_exit");
            if (NULL == core_exit_func){
                printf("Can't call core_execute_loop dll function");
                exit(1);
            }
        }else{
            // A problem occured when trying to load the dll file.
            // The most common mistake is a wrong given path to
            // the dll file.
            printf("Can't load the dll file.\n");
            exit(1);
        }
    #else
        // If we are not using the dll configuration, 
        // then we just need to call the 'core_init_window' function
        // from core.c
        core_init_window();
    #endif

    // Main loop
    while(1){
        #if defined(CORE_USE_LIBTYPE_SHARED)
            // We call the function loaded from the dll
            core_execute_loop_func();
            core_get_hot_reload_func(&activate_hot_reload);
            if(activate_hot_reload == 1){
                // First, we free the library
                FreeLibrary(dll_handle);
                // Then, we demand to the user to press a touch to continue 
                // (enter key, on the terminal which runs the main program)
                char c;
                puts("[INFO] Press enter after rebuilding the functions dll file\n");
                // While the user doesn't pres any key, the scanf "block" the program
                scanf("%c", &c);
                
                // If the user press enter, we need to reload the dll file, and the functions we need
                dll_handle = LoadLibraryExA("core.dll", NULL, 0);
                if (NULL != dll_handle){
                    core_load_raylib_functions_func = (void*) GetProcAddress(dll_handle, "core_load_raylib_functions");
                    if (NULL == core_load_raylib_functions_func){
                        printf("Can't call core_load_raylib_functions dll function");
                        exit(1);
                    }else{
                        core_load_raylib_functions_func(
                            &InitWindow,
                            &CloseWindow,
                            &BeginDrawing,
                            &EndDrawing,
                            &ClearBackground,
                            &GuiButton,
                            &GuiSetStyle
                        );
                    }
                    core_execute_loop_func = (void*) GetProcAddress(dll_handle, "core_execute_loop");
                    if (NULL == core_execute_loop_func){
                        printf("Can't call core_execute_loop dll function");
                        exit(1);
                    }
                    core_get_hot_reload_func = (void*) GetProcAddress(dll_handle, "core_get_hot_reload");
                    if(NULL == core_get_hot_reload_func){
                        printf("Can't call core_get_hot_reload dll function");
                        exit(1);
                    }
                    core_exit_func = (void*) GetProcAddress(dll_handle, "core_exit");
                    if (NULL == core_exit_func){
                        printf("Can't call core_execute_loop dll function");
                        exit(1);
                    }
                }else{
                    printf("Can't load the dll file.\n");
                    exit(1);
                }
            }
        #else
            // If we are not using the dll configuration, 
            // we direclty call the function from core.c 
            core_exec_loop();
        #endif
    }
    #if defined(CORE_USE_LIBTYPE_SHARED)
        core_exit_func();
        // Important line here, we need to free the dll
        FreeLibrary(dll_handle);
    #else
        // If we are not using the dll configuration,
        // simply call the 'core_exit' function from core.c
        core_exit();
    #endif
    return 0;
}

And voilà!
Now, we are able to reload our code from core.c on the fly. We just need to press R inside the program window, make our changes, recompile our dll with the commands listed below, then press enter in the terminal which has launched the main program, and see our changes in realtime!

Before ending this long article, I just wanted to write this little warning: if you want to retrieve the exact same state of your program after hot reload, you need to create (an) other bridge(s) function(s) between the main program (main.c), and your dll (core.c in our case), to retrieve every single variable which you instanciated on the stack, on the dll part.

Because when you reload the dll file, the stack is reinitialized, so your variables will be reset to their default value.

But you don’t need to do so for variables you allocated on the heap. If you didn’t destroy them before hot reload, they always be available, until you close the main application.

If you want, in an other article, I could show you how to be even more productive by « automating » hot reload with your code editor (in my case : visual studio code). Don’t hesitate to tell me in the comments of this article if that could interest you!

Anyway, if you’ve understood and applyed what I shared with you in this article, you normally will end with something really cool like that (no need to relaunch the application anymore):

hot reload in action

 

Conclusion

 

I hope this article has been usefull to you, and you’ll be able to add hot reload to your program/game. It’s a really time savier feature to use while writing your application!

If you encounter any problem, feel free to ask for help in the comments below. I’ll be more than happy to help you!

In any case, thank you really much for your patience, and to have read this article until the end.

Have fun coding games, tools, or whatever you have in mind!

Victor Gallet

Victor Gallet

Étudiant programmeur jeu vidéo. J'aime par dessus tout apprendre, et je suis un éternel curieux de tout. Mon principal but dans la vie est d’être une meilleure personne, et de partager mes (faibles) connaissances avec les autres.

2 Comments

  • Michael C dit :

    Good stuff! Automated reloading of my game DLL was the first feature I added to my engine. I can’t imagine working without it. It’s especially easy to do this when you have one main entry point to the game and all of the platform code is outside of the DLL. You can then setup a platform API struct containing function pointers and the game can use that without having to rebind anything after a reload.

    Another indispensable feature is hotloading asset changes at runtime. This is so useful for iterating on a shader or modifying a texture in some art tool and immediately seeing how it looks in the game.

    • Victor Gallet dit :

      Hi Michael, thank you for your comment!
      Yes, I totally agree with you, hot reload with dll (or with .so files on linux for exampe) is something that you can’t work without after tried it.
      And I must say that for the hotloading part, to me, my shaders (I have very few of them in fact for now, still learning glsl ^^) are simple strings. So whenever I hot reload the game (now, I’ve a better way of doing it, by just pressing f5 on visual studio code), I’m able to see the changes.
      So for now, I’m okay with that way of doing it. But it’s mainly because I’m just dealing with fragment shader (I’m making a 2d game), not with vertex shader…

      So anyway, thank you again for your comment, and I’m really interested in what you are working on right now (and your past programs/games).
      If you can share some pictures, that would be awsome!

Leave a Reply

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.