Hello, I wanted to address the build architecture of this project. Given that this is a multiplayer game, there will be a server and client program. I absolutely want both of these programs to be using the same code. The only difference between these two programs will be the fact that one will be presenting (rendering) to the screen while the other will just be a standard console application (which you will be able to send commands to via sockets / network).

Structure

To achieve this I first separated the project into 3 main directories:

  • common
  • renderer
  • game

In the common directory, there are all the tools / utilities that the other two high level modules use such as containers, typedefs and the event system.

The renderer module holds everything that has to do with user interfacing - 3D graphics, UI, audio, surface (window) creation, …

And finally the game module holds just the game logic (and also the code needed to render the game, calling functions from the renderer module as if it were to be a sort of API).

common and renderer get compiled into static libraries and the game module will be used to compile into two different executables: vkPhysics_client and vkPhysics_server. The client program gets linked with the renderer and the server application does not.

Now the thing is that the server program still contains code that calls stuff from the renderer module. However, it also compiles with a preprocessor definition: LINK_AGAINST_RENDERER which basically makes it so that in the headers which declare renderer functions, it just adds an inline definition with an empty body.

#if LINK_AGAINST_RENDERER
#define DECLARE_RENDERER_PROC(return_type, name, ...)   \
    return_type name(__VA_ARGS__)
#define DECLARE_VOID_RENDERER_PROC(return_type, name, ...)   \
    return_type name(__VA_ARGS__)
#else
#define DECLARE_RENDERER_PROC(return_type, name, ...)   \
    inline return_type name(__VA_ARGS__) { return (return_type){}; }
#define DECLARE_VOID_RENDERER_PROC(return_type, name, ...)   \
    inline return_type name(__VA_ARGS__) {}
#endif

// ...

DECLARE_RENDERER_PROC(gpu_buffer_t, create_gpu_buffer,
    uint32_t size,
    void *data,
    VkBufferUsageFlags usage);
    
// ...

And voila! no compile errors / link errors! Two different programs!

One thing that will need to be in mind will be having to structure the game in such a way that it’s easy to run the game without any rendering whatsoever.