Skip to content

Schwungus/caulk

Repository files navigation

caulk

A splatter of caulk paste

caulk is an up-to-date, functional, NON-DEPRECATED wrapper for Valve's Steamworks API for use with plain C instead of C++ as intended. Capische?

Refer to the usage section and the code example for a quick rundown.

✔️ Schwungus-certified.

Rationale

The Steamworks SDK provides the header steam_api_flat.h which declares interoperable interface functions. However, it is written in C++, which leads to grotesque build errors if you include it in your plain-C code. This library mitigates that by generating a compatibility layer, gluing C++ classes, structures, functions, and methods defined in the SDK to plain-C code, with the help of steam_api.json, which is a repository of all classes and methods used in Steamworks designed for this specific purpose (thanks Valve!).

Gluing the C++ SDK to C objects nonetheless requires using a C++ linker to produce the final binary. This means you will have to use a C++ toolchain to build your game.

The key takeaways from going on through with this all are twofold:

  1. You get to use Steamworks inside a plain-C game. Doesn't matter whether it's being used for personal amusement or due to technical limitations.
  2. Other programming languages that can interface with C native libraries won't have to reinvent a whole new wrapper generator to bind the C++ Steamworks SDK to their C glue module. caulk reduces the friction of porting Steamworks to other programming languages by a whole step.

Basic usage

caulk requires a ZIP of the Steamworks SDK in your project's root. Click that link to semi-legally download it.

caulk uses CMake for the build pipeline. Since CMake is the de-facto standard for cross-platform C/C++ compilation, you shouldn't be afraid to use it - here's a CMakeLists.txt example:

cmake_minimum_required(VERSION 3.24.0 FATAL_ERROR)
project(myProject)

# REQUIRED: point this to your Steamworks SDK archive.
set(STEAMWORKS_SDK_ZIP ${CMAKE_SOURCE_DIR}/steamworks_sdk_163.zip)

include(FetchContent)
FetchContent_Declare(caulk
    GIT_REPOSITORY https://github.com/Schwungus/caulk.git
    GIT_TAG <release tag or commit SHA>) # edit this line to your liking
FetchContent_MakeAvailable(caulk)

add_executable(myProject main.c)
target_link_libraries(myProject PRIVATE caulk)
# call this to automatically copy steam_appid.txt and shared library objects after build:
caulk_populate(myProject)

You will also need to include a steam_appid.txt in your project's root. You should use the caulk_populate(targetName) CMake convenience function: it copies steam_appid.txt and steamapi.dll over to the passed target's binary output directory.

To actually use the Steamworks SDK from your C code, add #include <caulk.h> and prefix each Steamworks function call with caulk_:

#include <stdlib.h>
#include <caulk.h>

int main(int argc, char* argv[]) {
    if (!caulk_Init())
        return EXIT_FAILURE;

    /* Do Steamworks stuff here... */

    caulk_Shutdown();
    return EXIT_SUCCESS;
}

Again, see test.c for a more complete example.

The API is designed to be self-documenting. Once you look up a Steamworks object you need to use, calling methods on it is simple: just pass a pointer to your object to a function named caulk_ClassName_MethodName(). "Interface" types from the Steamworks SDK are even easier to use: you don't need to make an object for them; just call caulk_InterfaceName_MethodName()! (The I prefix is absent from InterfaceName in this call signature: e.g. ISteamMatchmaking becomes just SteamMatchmaking.)

Callbacks and call results

If a method returns SteamAPICall_t instead of giving the desired result immediately, it must be an asynchronous call which will spit out a value eventually. To use this value, you will have to define a handler function using caulk_Resolve(); that function will be called by caulk_Dispatch() whenever your result is ready.

Also, you'll be receiving a lot of events from Steamworks. To make use of them, you'll have to register handlers using caulk_Register(). These also rely on calls to caulk_Dispatch() to trigger.

See the example below for both caulk_Resolve() and caulk_Register():

#include <stdlib.h>
#include <caulk.h>

static void resolveCreateLobby(void* pData, bool ioFail) {
    LobbyCreated_t* data = pData;
    if (!ioFail && data->m_eResult == k_EResultOK)
        printf("Created lobby ID=%d!!!\n", data->m_ulSteamIDLobby);
}

static void onEnterLobby(void* pData) {
    LobbyEnter_t* data = pData;
    // Do cool stuff with `data`.
}

int main(int argc, char* argv[]) {
    if (!caulk_Init())
        return EXIT_FAILURE;

    // Call `onEnterLobby()` every time we enter a lobby.
    caulk_Register(LobbyEnter_t_iCallback, onEnterLobby);

    // Let a lobby be created in the background, and run `resolveCreateLobby()` when it's done.
    SteamAPICall_t cb = caulk_SteamMatchmaking_CreateLobby(k_ELobbyTypeFriendsOnly, 2);
    caulk_Resolve(cb, resolveCreateLobby);

    for (;;)
        // Dispatch the registered handlers:
        caulk_Dispatch(); // should put a `Sleep` here or smth...

    caulk_Shutdown();
    return EXIT_SUCCESS;
}

Cross-Compilation

Please note that the compatibility-layer generator compiles to a native binary that has to be run in order for caulk to even compile. This means you cannot (currently) cross-compile the library from scratch (e.g. from Linux targeting Windows), since the resulting generator binary will be a Windows executable that cannot run natively on the builder Linux.

As a workaround, you'll have to use one of the releases, where the glue-code generator is compiled to a cross-platform APE binary. Instruct caulk to use the prebuilt generator binary by changing your FetchContent_Declare block to something along the lines of:

FetchContent_Declare(caulk
    URL https://github.com/Schwungus/caulk/releases/download/rolling/caulk-rolling.tar.gz)
set(CAULK_PREBUILT_GENERATOR ${caulk_SOURCE_DIR}/ape$<IF:$<BOOL:WIN32>,.exe,>)
FetchContent_MakeAvailable(caulk)

About

A plain C wrapper for the Steamworks API

Topics

Resources

License

Stars

Watchers

Forks

Contributors 2

  •  
  •