Files
stk/README.md
T

280 lines
7.7 KiB
Markdown

# stk (Stalwart Toolkit)
[![License: MPL 2.0](https://img.shields.io/badge/License-MPL_2.0-brightgreen.svg)](https://opensource.org/licenses/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](docs/design.md) for the full design philosophy and roadmap.
---
## Quick Start
### Building
```bash
# Unix (Linux/BSD/macOS)
./build.sh debug release
# Windows
build.bat debug release
```
### Installation
#### Unix (Linux/BSD/macOS)
```bash
./build.sh install
```
Installs to `/usr/local` by default. Use `PREFIX` to customize:
```bash
./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
```c
#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:
```c
/* 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:
```bash
# 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:
```c
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] }`.
```c
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
```c
/* 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.9
### 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](CHANGELOG.md) for detailed release notes.
---
## Testing
Run the included test suite:
```bash
./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.