New base for a configuration system

This commit is contained in:
Perttu Ahola 2014-10-28 19:39:06 +02:00
parent d5182d09c1
commit 82e0ff612a
9 changed files with 195 additions and 59 deletions

View File

@ -121,8 +121,8 @@ struct Module: public interface::Module, public main_context::Interface
const interface::ServerConfig &server_config = m_server->get_config();
sv_<ss_> resource_paths = {
server_config.urho3d_path+"/Bin/CoreData",
server_config.urho3d_path+"/Bin/Data",
server_config.get<ss_>("urho3d_path")+"/Bin/CoreData",
server_config.get<ss_>("urho3d_path")+"/Bin/Data",
};
auto *fs = interface::getGlobalFilesystem();
ss_ resource_paths_s;

View File

@ -19,9 +19,6 @@ Buildat TODO
- Support Cereal's shared pointer serialization in Lua
- Automatically fetch and build and patch Urho3D when building buildat
- Maybe no
- Automatic buildat root detection so that the server and the client can
generally be started with any working directory in either the in-source or
windows-installed configuration
- Precompiled mode
- Singleplayer UI
- Show all exceptions and errors on client using ui_utils.show_message_dialog
@ -68,3 +65,10 @@ Buildat TODO
- If loading a pre-built module fails even while files haven't been modified,
try building it again (this is sometimes needed when buildat_core is updated
without header modifications)
Doing now:
- Automatic buildat root detection so that the server and the client can
generally be started with any working directory in either the in-source or
windows-installed configuration
- Allow developing a game outside of the buildat source tree without hassle

66
src/core/config.h Normal file
View File

@ -0,0 +1,66 @@
// http://www.apache.org/licenses/LICENSE-2.0
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
#pragma once
#include "core/types.h"
#include "core/json.h"
namespace core
{
struct Config
{
json::Value defaults;
json::Value values;
Config():
defaults(json::object()),
values(json::object())
{}
template<typename T>
void set_default(const ss_ &name, const T &native_v)
{
json::Value new_v(native_v);
const json::Value &current_v = values.get(name);
if(!current_v.is_undefined() &&
current_v.get_type() != new_v.get_type()){
throw Exception(ss_()+"Cannot set defaults["+name+"] with type "+
new_v.desc_type()+"; value already exists with type "+
current_v.desc_type());
}
defaults.set(name, new_v);
}
template<typename T>
void set(const ss_ &name, const T &native_v)
{
json::Value new_v(native_v);
const json::Value &default_v = defaults.get(name);
if(!default_v.is_undefined() &&
new_v.get_type() != default_v.get_type()){
throw Exception(ss_()+"Cannot set values["+name+"] with type "+
new_v.desc_type()+"; default determines required type "+
default_v.desc_type());
}
values.set(name, new_v);
}
template<typename T>
const T& get(const ss_ &name) const
{
const json::Value &current_v = values.get(name);
if(current_v.is_undefined()){
// Try default
const json::Value &default_v = defaults.get(name);
if(!default_v.is_undefined())
return default_v.as<T>();
throw Exception("Configuration value \""+name+"\" not found");
}
return current_v.as<T>();
}
};
}
// vim: set noet ts=4 sw=4:

View File

@ -89,6 +89,7 @@ struct ValuePrivate
std::string s;
std::vector<Value> a;
std::map<std::string, Value> o;
double number; // Used if integer value is referenced as const double
};
}
@ -175,6 +176,20 @@ Value& json::Value::operator=(const Value &other)
return *this;
}
const char* json::Value::desc_type() const {
switch(type){
case T_UNDEFINED: return "UNDEFINED";
case T_NULL: return "NULL";
case T_BOOL: return "BOOL";
case T_INT: return "INT";
case T_FLOAT: return "FLOAT";
case T_STRING: return "STRING";
case T_ARRAY: return "ARRAY";
case T_OBJECT: return "OBJECT";
}
return "unknown";
}
bool json::Value::is_undefined() const {
return type == T_UNDEFINED;
}
@ -266,9 +281,9 @@ const Value& json::Value::get(const std::string &key) const {
return it->second;
}
break;
default:
return dummy;
}
default:
return dummy;
}
}
Value& json::Value::operator[](const char *key){
@ -283,9 +298,9 @@ Value& json::Value::operator[](const std::string &key){
return it->second;
}
break;
default:
throw MutableDoesNotExist();
}
default:
throw MutableDoesNotExist();
}
}
void json::Value::clear(){
@ -307,7 +322,8 @@ const char* json::Value::as_cstring() const {
return p->s.c_str();
}
}
std::string json::Value::as_string(const std::string &default_value) const {
const std::string& json::Value::as_string() const {
static std::string default_value;
switch(type){
default:
return default_value;
@ -315,7 +331,8 @@ std::string json::Value::as_string(const std::string &default_value) const {
return p->s;
}
}
int json::Value::as_integer(int default_value) const {
const int64_t& json::Value::as_integer() const {
static int64_t default_value = 0;
switch(type){
default:
return default_value;
@ -323,7 +340,8 @@ int json::Value::as_integer(int default_value) const {
return value.i;
}
}
double json::Value::as_real(double default_value) const {
const double& json::Value::as_real() const {
static double default_value = 0;
switch(type){
default:
return default_value;
@ -331,17 +349,21 @@ double json::Value::as_real(double default_value) const {
return value.f;
}
}
double json::Value::as_number(double default_value) const {
const double& json::Value::as_number() const {
static double default_value = 0.0;
switch(type){
default:
return default_value;
case T_INT:
return value.i;
p = new ValuePrivate();
p->number = value.i;
return p->number;
case T_FLOAT:
return value.f;
}
}
bool json::Value::as_boolean(bool default_value) const {
const bool& json::Value::as_boolean() const {
static bool default_value = false;
switch(type){
default:
return default_value;
@ -696,4 +718,5 @@ const Value json::load_file(const char *path, json_error_t *error){
t.read(&buffer[0], size);
return json::load_string(buffer.c_str(), error);
}
// codestyle:disable (muh beautiful alignments)
// vim: set noet ts=4 sw=4:

View File

@ -21,13 +21,7 @@ namespace json
struct ValuePrivate;
class Value
{
friend const Value json::object();
friend const Value json::array();
friend const Value json::null();
friend const Value json::load_sajson(const sajson::value &src);
friend class Iterator;
ValuePrivate *p;
public:
enum Type {
T_UNDEFINED,
T_NULL,
@ -38,7 +32,16 @@ namespace json
T_ARRAY,
T_OBJECT,
//T_RAW,
} type;
};
private:
friend const Value json::object();
friend const Value json::array();
friend const Value json::null();
friend const Value json::load_sajson(const sajson::value &src);
friend class Iterator;
mutable ValuePrivate *p;
Type type;
union {
bool b;
int64_t i;
@ -60,6 +63,11 @@ namespace json
// assignment operator
Value& operator=(const Value &value);
// get type
Type get_type() const {return type;}
// describe type
const char* desc_type() const;
// check value type
bool is_undefined() const;
bool is_object() const;
@ -95,12 +103,14 @@ namespace json
// get value cast to specified type
const char* as_cstring() const;
std::string as_string(const std::string &default_value =
std::string()) const;
int as_integer(int default_value = 0) const;
double as_real(double default_value = 0.0) const;
double as_number(double default_value = 0.0) const;
bool as_boolean(bool default_value = false) const;
const std::string& as_string() const;
const int64_t& as_integer() const;
const double& as_real() const;
const double& as_number() const;
const bool& as_boolean() const;
// get value according to template type
template<typename T> const T& as() const;
// set an object property (converts value to object is not one already)
Value& set_key(const char *key, const Value &value);
@ -136,6 +146,22 @@ namespace json
const Value deepcopy() const;
};
template<> inline const json::Value& Value::as<json::Value>() const {
return *this;
}
template<> inline const std::string& Value::as<std::string>() const {
return as_string();
}
template<> inline const int64_t& Value::as<int64_t>() const {
return as_integer();
}
template<> inline const double& Value::as<double> () const {
return as_number();
}
template<> inline const bool& Value::as<bool> () const {
return as_boolean();
}
// iterators over a JSON object
struct IteratorPrivate;
class Iterator {
@ -204,5 +230,5 @@ namespace json
}
#define JSON_INDENT(x) // Dummy for now
// codestyle:disable (muh beautiful alignments)
// vim: set noet ts=4 sw=4:

View File

@ -43,8 +43,24 @@ static bool check_runnable(const ss_ &command)
}
}
Config::Config()
{
set_default("rccpp_build_path", "../cache/rccpp_build");
set_default("interface_path", "../src/interface");
set_default("share_path", "..");
set_default("urho3d_path", "../../Urho3D");
set_default("compiler_command", "c++");
set_default("skip_compiling_modules", json::object());
}
bool Config::check_paths()
{
ss_ rccpp_build_path = get<ss_>("rccpp_build_path");
ss_ interface_path = get<ss_>("interface_path");
ss_ share_path = get<ss_>("share_path");
ss_ urho3d_path = get<ss_>("urho3d_path");
ss_ compiler_command = get<ss_>("compiler_command");
bool fail = false;
if(!check_file_readable(share_path+"/builtin/network/network.cpp")){
@ -85,5 +101,4 @@ bool Config::check_paths()
}
}
// vim: set noet ts=4 sw=4:

View File

@ -2,17 +2,13 @@
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
#pragma once
#include "core/types.h"
#include "core/config.h"
namespace server
{
struct Config
struct Config: public core::Config
{
ss_ rccpp_build_path = "../cache/rccpp_build";
ss_ interface_path = "../src/interface";
ss_ share_path = "..";
ss_ urho3d_path = "../../Urho3D";
ss_ compiler_command = "c++";
set_<ss_> skip_compiling_modules;
Config();
bool check_paths();
};

View File

@ -2,6 +2,7 @@
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
#include "core/types.h"
#include "core/log.h"
#include "core/config.h"
#include "server/config.h"
#include "server/state.h"
#include "interface/server.h"
@ -104,23 +105,23 @@ int main(int argc, char *argv[])
break;
case 'r':
fprintf(stderr, "INFO: config.rccpp_build_path: %s\n", c55_optarg);
config.rccpp_build_path = c55_optarg;
config.set("rccpp_build_path", c55_optarg);
break;
case 'i':
fprintf(stderr, "INFO: config.interface_path: %s\n", c55_optarg);
config.interface_path = c55_optarg;
config.set("interface_path", c55_optarg);
break;
case 'S':
fprintf(stderr, "INFO: config.share_path: %s\n", c55_optarg);
config.share_path = c55_optarg;
config.set("share_path", c55_optarg);
break;
case 'U':
fprintf(stderr, "INFO: config.urho3d_path: %s\n", c55_optarg);
config.urho3d_path = c55_optarg;
config.set("urho3d_path", c55_optarg);
break;
case 'c':
fprintf(stderr, "INFO: config.compiler_command: %s\n", c55_optarg);
config.compiler_command = c55_optarg;
config.set("compiler_command", c55_optarg);
break;
case 'l':
log_set_max_level(atoi(c55_optarg));
@ -131,7 +132,11 @@ int main(int argc, char *argv[])
case 'C':
fprintf(stderr, "INFO: config.skip_compiling_modules += %s\n",
c55_optarg);
config.skip_compiling_modules.insert(c55_optarg);
{
auto v = config.get<json::Value>("skip_compiling_modules");
v.set(c55_optarg, json::Value(true));
config.set("skip_compiling_modules", v);
}
break;
default:
fprintf(stderr, "ERROR: Invalid command-line argument\n");

View File

@ -352,7 +352,7 @@ struct CState: public State, public interface::Server
up_<interface::Thread> m_file_watch_thread;
CState():
m_compiler(rccpp::createCompiler(g_server_config.compiler_command)),
m_compiler(rccpp::createCompiler(g_server_config.get<ss_>("compiler_command"))),
m_thread_pool(interface::thread_pool::createThreadPool())
{
m_thread_pool->start(4); // TODO: Configurable
@ -367,14 +367,14 @@ struct CState: public State, public interface::Server
// We don't want to directly add the interface path as it contains
// stuff like mutex.h which match on Windows to Urho3D's Mutex.h
m_compiler->include_directories.push_back(
g_server_config.interface_path+"/..");
g_server_config.get<ss_>("interface_path")+"/..");
m_compiler->include_directories.push_back(
g_server_config.interface_path+"/../../3rdparty/cereal/include");
g_server_config.get<ss_>("interface_path")+"/../../3rdparty/cereal/include");
m_compiler->include_directories.push_back(
g_server_config.interface_path+
g_server_config.get<ss_>("interface_path")+
"/../../3rdparty/polyvox/library/PolyVoxCore/include");
m_compiler->include_directories.push_back(
g_server_config.share_path+"/builtin");
g_server_config.get<ss_>("share_path")+"/builtin");
// Setup Urho3D in RCC++
@ -385,15 +385,15 @@ struct CState: public State, public interface::Server
};
for(const ss_ &subdir : urho3d_subdirs){
m_compiler->include_directories.push_back(
g_server_config.urho3d_path+"/Source/Engine/"+subdir);
g_server_config.get<ss_>("urho3d_path")+"/Source/Engine/"+subdir);
}
m_compiler->include_directories.push_back(
g_server_config.urho3d_path+"/Build/Engine"); // Urho3D.h
g_server_config.get<ss_>("urho3d_path")+"/Build/Engine"); // Urho3D.h
m_compiler->library_directories.push_back(
g_server_config.urho3d_path+"/Lib");
g_server_config.get<ss_>("urho3d_path")+"/Lib");
m_compiler->libraries.push_back("-lUrho3D");
m_compiler->include_directories.push_back(
g_server_config.urho3d_path+"/Source/ThirdParty/Bullet/src");
g_server_config.get<ss_>("urho3d_path")+"/Source/ThirdParty/Bullet/src");
}
~CState()
{
@ -521,7 +521,8 @@ struct CState: public State, public interface::Server
log_d(MODULE, "extra_cxxflags: %s", cs(extra_cxxflags));
log_d(MODULE, "extra_ldflags: %s", cs(extra_ldflags));
bool skip_compile = g_server_config.skip_compiling_modules.count(info.name);
bool skip_compile = g_server_config.get<json::Value>(
"skip_compiling_modules").get(info.name).as_boolean();
sv_<ss_> files_to_hash = {init_cpp_path};
files_to_hash.insert(
@ -532,12 +533,12 @@ struct CState: public State, public interface::Server
#ifdef _WIN32
// On Windows, we need a new name for each modification of the module
// because Windows caches DLLs by name
ss_ build_dst = g_server_config.rccpp_build_path +
ss_ build_dst = g_server_config.get<ss_>("rccpp_build_path") +
"/"+info.name+"_"+interface::sha1::hex(content_hash)+"."+
MODULE_EXTENSION;
// TODO: Delete old ones
#else
ss_ build_dst = g_server_config.rccpp_build_path +
ss_ build_dst = g_server_config.get<ss_>("rccpp_build_path") +
"/"+info.name+"."+MODULE_EXTENSION;
#endif
@ -795,7 +796,7 @@ struct CState: public State, public interface::Server
ss_ get_builtin_modules_path()
{
return g_server_config.share_path+"/builtin";
return g_server_config.get<ss_>("share_path")+"/builtin";
}
ss_ get_module_path(const ss_ &module_name)