#pragma once #include "core/utils/toml.hpp" #include #include #include #include "world/Universe.hpp" #include "render/Renderer.hpp" #include "render/Window.hpp" #include "contouring/Abstract.hpp" #include "control/Camera.hpp" namespace config::client { inline glm::vec4 fromHex(const std::string& str) { std::array rgb = {UCHAR_MAX}; sscanf(str.c_str() + 1, "%02X%02X%02X", (unsigned int *)&rgb[0], (unsigned int *)&rgb[1], (unsigned int *)&rgb[2]); return glm::vec4(rgb[0] * 1.f / UCHAR_MAX, rgb[1] * 1.f / UCHAR_MAX, rgb[2] * 1.f / UCHAR_MAX, 1); } inline std::string toHexa(const glm::vec4& rgb) { std::ostringstream sstr; sstr << '#' << std::hex << std::setw(2) << std::setfill('0') << static_cast(rgb.x * UCHAR_MAX) << static_cast(rgb.y * UCHAR_MAX) << static_cast(rgb.z * UCHAR_MAX); return sstr.str(); } /// Client side configuration struct options { public: options(toml::node_view config) { assert(config["enabled"] && "probably a broken config file"); window.targetFPS = config["window"]["target_fps"].value_or(window.targetFPS); window.sampling = config["window"]["sampling"].value_or(window.sampling); window.fullscreen = config["window"]["fullscreen"].value_or(window.fullscreen); renderer.inFlightFrames = config["window"]["parallel_frames"].value_or(renderer.inFlightFrames); preferVulkan = config["render"]["prefer_vulkan"].value_or(preferVulkan); renderer.textures = config["render"]["textures"].value_or(renderer.textures); renderer.textureQuality = config["render"]["texture_quality"].value_or(renderer.textureQuality); renderer.textureSharpness = config["render"]["texture_angular_quality"].value_or(renderer.textureSharpness); renderer.voxel.pbr = config["render"]["pbr"].value_or(renderer.voxel.pbr); renderer.voxel.planar = static_cast(config["render"]["planar"].value_or(static_cast(renderer.voxel.planar))); renderer.voxel.stochastic = config["render"]["stochastic"].value_or(renderer.voxel.stochastic); renderer.voxel.geometry = config["render"]["geometry"].value_or(renderer.voxel.geometry); renderer.voxel.blend = config["render"]["blend"].value_or(renderer.voxel.blend); renderer.voxel.fog = config["render"]["fog"].value_or(renderer.voxel.fog); const std::string fog = config["render"]["fog_color"].value_or(std::string{"#000000"}); renderer.clear_color = fromHex(fog); renderer.skybox = config["render"]["skybox"].value_or(renderer.skybox); renderer.voxel.curvature = config["render"]["curvature"].value_or(renderer.voxel.curvature); renderer.voxel.curv_depth = config["render"]["curvature_depth"].value_or(renderer.voxel.curv_depth); renderer.voxel.transparency = config["render"]["transparency"].value_or(renderer.voxel.transparency); culling = config["render"]["culling"].value_or(culling); contouring = config["contouring"].value_or(std::string("")); camera.far_dist = config["camera"]["far"].value_or(camera.far_dist); camera.near_dist = config["camera"]["near"].value_or(camera.near_dist); camera.fov = config["camera"]["fov"].value_or(camera.fov); control.sensibility = config["control"]["sensibility"].value_or(control.sensibility); control.speed = config["control"]["speed"].value_or(control.speed); control.collide = config["control"]["collide"].value_or(control.collide); world.loadDistance = config["world"]["load_distance"].value_or(world.loadDistance); world.keepDistance = config["world"]["keep_distance"].value_or(world.keepDistance); world.useAverages = config["world"]["use_averages"].value_or(world.useAverages); world.trustMajorant = config["world"]["trust_majorant"].value_or(world.trustMajorant); world.editPrediction = config["world"]["edit_prediction"].value_or(world.editPrediction); world.editHandling = config["world"]["edit_handling"].value_or(world.editHandling); world.movePrediction = config["world"]["move_prediction"].value_or(world.movePrediction); world.moveCollision = config["world"]["move_collision"].value_or(world.moveCollision); #ifndef LIGHT_CLIENT const auto useLocal = config["connection"]["use_local"].value_or(true); #else const auto useLocal = false; #endif if(!useLocal) { world::client::Universe::connection ct; ct.host = config["connection"]["host"].value_or(ct.host); ct.port = config["connection"]["port"].value_or(ct.port); connection = ct; } editor.visible = config["editor"]["visible"].value_or(editor.visible); editor.tool.shape = static_cast(config["editor"]["tool"]["shape"].value_or(static_cast(editor.tool.shape))); editor.tool.radius = config["editor"]["tool"]["radius"].value_or(editor.tool.radius); editor.tool.material = config["editor"]["tool"]["material"].value_or(editor.tool.material); editor.tool.emptyAir = config["editor"]["tool"]["empty_air"].value_or(editor.tool.emptyAir); console.visible = config["console"]["visible"].value_or(console.visible); console.scroll = config["console"]["scroll"].value_or(console.scroll); console.opacity = config["console"]["opacity"].value_or(console.opacity); debugMenu.bar = config["debug_menu"]["bar"].value_or(debugMenu.bar); debugMenu.render = config["debug_menu"]["render"].value_or(debugMenu.render); debugMenu.world = config["debug_menu"]["world"].value_or(debugMenu.world); debugMenu.contouring = config["debug_menu"]["contouring"].value_or(debugMenu.contouring); debugMenu.controls = config["debug_menu"]["controls"].value_or(debugMenu.controls); overlay.visible = config["overlay"]["visible"].value_or(overlay.visible); overlay.corner = config["overlay"]["corner"].value_or(overlay.corner); if (config["keys"].is_table()) { for(auto entry: *config["keys"].as_table()) { keys.emplace_back(std::stoi(entry.first), entry.second.value_or(-1)); } } } toml::table save() { auto config = toml::table(); config.insert_or_assign("enabled", true); config.insert_or_assign("window", toml::table({ {"target_fps", window.targetFPS}, {"sampling", window.sampling}, {"fullscreen", window.fullscreen}, {"parallel_frames", renderer.inFlightFrames} })); config.insert_or_assign("render", toml::table({ {"prefer_vulkan", preferVulkan}, {"textures", renderer.textures}, {"texture_quality", renderer.textureQuality}, {"texture_angular_quality", renderer.textureSharpness}, {"pbr", renderer.voxel.pbr}, {"planar", static_cast(renderer.voxel.planar)}, {"stochastic", renderer.voxel.stochastic}, {"geometry", renderer.voxel.geometry}, {"blend", renderer.voxel.blend}, {"fog", renderer.voxel.fog}, {"fog_color", toHexa(renderer.clear_color)}, {"skybox", renderer.skybox}, {"curvature", renderer.voxel.curvature}, {"curvature_depth", renderer.voxel.curv_depth}, {"transparency", renderer.voxel.transparency}, {"culling", culling} })); config.insert_or_assign("contouring", contouring); config.insert_or_assign("camera", toml::table({ {"far", camera.far_dist}, {"near", camera.near_dist}, {"fov", camera.fov} })); config.insert_or_assign("control", toml::table({ {"sensibility", control.sensibility}, {"speed", control.speed}, {"collide", control.collide} })); config.insert_or_assign("world", toml::table({ {"load_distance", world.loadDistance}, {"keep_distance", world.keepDistance}, {"use_averages", world.useAverages}, {"trust_majorant", world.trustMajorant}, {"edit_prediction", world.editPrediction}, {"edit_handling", world.editHandling}, {"move_prediction", world.movePrediction}, {"move_collision", world.moveCollision} })); if(connection.has_value()) { const auto &ct = connection.value(); config.insert_or_assign("connection", toml::table({ #ifndef LIGHT_CLIENT {"use_local", false}, #endif {"host", ct.host}, {"port", ct.port} })); } else { config.insert_or_assign("connection", toml::table({{"use_local", true}})); } config.insert_or_assign("editor", toml::table({ {"visible", editor.visible}, {"tool", toml::table({ {"shape", static_cast(editor.tool.shape)}, {"radius", editor.tool.radius}, {"material", editor.tool.material}, {"empty_air", editor.tool.emptyAir} })} })); config.insert_or_assign("console", toml::table({ {"visible", console.visible}, {"scroll", console.scroll}, {"opacity", console.opacity} })); config.insert_or_assign("debug_menu", toml::table({ {"bar", debugMenu.bar}, {"render", debugMenu.render}, {"world", debugMenu.world}, {"contouring", debugMenu.contouring}, {"controls", debugMenu.controls} })); config.insert_or_assign("overlay", toml::table({ {"visible", overlay.visible}, {"corner", overlay.corner} })); toml::table key_table; for (const auto& key: keys) key_table.insert_or_assign(std::to_string(key.first), key.second); config.insert_or_assign("keys", key_table); return config; } world::client::Universe::options world; struct { bool bar = false; bool render = false; bool world = false; bool contouring = false; bool controls = false; } debugMenu; windowOptions window; bool preferVulkan = false; render::renderOptions renderer; int culling = 0; std::string contouring; Controllable::options control; Camera::options camera; struct { bool visible = false; struct { world::action::Shape shape = world::action::Shape::Cube; int radius = 2; unsigned short material = 5; bool emptyAir = true; } tool; } editor; struct { bool visible = true; bool scroll = true; float opacity = .8f; } console; struct overlay_t { bool visible = true; int corner = 3; } overlay; std::optional connection = {}; std::vector> keys; }; }