Home

Awesome

<div align="center"> <h1>Cvars</h1> <i>Configuration variables .rs</i> <br /> A simple and ergonomic way to store and edit configuration at runtime </div> <br /> <!-- When updating this, don't forget each console has its own readme too. -->

GitHub Docs.rs Crates.io License (AGPLv3) CI Audit Dependency status Crates.io Discord

<!-- ![Total lines](https://tokei.rs/b1/github/martin-t/cvars) Disabled because it counts cvars-bench-compile-time, plus it's currently broken and always shows 0 anyway. -->

Cvars (console variables or configuration variables) are a simple way to store settings you want to change at runtime without restarting your program.

Consoles are the most ergonomic way to set cvars but you can write your own UI or read them from stdin if you want. They are available for Fyrox and Macroquad.

These crates are inspired by the idTech (Doom, Quake) and Source family of game engines but they can be useful outside games. Cvars allow you to iterate faster by letting you test certain gameplay changes without recompiling. They also make your game more moddable if you expose (a subset of) them to players.

TL;DR: Set and get struct fields based on the field's name as a string. User writes the cvar's name and new value into the console, it sets the appropriate field in your config struct and the game now behaves differently. Your gamecode uses cvars as regular staticly typed values.

<!-- To avoid keeping a large file in the repo forever, this video was uploaded to a dummy github issue. AFAIK the issue doesn't even need to be submitted and it'll still be hosted forever. -->

<a href="https://user-images.githubusercontent.com/4079823/152082630-a705286d-c630-4507-9213-b8a7b106d47e.mp4">Usage example video worth 15*1000 words per second</a>

Zero boilerplate - there are no traits to implement manually and no setup code to call per cvar.

Minimal performance cost - just struct field access instead of a hardcoded constant. Cvars are cheap enough to keep everything configurable even after you're done finding the best values - you can keep things tweakable in your released game for players to experiment themselves.

Usage

cargo add cvars
use cvars::cvars;

// This generates a Cvars struct containing all your config options
// and a corresponding Default impl.
cvars! {
    g_rocket_launcher_ammo_max: i32 = 20,
    g_rocket_launcher_damage: f32 = 100.0,
}

// Store this in your game state.
let mut cvars = Cvars::default();
// These normally come from the user
// (from stdin / your game's console / etc.)
let cvar_name = "g_rocket_launcher_damage";
let new_value = "150";

// This looks up the right field and sets it to the new value.
cvars.set_str(cvar_name, new_value).unwrap();

Motivation

A player/modder/gamedev wants rockets to do more damage. He types g_rocket_launcher_damage 150 into the game's console or stdin. The code gets both the cvar name and new value as strings so you can't write cvars.g_rocket_launcher_damage = 150. You need to look up the correct field based on the string - this is what cvars does - it generates set_str (and some other useful methods). You call cvars.set_str("g_rocket_launcher_damage", "150"); which looks up the right field, parses the value into its type and updates the field with it. From then on, rockets do 150 damage.

The important thing is that in the rest of your application, you can still access your cvars as regular struct fields - e.g. player.health -= cvars.g_rocket_launcher_damage;. This means you only need to use strings when the user (player or developer when debugging or testing a different balance) is changing the values. The rest of your gamelogic is still statically typed and using a cvar in gamecode is just a field access without any overhead.

A typical game will have hundreds or thousands of tunable parameters. With cvars and a console you can keep them all configurable for advanced players, modders and your-gamedev-self without having a build an elaborate settings menu. You can keep everything configurable using a TUI while also exposing common settings to normal players in your game's GUI.

See cvars/examples/stdin.rs for a small runnable example.

Real-world examples

Look at games using cvars:

Press ; to open the console. Shift+ESC also works in the native clients but not in the browser.

Fyrox console

GitHub Docs.rs Crates.io License (AGPL3) CI Audit Dependency status Discord

The Fyrox console is a separate crate in this repo. To use it in your game, add it to your Cargo.toml and call its methods on the relevant engine events.

Fyrox console

See the crates.io page or its docs for more information.

Macroquad console

GitHub Docs.rs Crates.io License (AGPL3) CI Audit Dependency status Discord

The Macroquad console is a separate crate in this repo. To use it in your game, add it to your Cargo.toml and call its update method every frame.

Macroquad console

See the crates.io page or its docs for more information.

Features

Features I am currently not planning to implement myself but would be nice to have. I might accept a PR if it's clean and maintainable but it's probably better if you implement them in your own crate:

Alternatives

Compared to these, cvars either has no overhead at runtime or requires less setup code. The downside currently might be slightly increased incremental compile times (by hundreds of milliseconds).

Cvars also serves a slightly different purpose than inline_tweak and const-tweaker. It's meant to stay in code forever, even after releasing your game, to enable modding by your game's community.

Finally, you might not need a specialized crate like cvars or inline_tweak at all. A lot of common wisdom in gamedev is wrong or outdated. What you need might be just a file containing RON or JSON which is loaded each frame and deserialized into a config struct. It'll be cached by the OS most of the time and nobody minds a dropped frame during development after editing the file.

Development

The repo is organized as a cargo workspace for the main functionality, with consoles and benchmarks as separate crates not part of the workspace - see Cargo.toml for the technical reasons.

Lessons Learned

Contributing

You can always find me on the Rusty Games Discord server if you have any questions or suggestions.

Issues and Pull Requests are welcome. See the good first issue label for easy tasks.

License

AGPL-v3 or newer