Skip to content

anth64/stk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

156 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

stk (Stalwart Toolkit)

License: MPL 2.0

stk is a lightweight, modular toolkit for building games and game engines. It provides a portable foundation for dynamically loading modules, native or WASM, without enforcing any architecture or design choices.

It is designed to run on modern systems running POSIX and Windows using C89.


Key Features

  • Dynamic module loading (native .so / .dll / .dylib)
  • Hot-swapping of modules at runtime
  • Cross-platform (Linux, BSD, Windows, macOS)
  • Optional WASM support for multi-language modules (planned)
  • Developer tools: lightweight metadata, logging/tracing, and dependency management
  • Minimal, portable API

Philosophy

stk is non-opinionated: developers control architecture, engine design, and game logic while relying on a predictable, lean foundation.

See docs/design.md for the full design philosophy and roadmap.


Quick Start

Building

# Unix (Linux/BSD/macOS)
./build.sh debug release

# Windows
build.bat debug release

Installation

Unix (Linux/BSD/macOS)

./build.sh install

Installs to /usr on Linux, /usr/local on BSD/macOS by default. Use PREFIX to customize:

./build.sh PREFIX=$HOME/.local install

Windows

build.bat release
  • Once finished building, copy the headers from include/ to your_project/include/stk/ and copy bin/release/stk.dll or bin/release/stk.lib to your project's lib directory, depending on whether you are linking dynamically or statically.

Usage

Basic Example

#include <stk/stk.h>
#include <stdio.h>

int main(void)
{
    int running = 1;
    size_t events;
    
    /* Initialize stk */
    if (stk_init() != STK_INIT_SUCCESS) {
        fprintf(stderr, "Failed to initialize stk\n");
        return 1;
    }

    /* Main loop - poll for module changes */
    while (running) {
        events = stk_poll();
        if (events > 0) {
            printf("Detected %zu module event(s)\n", events);
        }
        
        /* Your game/application logic here */
    }

    /* Shutdown stk systems*/
    stk_shutdown();
    return 0;
}

Creating a Module

Modules are simple shared libraries with stk_mod_init and stk_mod_shutdown functions:

/* my_module.c */
#include <stdio.h>

int stk_mod_init(void)
{
    printf("Module loaded!\n");
    return 0;  /* Return 0 on success */
}

void stk_mod_shutdown(void)
{
    printf("Module unloaded!\n");
}

Build as a shared library:

# Linux/BSD
cc -shared -fPIC -o my_module.so my_module.c

# macOS
cc -shared -fPIC -o my_module.dylib my_module.c

# Windows (MSVC)
cl /LD my_module.c /Fe:my_module.dll

# Windows (MinGW)
cc -shared -o my_module.dll my_module.c

Place the compiled module in the mods/ directory (default), and stk will automatically load it and watch for changes.

Module Metadata

Modules can optionally export metadata functions:

const char *stk_mod_name(void)        { return "My Module"; }
const char *stk_mod_version(void)     { return "1.2.0"; }
const char *stk_mod_description(void) { return "Does something useful"; }

All three are optional. Version defaults to 0.0.0 if not exported or unparseable.

Declaring Dependencies

Modules declare dependencies via an exported sentinel-terminated array. No stk headers are required in the module. The only requirement is that the memory layout matches: { char[64], char[32] }.

typedef struct {
    char id[64];
    char version[32];
} dep_t;

dep_t stk_mod_deps[] = {
    { "physics", ">=2.0.0" },
    { "renderer", "^1.0.0" },
    { "", "" }  /* sentinel */
};

Version constraint operators:

  • =1.0.0 exact match
  • >=2.0.0 minimum version, also the default if no operator is specified
  • ^1.0.0 same major, greater or equal minor/patch

If a dependency is removed at runtime, all affected modules are unloaded and queued. When the dependency comes back, they load automatically.

Configuration

/* Set custom module directory (default: "mods") */
stk_set_mod_dir("custom_mods");

/* Set custom temp directory name (default: ".tmp") */
stk_set_tmp_dir_name(".my_tmp");

/* Set custom init function name (default: "stk_mod_init") */
stk_set_module_init_fn("my_init");

/* Set custom shutdown function name (default: "stk_mod_shutdown") */
stk_set_module_shutdown_fn("my_shutdown");

/* Set function name to get module name (default: "stk_mod_name") */
stk_set_module_name_fn("my_mod_name");

/* Set function name to get module version (default: "stk_mod_version") */
stk_set_module_version_fn("my_mod_version");

/* Set function name to get module description (default: "stk_mod_description") */
stk_set_module_description_fn("my_mod_description");

/* Set deps array symbol name (default: "stk_mod_deps") */
stk_set_module_deps_sym("my_mod_deps");

/*
 * All the above functions must be called before stk_init()
 * if the defaults need to be changed.
 */
stk_init();

API Reference

Initialization

  • unsigned char stk_init(void) - Initialize stk, returns STK_INIT_SUCCESS on success
  • void stk_shutdown(void) - Shutdown and cleanup all modules

Runtime

  • size_t stk_poll(void) - Poll for module changes, returns number of events processed
  • size_t stk_module_count(void) - Get number of currently loaded modules

Configuration

  • void stk_set_mod_dir(const char *path) - Set module directory
  • void stk_set_tmp_dir_name(const char *name) - Set temp directory name
  • void stk_set_module_init_fn(const char *name) - Set module init function name
  • void stk_set_module_shutdown_fn(const char *name) - Set module shutdown function name
  • void stk_set_module_name_fn(const char *name); - Set module name function name
  • void stk_set_module_version_fn(const char *name); - Set module version function name
  • void stk_set_module_description_fn(const char *name); - Set module description function name
  • void stk_set_module_deps_sym(const char *name) - Set module deps array symbol name (default: stk_mod_deps)

Logging

  • void stk_set_logging_enabled(unsigned char enabled) - Enable/disable all logging
  • unsigned char stk_is_logging_enabled(void) - Query logging state
  • void stk_set_log_output(FILE *fp) - Set log output stream (default: stdout, NULL disables)
  • void stk_set_log_prefix(const char *prefix) - Set log prefix (default: "stk")
  • void stk_set_log_level(stk_log_level_t level) - Set minimum log level (default: INFO)

Log Levels: STK_LOG_ERROR, STK_LOG_WARN, STK_LOG_INFO, STK_LOG_DEBUG


Project Status

Current Version: 1.0.0-pre.11

What Works

  • Cross-platform module loading and hot-reloading
  • File watching (inotify/kqueue/FindFirstFile)
  • Robust hot-reload even during extremely rapid file changes
  • Enhanced logging with levels, timestamps, and filtering
  • Runtime-configurable logging behavior
  • Optional module metadata (name, version, description)
  • Dependency declaration, validation, and versioning
  • Detailed dependency failure logging (missing ids, version mismatches)
  • Cascade unload when dependencies are removed
  • Pending queue with automatic retry when deps become available
  • Topological sort with cycle detection

Phase 2

  • WASM module support

See CHANGELOG.md for detailed release notes.


Testing

Run the included test suite:

./build.sh test    # Unix
build.bat test     # Windows

The test will watch the mods/ directory and report when modules are loaded, reloaded, or unloaded.


License

Mozilla Public License 2.0 (MPL-2.0)


Contributing

Contributions welcome! Please ensure code follows C89 standard and works across all supported platforms.

About

A lightweight, portable, modular toolkit for building games and game engines.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages