Quick refactor of the config system to make it easier to refactor config.cpp/hpp later.

This commit is contained in:
Zed A. Shaw 2025-09-16 11:59:30 -04:00
parent 47f659ae8e
commit e523aa8b02
29 changed files with 138 additions and 140 deletions

View file

@ -60,7 +60,7 @@ clean:
meson compile --clean -C builddir
debug_test: build
gdb --nx -x .gdbinit --ex run --ex bt --ex q --args builddir/runtests -e
gdb --nx -x .gdbinit --ex run --ex bt --ex q --args builddir/runtests -e "[lighting]"
win_installer:
powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp'

2
ai.cpp
View file

@ -66,7 +66,7 @@ namespace ai {
void init(std::string config_path) {
if(!initialized) {
Config config(config_path);
auto config = settings::get(config_path);
// profile specifies what keys (bitset indexes) are allowed
// and how they map to the bitset of State

View file

@ -97,8 +97,8 @@ namespace animation {
void init() {
if(!initialized) {
Config animations("assets/animations.json");
Config config("assets/config.json");
auto animations = settings::get("animations");
auto config = settings::get("config");
auto& sprites = config["sprites"];
for(auto& [name, data] : animations.json().items()) {

View file

@ -265,5 +265,8 @@
"text_size": 20,
"label_size": 20,
"font_file_name": "assets/text.otf"
},
"player": {
"hands": "male_hand"
}
}

View file

@ -47,7 +47,7 @@ namespace sfml {
guecs::Theme Backend::theme() {
palette::init();
auto config = Config("assets/config.json")["theme"];
auto config = settings::Config("assets/config.json")["theme"];
guecs::Theme theme {
.BLACK=palette::get("gui/theme:black"),
@ -71,7 +71,7 @@ namespace sfml {
theme.BG_COLOR = palette::get("gui/theme:bg_color");
theme.BORDER_COLOR = palette::get("gui/theme:border_color");
theme.BG_COLOR_DARK = palette::get("gui/theme:bg_color_dark");
theme.FONT_FILE_NAME = Config::path_to(config["font_file_name"]).string();
theme.FONT_FILE_NAME = settings::Config::path_to(config["font_file_name"]).string();
return theme;
}

View file

@ -52,13 +52,13 @@ namespace components {
};
struct GameConfig {
Config game;
Config enemies;
Config items;
Config tiles;
Config devices;
Config bosses;
Config rituals;
settings::Config game;
settings::Config enemies;
settings::Config items;
settings::Config tiles;
settings::Config devices;
settings::Config bosses;
settings::Config rituals;
};
struct Personality {

View file

@ -2,51 +2,66 @@
#include "dbc.hpp"
#include <fmt/core.h>
using nlohmann::json;
using fmt::format;
namespace settings {
using nlohmann::json;
using fmt::format;
std::filesystem::path Config::BASE_DIR{"."};
std::filesystem::path Config::BASE_DIR{"."};
Config::Config(const std::string src_path) : $src_path(src_path) {
auto path_to = Config::path_to($src_path);
dbc::check(std::filesystem::exists(path_to),
fmt::format("requested config file {} doesn't exist", path_to.string()));
std::ifstream infile(path_to);
$config = json::parse(infile);
}
nlohmann::json &Config::operator[](size_t key) {
return $config[key];
}
json &Config::operator[](const std::string &key) {
dbc::check($config.contains(key), fmt::format("ERROR in config, key {} doesn't exist.", key));
return $config[key];
}
std::wstring Config::wstring(const std::string main_key, const std::string sub_key) {
dbc::check($config.contains(main_key), fmt::format("ERROR wstring main/key in config, main_key {} doesn't exist.", main_key));
dbc::check($config[main_key].contains(sub_key), fmt::format("ERROR wstring in config, main_key/key {}/{} doesn't exist.", main_key, sub_key));
const std::string& str_val = $config[main_key][sub_key];
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
return $converter.from_bytes(str_val);
}
std::vector<std::string> Config::keys() {
std::vector<std::string> the_fucking_keys;
for(auto& [key, value] : $config.items()) {
the_fucking_keys.push_back(key);
Config::Config(const std::string src_path) : $src_path(src_path) {
auto path_to = Config::path_to($src_path);
dbc::check(std::filesystem::exists(path_to),
fmt::format("requested config file {} doesn't exist", path_to.string()));
std::ifstream infile(path_to);
$config = json::parse(infile);
}
return the_fucking_keys;
}
nlohmann::json &Config::operator[](size_t key) {
return $config[key];
}
void Config::set_base_dir(const char *optarg) {
Config::BASE_DIR.assign(optarg);
}
json &Config::operator[](const std::string &key) {
dbc::check($config.contains(key), fmt::format("ERROR in config, key {} doesn't exist.", key));
return $config[key];
}
std::filesystem::path Config::path_to(const std::string& path) {
return Config::BASE_DIR / path;
std::wstring Config::wstring(const std::string main_key, const std::string sub_key) {
dbc::check($config.contains(main_key), fmt::format("ERROR wstring main/key in config, main_key {} doesn't exist.", main_key));
dbc::check($config[main_key].contains(sub_key), fmt::format("ERROR wstring in config, main_key/key {}/{} doesn't exist.", main_key, sub_key));
const std::string& str_val = $config[main_key][sub_key];
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
return $converter.from_bytes(str_val);
}
std::vector<std::string> Config::keys() {
std::vector<std::string> the_fucking_keys;
for(auto& [key, value] : $config.items()) {
the_fucking_keys.push_back(key);
}
return the_fucking_keys;
}
void Config::set_base_dir(const char *optarg) {
Config::BASE_DIR.assign(optarg);
}
std::filesystem::path Config::path_to(const std::string& path) {
return Config::BASE_DIR / path;
}
Config get(const std::string& name) {
if(name.ends_with(".json")) {
return {name};
} else {
auto path = Config::BASE_DIR / fmt::format("assets/{}.json", name);
dbc::check(std::filesystem::exists(path), fmt::format(
"config file {} does not exist", path.string()));
return {path.string()};
}
}
}

View file

@ -4,22 +4,26 @@
#include <codecvt>
#include <filesystem>
struct Config {
static std::filesystem::path BASE_DIR;
nlohmann::json $config;
std::string $src_path;
namespace settings {
struct Config {
static std::filesystem::path BASE_DIR;
nlohmann::json $config;
std::string $src_path;
Config(const std::string src_path);
Config(const std::string src_path);
Config(nlohmann::json config, std::string src_path)
: $config(config), $src_path(src_path) {}
Config(nlohmann::json config, std::string src_path)
: $config(config), $src_path(src_path) {}
nlohmann::json &operator[](size_t);
nlohmann::json &operator[](const std::string &key);
nlohmann::json &json() { return $config; };
std::wstring wstring(const std::string main_key, const std::string sub_key);
std::vector<std::string> keys();
nlohmann::json &operator[](size_t);
nlohmann::json &operator[](const std::string &key);
nlohmann::json &json() { return $config; };
std::wstring wstring(const std::string main_key, const std::string sub_key);
std::vector<std::string> keys();
static void set_base_dir(const char *optarg);
static std::filesystem::path path_to(const std::string& path);
};
static void set_base_dir(const char *optarg);
static std::filesystem::path path_to(const std::string& path);
};
Config get(const std::string &name);
}

View file

@ -2,7 +2,6 @@
#include "components.hpp"
#include "worldbuilder.hpp"
#include "constants.hpp"
#include "save.hpp"
#include "systems.hpp"
#include "components.hpp"
#include "rituals.hpp"
@ -21,7 +20,7 @@ inline shared_ptr<DinkyECS::World> clone_load_world(shared_ptr<DinkyECS::World>
auto world = make_shared<DinkyECS::World>();
if(prev_world == nullptr) {
save::load_configs(*world);
GameDB::load_configs(*world);
} else {
prev_world->clone_into(*world);
}
@ -134,4 +133,16 @@ namespace GameDB {
dbc::check(initialized, "Forgot to call GameDB::init()");
return current_level().player;
}
void load_configs(DinkyECS::World &world) {
world.set_the<GameConfig>({
settings::get("config"),
settings::get("enemies"),
settings::get("items"),
settings::get("tiles"),
settings::get("devices"),
settings::get("bosses"),
settings::get("rituals")
});
}
}

View file

@ -12,7 +12,6 @@ namespace components {
struct Position;
}
namespace GameDB {
struct Level {
size_t index;
@ -31,4 +30,6 @@ namespace GameDB {
std::shared_ptr<DinkyECS::World> current_world();
components::Position& player_position();
DinkyECS::Entity the_player();
void load_configs(DinkyECS::World &world);
}

View file

@ -11,12 +11,15 @@ namespace gui {
MainUI::MainUI(sf::RenderWindow& window) :
$window(window),
$rayview(std::make_shared<Raycaster>(RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT)),
$hand(textures::get_sprite("female_hand")),
$hand_anim(animation::load("female_hand"))
$rayview(std::make_shared<Raycaster>(RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT))
{
$window.setVerticalSyncEnabled(VSYNC);
$window.setFramerateLimit(FRAME_LIMIT);
auto config = settings::get("config");
$hand = textures::get_sprite(config["player"]["hands"]);
$hand_anim = animation::load(config["player"]["hands"]);
}
void MainUI::dirty() {
@ -34,7 +37,6 @@ namespace gui {
$overlay_ui.init();
}
void MainUI::render() {
if($needs_render) $rayview->render();
$rayview->draw($window);

View file

@ -14,7 +14,7 @@ int main(int argc, char* argv[]) {
components::init();
sfml::Backend backend;
guecs::init(&backend);
ai::init("assets/ai.json");
ai::init("ai");
animation::init();
GameDB::init();

View file

@ -115,7 +115,6 @@ sources = [
'rand.cpp',
'raycaster.cpp',
'rituals.cpp',
'save.cpp',
'shaders.cpp',
'shiterator.hpp',
'sound.cpp',

View file

@ -21,7 +21,7 @@ namespace palette {
COLOR.initialized = true;
COLOR.config = json_file;
Config config(json_file);
auto config = settings::get(json_file);
json& colors = config.json();
for(auto [key, value_specs] : colors.items()) {

View file

@ -4,7 +4,7 @@
namespace palette {
using std::string;
void init(const std::string &config="assets/palette.json");
void init(const std::string &config="palette");
sf::Color get(const string &key);

View file

@ -52,7 +52,7 @@ namespace ritual {
};
struct Engine {
Config $config;
settings::Config $config;
ai::AIProfile $profile;
std::unordered_map<std::string, ai::Action> $actions;
std::unordered_map<std::string, ai::State> $states;

View file

@ -1,23 +0,0 @@
#include "save.hpp"
#include <fstream>
#include "dbc.hpp"
#include <fmt/core.h>
#include "config.hpp"
#include <filesystem>
using namespace components;
using namespace fmt;
void save::load_configs(DinkyECS::World &world) {
Config game("./assets/config.json");
Config enemies("./assets/enemies.json");
Config items("./assets/items.json");
Config tiles("./assets/tiles.json");
Config devices("./assets/devices.json");
Config bosses("./assets/bosses.json");
Config rituals("./assets/rituals.json");
world.set_the<GameConfig>({
game, enemies, items, tiles, devices, bosses, rituals
});
}

View file

@ -1,14 +0,0 @@
#pragma once
#include "components.hpp"
#include "map.hpp"
#include "dinkyecs.hpp"
#include <filesystem>
#include <string>
#include <map>
namespace save {
namespace fs = std::filesystem;
void load_configs(DinkyECS::World &world);
}

View file

@ -33,7 +33,7 @@ namespace shaders {
if(!INITIALIZED) {
dbc::check(sf::Shader::isAvailable(), "no shaders?!");
INITIALIZED = true;
Config config("assets/shaders.json");
auto config = settings::get("shaders");
bool good = load_shader("ERROR", config["ERROR"]);
dbc::check(good, "Failed to load ERROR shader. Look in assets/shaders.json");

View file

@ -28,7 +28,7 @@ namespace sound {
void init() {
if(!initialized) {
Config assets("assets/config.json");
auto assets = settings::get("config");
for(auto& el : assets["sounds"].items()) {
load(el.key(), el.value());

View file

@ -130,7 +130,7 @@ TEST_CASE("ai as a module like sound/sprites", "[ai]") {
TEST_CASE("ai autowalker ai test", "[ai]") {
ai::reset();
ai::init("assets/ai.json");
ai::init("ai");
auto start = ai::load_state("Host::initial_state");
auto goal = ai::load_state("Host::final_state");
int enemy_count = 5;
@ -170,7 +170,7 @@ TEST_CASE("ai autowalker ai test", "[ai]") {
TEST_CASE("Confirm EntityAI behaves as expected", "[ai]") {
ai::reset();
ai::init("assets/ai.json");
ai::init("ai");
auto ai_start = ai::load_state("Enemy::initial_state");
auto ai_goal = ai::load_state("Enemy::final_state");

View file

@ -9,7 +9,7 @@ using namespace combat;
TEST_CASE("battle operations fantasy", "[combat-battle]") {
ai::reset();
ai::init("assets/ai.json");
ai::init("ai");
auto ai_start = ai::load_state("Enemy::initial_state");
auto ai_goal = ai::load_state("Enemy::final_state");

View file

@ -16,7 +16,7 @@ TEST_CASE("confirm component loading works", "[components]") {
DinkyECS::World world;
for(auto test_data : test_list) {
Config config(test_data);
auto config = settings::get(test_data);
auto data_list = config.json();
for(auto& [key, data] : data_list.items()) {
@ -31,7 +31,7 @@ TEST_CASE("confirm component loading works", "[components]") {
}
TEST_CASE("make sure json_mods works", "[components]") {
Config config("assets/bosses.json");
auto config = settings::get("bosses");
// this confirms that loading something with an optional
// field works with the json conversions in json_mods.hpp
for(auto& comp_data : config["RAT_KING"]["components"]) {

View file

@ -3,8 +3,8 @@
#include <iostream>
TEST_CASE("confirm basic config loader ops", "[config]") {
Config::set_base_dir("./");
Config config("assets/devices.json");
settings::Config::set_base_dir("./");
auto config = settings::get("devices");
auto data_list = config.json();
auto the_keys = config.keys();
@ -19,7 +19,7 @@ TEST_CASE("confirm basic config loader ops", "[config]") {
}
}
Config indexed("tests/config_test.json");
auto indexed = settings::get("tests/config_test.json");
auto& test_0 = indexed[0];
REQUIRE(test_0["test"] == 0);

View file

@ -8,7 +8,7 @@ using namespace fmt;
using namespace components;
TEST_CASE("test the loot ui", "[loot]") {
Config items("assets/items.json");
auto items = settings::get("assets/items.json");
DinkyECS::World world;
auto torch = world.entity();
auto& data = items["TORCH_BAD"];

View file

@ -39,12 +39,12 @@ namespace textures {
}
void load_sprites() {
Config sprites("assets/config.json");
auto sprites = settings::get("config");
bool smooth = sprites["graphics"]["smooth_textures"];
load_sprite_textures(TMGR.sprite_textures, sprites["sprites"], smooth);
Config icons("assets/icons.json");
auto icons = settings::get("assets/icons.json");
load_sprite_textures(TMGR.icon_textures, icons.json(), smooth);
}
@ -56,7 +56,7 @@ namespace textures {
}
void load_tiles() {
Config assets("assets/tiles.json");
auto assets = settings::get("tiles");
auto &tiles = assets.json();
resize_shit(tiles.size());
@ -93,7 +93,7 @@ namespace textures {
}
void load_map_tiles() {
Config config("./assets/map_tiles.json");
auto config = settings::get("map_tiles");
json& tiles = config.json();
for(auto tile : tiles) {

View file

@ -12,7 +12,7 @@ int main(int argc, char* argv[]) {
textures::init();
sound::init();
ai::init("assets/ai.json");
ai::init("ai");
animation::init();
sound::mute(false);

View file

@ -203,7 +203,7 @@ struct MapTileBuilder {
void load_config(MapConfig& config, bool is_centered, std::string path, std::function<json&(json&)> finder)
{
Config tiles(path);
auto tiles = settings::get(path);
for(auto [key, val] : tiles.json().items()) {
config.it.next();
@ -248,13 +248,13 @@ int main() {
palette::init();
MapConfig config;
load_config(config, false, "./assets/tiles.json", [](json& val) -> json& {
load_config(config, false, "tiles", [](json& val) -> json& {
return val;
});
load_config(config, true, "./assets/items.json", component_display);
load_config(config, true, "./assets/devices.json", component_display);
load_config(config, true, "./assets/enemies.json", component_display);
load_config(config, true, "items", component_display);
load_config(config, true, "devices", component_display);
load_config(config, true, "enemies", component_display);
fmt::println("-----------------------------------------");
MapTileBuilder builder(ICONGEN_MAP_TILE_DIM, ICONGEN_MAP_TILE_DIM);

View file

@ -15,7 +15,7 @@ using namespace components;
void WorldBuilder::stylize_rooms() {
auto& tiles = $map.tiles();
Config style_config("assets/styles.json");
auto style_config = settings::get("styles");
json& styles = style_config.json();
for(auto& room : $map.rooms()) {