diff --git a/src/coders/commons.cpp b/src/coders/commons.cpp index 68f977d5..04b0223b 100644 --- a/src/coders/commons.cpp +++ b/src/coders/commons.cpp @@ -1,320 +1,323 @@ -#include "commons.h" - -#include "../util/stringutil.h" - -#include -#include -#include - -inline double power(double base, int64_t power) { - double result = 1.0; - for (int64_t i = 0; i < power; i++) { - result *= base; - } - return result; -} - -parsing_error::parsing_error( - std::string message, - std::string filename, - std::string source, - uint pos, - uint line, - uint linestart) - : std::runtime_error(message), filename(filename), source(source), - pos(pos), line(line), linestart(linestart) { -} - -std::string parsing_error::errorLog() const { - std::stringstream ss; - uint linepos = pos - linestart; - ss << "parsing error in file '" << filename; - ss << "' at " << (line+1) << ":" << linepos << ": " << this->what() << "\n"; - size_t end = source.find("\n", linestart); - if (end == std::string::npos) { - end = source.length(); - } - ss << source.substr(linestart, end-linestart) << "\n"; - for (uint i = 0; i < linepos; i++) { - ss << " "; - } - ss << "^"; - return ss.str(); -} - -BasicParser::BasicParser(std::string file, std::string source) : filename(file), source(source) { -} - -void BasicParser::skipWhitespace() { - while (hasNext()) { - char next = source[pos]; - if (next == '\n') { - line++; - linestart = ++pos; - continue; - } - if (is_whitespace(next)) { - pos++; - } else { - break; - } - } -} - -void BasicParser::skip(size_t n) { - n = std::min(n, source.length()-pos); - - for (size_t i = 0; i < n; i++) { - char next = source[pos++]; - if (next == '\n') { - line++; - linestart = pos; - } - } -} - -void BasicParser::skipLine() { - while (hasNext()) { - if (source[pos] == '\n') { - pos++; - linestart = pos; - line++; - break; - } - pos++; - } -} - -bool BasicParser::skipTo(const std::string& substring) { - size_t idx = source.find(substring, pos); - if (idx == std::string::npos) { - skip(source.length()-pos); - return false; - } else { - skip(idx-pos); - return true; - } -} - -bool BasicParser::hasNext() { - return pos < source.length(); -} - -bool BasicParser::isNext(const std::string& substring) { - if (source.length() - pos < substring.length()) { - return false; - } - return source.substr(pos, substring.length()) == substring; -} - -char BasicParser::nextChar() { - if (!hasNext()) { - throw error("unexpected end"); - } - return source[pos++]; -} - -void BasicParser::expect(char expected) { - char c = peek(); - if (c != expected) { - throw error("'"+std::string({expected})+"' expected"); - } - pos++; -} - -void BasicParser::expect(const std::string& substring) { - if (substring.empty()) - return; - for (uint i = 0; i < substring.length(); i++) { - if (source.length() <= pos + i || source[pos+i] != substring[i]) { - throw error(util::quote(substring)+" expected"); - } - } - pos += substring.length(); -} - -void BasicParser::expectNewLine() { - while (hasNext()) { - char next = source[pos]; - if (next == '\n') { - line++; - linestart = ++pos; - return; - } - if (is_whitespace(next)) { - pos++; - } else { - throw error("line separator expected"); - } - } -} - -void BasicParser::goBack() { - if (pos) pos--; -} - -char BasicParser::peek() { - skipWhitespace(); - if (pos >= source.length()) { - throw error("unexpected end"); - } - return source[pos]; -} - -std::string BasicParser::parseName() { - char c = peek(); - if (!is_identifier_start(c)) { - if (c == '"') { - pos++; - return parseString(c); - } - throw error("identifier expected"); - } - int start = pos; - while (hasNext() && is_identifier_part(source[pos])) { - pos++; - } - return source.substr(start, pos-start); -} - -int64_t BasicParser::parseSimpleInt(int base) { - char c = peek(); - int index = hexchar2int(c); - if (index == -1 || index >= base) { - throw error("invalid number literal"); - } - int64_t value = index; - pos++; - while (hasNext()) { - c = source[pos]; - while (c == '_') { - c = source[++pos]; - } - index = hexchar2int(c); - if (index == -1 || index >= base) { - return value; - } - value *= base; - value += index; - pos++; - } - return value; -} - -bool BasicParser::parseNumber(int sign, number_u& out) { - char c = peek(); - int base = 10; - if (c == '0' && pos + 1 < source.length() && - (base = is_box(source[pos+1])) != 10) { - pos += 2; - out = parseSimpleInt(base); - return true; - } else if (c == 'i' && pos + 2 < source.length() && source[pos+1] == 'n' && source[pos+2] == 'f') { - pos += 3; - out = INFINITY * sign; - return false; - } else if (c == 'n' && pos + 2 < source.length() && source[pos+1] == 'a' && source[pos+2] == 'n') { - pos += 3; - out = NAN * sign; - return false; - } - int64_t value = parseSimpleInt(base); - if (!hasNext()) { - out = value * sign; - return true; - } - c = source[pos]; - if (c == 'e' || c == 'E') { - pos++; - int s = 1; - if (peek() == '-') { - s = -1; - pos++; - } else if (peek() == '+'){ - pos++; - } - out = sign * value * power(10.0, s * parseSimpleInt(10)); - return false; - } - if (c == '.') { - pos++; - int64_t expo = 1; - while (hasNext() && source[pos] == '0') { - expo *= 10; - pos++; - } - int64_t afterdot = 0; - if (hasNext() && is_digit(source[pos])) { - afterdot = parseSimpleInt(10); - } - expo *= power(10, fmax(0, log10(afterdot) + 1)); - c = source[pos]; - - double dvalue = (value + (afterdot / (double)expo)); - if (c == 'e' || c == 'E') { - pos++; - int s = 1; - if (peek() == '-') { - s = -1; - pos++; - } else if (peek() == '+'){ - pos++; - } - out = sign * dvalue * power(10.0, s * parseSimpleInt(10)); - return false; - } - out = sign * dvalue; - return false; - } - out = sign * value; - return true; -} - -std::string BasicParser::parseString(char quote, bool closeRequired) { - std::stringstream ss; - while (hasNext()) { - char c = source[pos]; - if (c == quote) { - pos++; - return ss.str(); - } - if (c == '\\') { - pos++; - c = nextChar(); - if (c >= '0' && c <= '7') { - pos--; - ss << (char)parseSimpleInt(8); - continue; - } - switch (c) { - case 'n': ss << '\n'; break; - case 'r': ss << '\r'; break; - case 'b': ss << '\b'; break; - case 't': ss << '\t'; break; - case 'f': ss << '\f'; break; - case '\'': ss << '\\'; break; - case '"': ss << '"'; break; - case '\\': ss << '\\'; break; - case '/': ss << '/'; break; - case '\n': pos++; continue; - default: - throw error("'\\" + std::string({c}) + - "' is an illegal escape"); - } - continue; - } - if (c == '\n' && closeRequired) { - throw error("non-closed string literal"); - } - ss << c; - pos++; - } - if (closeRequired) { - throw error("unexpected end"); - } - return ss.str(); -} - -parsing_error BasicParser::error(std::string message) { - return parsing_error(message, filename, source, pos, line, linestart); -} +#include "commons.h" + +#include "../util/stringutil.h" + +#include +#include +#include + +inline double power(double base, int64_t power) { + double result = 1.0; + for (int64_t i = 0; i < power; i++) { + result *= base; + } + return result; +} + +parsing_error::parsing_error( + std::string message, + std::string filename, + std::string source, + uint pos, + uint line, + uint linestart) + : std::runtime_error(message), filename(filename), source(source), + pos(pos), line(line), linestart(linestart) { +} + +std::string parsing_error::errorLog() const { + std::stringstream ss; + uint linepos = pos - linestart; + ss << "parsing error in file '" << filename; + ss << "' at " << (line+1) << ":" << linepos << ": " << this->what() << "\n"; + size_t end = source.find("\n", linestart); + if (end == std::string::npos) { + end = source.length(); + } + ss << source.substr(linestart, end-linestart) << "\n"; + for (uint i = 0; i < linepos; i++) { + ss << " "; + } + ss << "^"; + return ss.str(); +} + +BasicParser::BasicParser( + const std::string& file, + const std::string& source +) : filename(file), source(source) { +} + +void BasicParser::skipWhitespace() { + while (hasNext()) { + char next = source[pos]; + if (next == '\n') { + line++; + linestart = ++pos; + continue; + } + if (is_whitespace(next)) { + pos++; + } else { + break; + } + } +} + +void BasicParser::skip(size_t n) { + n = std::min(n, source.length()-pos); + + for (size_t i = 0; i < n; i++) { + char next = source[pos++]; + if (next == '\n') { + line++; + linestart = pos; + } + } +} + +void BasicParser::skipLine() { + while (hasNext()) { + if (source[pos] == '\n') { + pos++; + linestart = pos; + line++; + break; + } + pos++; + } +} + +bool BasicParser::skipTo(const std::string& substring) { + size_t idx = source.find(substring, pos); + if (idx == std::string::npos) { + skip(source.length()-pos); + return false; + } else { + skip(idx-pos); + return true; + } +} + +bool BasicParser::hasNext() { + return pos < source.length(); +} + +bool BasicParser::isNext(const std::string& substring) { + if (source.length() - pos < substring.length()) { + return false; + } + return source.substr(pos, substring.length()) == substring; +} + +char BasicParser::nextChar() { + if (!hasNext()) { + throw error("unexpected end"); + } + return source[pos++]; +} + +void BasicParser::expect(char expected) { + char c = peek(); + if (c != expected) { + throw error("'"+std::string({expected})+"' expected"); + } + pos++; +} + +void BasicParser::expect(const std::string& substring) { + if (substring.empty()) + return; + for (uint i = 0; i < substring.length(); i++) { + if (source.length() <= pos + i || source[pos+i] != substring[i]) { + throw error(util::quote(substring)+" expected"); + } + } + pos += substring.length(); +} + +void BasicParser::expectNewLine() { + while (hasNext()) { + char next = source[pos]; + if (next == '\n') { + line++; + linestart = ++pos; + return; + } + if (is_whitespace(next)) { + pos++; + } else { + throw error("line separator expected"); + } + } +} + +void BasicParser::goBack() { + if (pos) pos--; +} + +char BasicParser::peek() { + skipWhitespace(); + if (pos >= source.length()) { + throw error("unexpected end"); + } + return source[pos]; +} + +std::string BasicParser::parseName() { + char c = peek(); + if (!is_identifier_start(c)) { + if (c == '"') { + pos++; + return parseString(c); + } + throw error("identifier expected"); + } + int start = pos; + while (hasNext() && is_identifier_part(source[pos])) { + pos++; + } + return source.substr(start, pos-start); +} + +int64_t BasicParser::parseSimpleInt(int base) { + char c = peek(); + int index = hexchar2int(c); + if (index == -1 || index >= base) { + throw error("invalid number literal"); + } + int64_t value = index; + pos++; + while (hasNext()) { + c = source[pos]; + while (c == '_') { + c = source[++pos]; + } + index = hexchar2int(c); + if (index == -1 || index >= base) { + return value; + } + value *= base; + value += index; + pos++; + } + return value; +} + +bool BasicParser::parseNumber(int sign, number_u& out) { + char c = peek(); + int base = 10; + if (c == '0' && pos + 1 < source.length() && + (base = is_box(source[pos+1])) != 10) { + pos += 2; + out = parseSimpleInt(base); + return true; + } else if (c == 'i' && pos + 2 < source.length() && source[pos+1] == 'n' && source[pos+2] == 'f') { + pos += 3; + out = INFINITY * sign; + return false; + } else if (c == 'n' && pos + 2 < source.length() && source[pos+1] == 'a' && source[pos+2] == 'n') { + pos += 3; + out = NAN * sign; + return false; + } + int64_t value = parseSimpleInt(base); + if (!hasNext()) { + out = value * sign; + return true; + } + c = source[pos]; + if (c == 'e' || c == 'E') { + pos++; + int s = 1; + if (peek() == '-') { + s = -1; + pos++; + } else if (peek() == '+'){ + pos++; + } + out = sign * value * power(10.0, s * parseSimpleInt(10)); + return false; + } + if (c == '.') { + pos++; + int64_t expo = 1; + while (hasNext() && source[pos] == '0') { + expo *= 10; + pos++; + } + int64_t afterdot = 0; + if (hasNext() && is_digit(source[pos])) { + afterdot = parseSimpleInt(10); + } + expo *= power(10, fmax(0, log10(afterdot) + 1)); + c = source[pos]; + + double dvalue = (value + (afterdot / (double)expo)); + if (c == 'e' || c == 'E') { + pos++; + int s = 1; + if (peek() == '-') { + s = -1; + pos++; + } else if (peek() == '+'){ + pos++; + } + out = sign * dvalue * power(10.0, s * parseSimpleInt(10)); + return false; + } + out = sign * dvalue; + return false; + } + out = sign * value; + return true; +} + +std::string BasicParser::parseString(char quote, bool closeRequired) { + std::stringstream ss; + while (hasNext()) { + char c = source[pos]; + if (c == quote) { + pos++; + return ss.str(); + } + if (c == '\\') { + pos++; + c = nextChar(); + if (c >= '0' && c <= '7') { + pos--; + ss << (char)parseSimpleInt(8); + continue; + } + switch (c) { + case 'n': ss << '\n'; break; + case 'r': ss << '\r'; break; + case 'b': ss << '\b'; break; + case 't': ss << '\t'; break; + case 'f': ss << '\f'; break; + case '\'': ss << '\\'; break; + case '"': ss << '"'; break; + case '\\': ss << '\\'; break; + case '/': ss << '/'; break; + case '\n': pos++; continue; + default: + throw error("'\\" + std::string({c}) + + "' is an illegal escape"); + } + continue; + } + if (c == '\n' && closeRequired) { + throw error("non-closed string literal"); + } + ss << c; + pos++; + } + if (closeRequired) { + throw error("unexpected end"); + } + return ss.str(); +} + +parsing_error BasicParser::error(std::string message) { + return parsing_error(message, filename, source, pos, line, linestart); +} diff --git a/src/coders/commons.h b/src/coders/commons.h index c7169785..7ff70582 100644 --- a/src/coders/commons.h +++ b/src/coders/commons.h @@ -70,8 +70,8 @@ public: class BasicParser { protected: - std::string filename; - std::string source; + const std::string& filename; + const std::string& source; uint pos = 0; uint line = 1; uint linestart = 0; @@ -96,7 +96,7 @@ protected: parsing_error error(std::string message); - BasicParser(std::string filename, std::string source); + BasicParser(const std::string& file, const std::string& source); }; #endif // CODERS_COMMONS_H_ diff --git a/src/coders/json.cpp b/src/coders/json.cpp index 32eb4d8e..70bc6782 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -118,8 +118,8 @@ std::string json::stringify( return ss.str(); } -Parser::Parser(std::string filename, std::string source) - : BasicParser(filename, source) { +Parser::Parser(const std::string& filename, const std::string& source) + : BasicParser(filename, source) { } Map* Parser::parse() { @@ -244,11 +244,11 @@ Value* Parser::parseValue() { throw error("unexpected character '"+std::string({next})+"'"); } -std::unique_ptr json::parse(std::string filename, std::string source) { +std::unique_ptr json::parse(const std::string& filename, const std::string& source) { Parser parser(filename, source); return std::unique_ptr(parser.parse()); } -std::unique_ptr json::parse(std::string source) { +std::unique_ptr json::parse(const std::string& source) { return parse("", source); } diff --git a/src/coders/json.h b/src/coders/json.h index 3fc917da..77ec0dcf 100644 --- a/src/coders/json.h +++ b/src/coders/json.h @@ -24,13 +24,13 @@ namespace json { dynamic::Map* parseObject(); dynamic::Value* parseValue(); public: - Parser(std::string filename, std::string source); + Parser(const std::string& filename, const std::string& source); dynamic::Map* parse(); }; - extern std::unique_ptr parse(std::string filename, std::string source); - extern std::unique_ptr parse(std::string source); + extern std::unique_ptr parse(const std::string& filename, const std::string& source); + extern std::unique_ptr parse(const std::string& source); extern std::string stringify( const dynamic::Map* obj, @@ -38,4 +38,4 @@ namespace json { const std::string& indent); } -#endif // CODERS_JSON_H_ \ No newline at end of file +#endif // CODERS_JSON_H_ diff --git a/src/coders/toml.cpp b/src/coders/toml.cpp index 544a8b9b..587c6ddd 100644 --- a/src/coders/toml.cpp +++ b/src/coders/toml.cpp @@ -1,261 +1,127 @@ -#include "toml.h" -#include "commons.h" -#include "../util/stringutil.h" - -#include -#include -#include -#include -#include - -// FIXME: refactor this monster - -using namespace toml; - -Section::Section(std::string name) : name(name) { -} - -void Section::add(std::string name, Field field) { - if (fields.find(name) != fields.end()) { - throw std::runtime_error("field duplication"); - } - fields[name] = field; - keyOrder.push_back(name); -} - -void Section::add(std::string name, bool* ptr) { - add(name, {fieldtype::ftbool, ptr}); -} - -void Section::add(std::string name, int* ptr) { - add(name, {fieldtype::ftint, ptr}); -} - -void Section::add(std::string name, uint* ptr) { - add(name, {fieldtype::ftuint, ptr}); -} - -void Section::add(std::string name, int64_t* ptr) { - add(name, {fieldtype::ftint64, ptr}); -} - -void Section::add(std::string name, float* ptr) { - add(name, {fieldtype::ftfloat, ptr}); -} - -void Section::add(std::string name, double* ptr) { - add(name, {fieldtype::ftdouble, ptr}); -} - -void Section::add(std::string name, std::string* ptr) { - add(name, {fieldtype::ftstring, ptr}); -} - -const std::string& Section::getName() const { - return name; -} - -const Field* Section::field(const std::string& name) const { - auto found = fields.find(name); - if (found == fields.end()) { - return nullptr; - } - return &found->second; -} - -const std::vector& Section::keys() const { - return keyOrder; -} - -Wrapper::~Wrapper() { - for (auto entry : sections) { - delete entry.second; - } -} - -Section& Wrapper::add(std::string name) { - if (sections.find(name) != sections.end()) { - throw std::runtime_error("section duplication"); - } - Section* section = new Section(name); - sections[name] = section; - keyOrder.push_back(name); - return *section; -} - -Section* Wrapper::section(std::string name) { - auto found = sections.find(name); - if (found == sections.end()) { - return nullptr; - } - return found->second; -} - -std::string Wrapper::write() const { - std::stringstream ss; - for (const std::string& key : keyOrder) { - const Section* section = sections.at(key); - ss << "[" << key << "]\n"; - for (const std::string& key : section->keys()) { - ss << key << " = "; - const Field* field = section->field(key); - assert(field != nullptr); - switch (field->type) { - case fieldtype::ftbool: - ss << (*((bool*)field->ptr) ? "true" : "false"); - break; - case fieldtype::ftint: ss << *((int*)field->ptr); break; - case fieldtype::ftuint: ss << *((uint*)field->ptr); break; - case fieldtype::ftint64: ss << *((int64_t*)field->ptr); break; - case fieldtype::ftfloat: ss << *((float*)field->ptr); break; - case fieldtype::ftdouble: ss << *((double*)field->ptr); break; - case fieldtype::ftstring: - ss << util::escape(*((const std::string*)field->ptr)); - break; - } - ss << "\n"; - } - ss << "\n"; - } - return ss.str(); -} - -Reader::Reader(Wrapper* wrapper, std::string file, std::string source) -: BasicParser(file, source), wrapper(wrapper) { -} - -void Reader::skipWhitespace() { - BasicParser::skipWhitespace(); - if (hasNext() && source[pos] == '#') { - skipLine(); - if (hasNext() && is_whitespace(peek())) { - skipWhitespace(); - } - } -} - -void Reader::read() { - skipWhitespace(); - if (!hasNext()) { - return; - } - readSection(nullptr); -} - -void Section::set(const std::string& name, double value) { - const Field* field = this->field(name); - if (field == nullptr) { - std::cerr << "warning: unknown key '" << name << "'" << std::endl; - } else { - switch (field->type) { - case fieldtype::ftbool: *(bool*)(field->ptr) = fabs(value) > 0.0; break; - case fieldtype::ftint: *(int*)(field->ptr) = value; break; - case fieldtype::ftuint: *(uint*)(field->ptr) = value; break; - case fieldtype::ftint64: *(int64_t*)(field->ptr) = value; break; - case fieldtype::ftfloat: *(float*)(field->ptr) = value; break; - case fieldtype::ftdouble: *(double*)(field->ptr) = value; break; - case fieldtype::ftstring: *(std::string*)(field->ptr) = std::to_string(value); break; - default: - std::cerr << "error: type error for key '" << name << "'" << std::endl; - } - } -} - -void Section::set(const std::string& name, bool value) { - const Field* field = this->field(name); - if (field == nullptr) { - std::cerr << "warning: unknown key '" << name << "'" << std::endl; - } else { - switch (field->type) { - case fieldtype::ftbool: *(bool*)(field->ptr) = value; break; - case fieldtype::ftint: *(int*)(field->ptr) = (int)value; break; - case fieldtype::ftuint: *(uint*)(field->ptr) = (uint)value; break; - case fieldtype::ftint64: *(int64_t*)(field->ptr) = (int64_t)value; break; - case fieldtype::ftfloat: *(float*)(field->ptr) = (float)value; break; - case fieldtype::ftdouble: *(double*)(field->ptr) = (double)value; break; - case fieldtype::ftstring: *(std::string*)(field->ptr) = value ? "true" : "false"; break; - default: - std::cerr << "error: type error for key '" << name << "'" << std::endl; - } - } -} - -void Section::set(const std::string& name, std::string value) { - const Field* field = this->field(name); - if (field == nullptr) { - std::cerr << "warning: unknown key '" << name << "'" << std::endl; - } else { - switch (field->type) { - case fieldtype::ftstring: *(std::string*)(field->ptr) = value; break; - default: - std::cerr << "error: type error for key '" << name << "'" << std::endl; - } - } -} - -void Reader::readSection(Section* section /*nullable*/) { - while (hasNext()) { - skipWhitespace(); - if (!hasNext()) { - break; - } - char c = nextChar(); - if (c == '[') { - std::string name = parseName(); - Section* section = wrapper->section(name); - pos++; - readSection(section); - return; - } - pos--; - std::string name = parseName(); - expect('='); - c = peek(); - if (is_digit(c)) { - number_u num; - if (parseNumber(1, num)) { - if (section) - section->set(name, (double)std::get(num)); - } else { - if (section) - section->set(name, std::get(num)); - } - } else if (c == '-' || c == '+') { - int sign = c == '-' ? -1 : 1; - pos++; - number_u num; - if (parseNumber(sign, num)) { - if (section) - section->set(name, (double)std::get(num)); - } else { - if (section) - section->set(name, std::get(num)); - } - } else if (is_identifier_start(c)) { - std::string identifier = parseName(); - if (identifier == "true" || identifier == "false") { - bool flag = identifier == "true"; - if (section) { - section->set(name, flag); - } - } else if (identifier == "inf") { - if (section) { - section->set(name, INFINITY); - } - } else if (identifier == "nan") { - if (section) { - section->set(name, NAN); - } - } - } else if (c == '"' || c == '\'') { - pos++; - std::string str = parseString(c); - if (section) { - section->set(name, str); - } - } else { - throw error("feature is not supported"); - } - expectNewLine(); - } -} +#include "toml.h" +#include "commons.h" +#include "../data/dynamic.h" +#include "../util/stringutil.h" +#include "../files/settings_io.hpp" + +#include +#include +#include +#include +#include + +// FIXME: refactor this monster + +using namespace toml; + +class Reader : public BasicParser { + SettingsHandler& handler; + + void skipWhitespace() override { + BasicParser::skipWhitespace(); + if (hasNext() && source[pos] == '#') { + skipLine(); + if (hasNext() && is_whitespace(peek())) { + skipWhitespace(); + } + } + } + void readSection(const std::string& section) { + while (hasNext()) { + skipWhitespace(); + if (!hasNext()) { + break; + } + char c = nextChar(); + if (c == '[') { + std::string name = parseName(); + pos++; + readSection(name); + return; + } + pos--; + std::string name = section+"."+parseName(); + expect('='); + c = peek(); + if (is_digit(c)) { + number_u num; + parseNumber(1, num); + handler.setValue(name, *dynamic::Value::of(num)); + } else if (c == '-' || c == '+') { + int sign = c == '-' ? -1 : 1; + pos++; + number_u num; + parseNumber(sign, num); + handler.setValue(name, *dynamic::Value::of(num)); + } else if (is_identifier_start(c)) { + std::string identifier = parseName(); + if (identifier == "true" || identifier == "false") { + bool flag = identifier == "true"; + handler.setValue(name, *dynamic::Value::boolean(flag)); + } else if (identifier == "inf") { + handler.setValue(name, *dynamic::Value::of(INFINITY)); + } else if (identifier == "nan") { + handler.setValue(name, *dynamic::Value::of(NAN)); + } + } else if (c == '"' || c == '\'') { + pos++; + std::string str = parseString(c); + handler.setValue(name, *dynamic::Value::of(str)); + } else { + throw error("feature is not supported"); + } + expectNewLine(); + } + } + +public: + Reader( + SettingsHandler& handler, + const std::string& file, + const std::string& source) + : BasicParser(file, source), handler(handler) { + } + + void read() { + skipWhitespace(); + if (!hasNext()) { + return; + } + readSection(""); + } +}; + +void toml::parse( + SettingsHandler& handler, + const std::string& file, + const std::string& source +) { + Reader reader(handler, file, source); + reader.read(); +} + +std::string toml::stringify(SettingsHandler& handler) { + auto& sections = handler.getSections(); + + std::stringstream ss; + for (auto& section : sections) { + ss << "[" << section.name << "]\n"; + for (const std::string& key : section.keys) { + ss << key << " = "; + auto setting = handler.getSetting(section.name+"."+key); + assert(setting != nullptr); + if (auto integer = dynamic_cast(setting)) { + ss << integer->get(); + } else if (auto number = dynamic_cast(setting)) { + ss << number->get(); + } else if (auto flag = dynamic_cast(setting)) { + ss << (flag->get() ? "true" : "false"); + } else if (auto string = dynamic_cast(setting)) { + ss << util::escape(string->get()); + } + ss << "\n"; + } + ss << "\n"; + } + return ss.str(); +} diff --git a/src/coders/toml.h b/src/coders/toml.h index 31f87e46..22bc00f1 100644 --- a/src/coders/toml.h +++ b/src/coders/toml.h @@ -1,72 +1,22 @@ -#ifndef CODERS_TOML_H_ -#define CODERS_TOML_H_ - -#include -#include -#include - -#include "commons.h" - -namespace toml { - enum class fieldtype { - ftbool, - ftint, - ftuint, - ftint64, - ftfloat, - ftdouble, - ftstring, - }; - - struct Field { - fieldtype type; - void* ptr; - }; - - class Section { - std::unordered_map fields; - std::vector keyOrder; - std::string name; - void add(std::string name, Field field); - public: - Section(std::string name); - void add(std::string name, bool* ptr); - void add(std::string name, int* ptr); - void add(std::string name, int64_t* ptr); - void add(std::string name, uint* ptr); - void add(std::string name, float* ptr); - void add(std::string name, double* ptr); - void add(std::string name, std::string* ptr); - - const Field* field(const std::string& name) const; - - void set(const std::string& name, double value); - void set(const std::string& name, bool value); - void set(const std::string& name, std::string value); - - const std::string& getName() const; - const std::vector& keys() const; - }; - - class Wrapper { - std::unordered_map sections; - std::vector keyOrder; - public: - ~Wrapper(); - Section& add(std::string section); - Section* section(std::string name); - - std::string write() const; - }; - - class Reader : public BasicParser { - Wrapper* wrapper; - void skipWhitespace() override; - void readSection(Section* section); - public: - Reader(Wrapper* wrapper, std::string file, std::string source); - void read(); - }; -} - -#endif // CODERS_TOML_H_ +#ifndef CODERS_TOML_H_ +#define CODERS_TOML_H_ + +#include +#include +#include + +#include "commons.h" + +class SettingsHandler; + +namespace toml { + std::string stringify(SettingsHandler& handler); + + void parse( + SettingsHandler& handler, + const std::string& file, + const std::string& source + ); +} + +#endif // CODERS_TOML_H_ diff --git a/src/coders/xml.cpp b/src/coders/xml.cpp index 384de5c1..68153311 100644 --- a/src/coders/xml.cpp +++ b/src/coders/xml.cpp @@ -176,7 +176,7 @@ const std::string& Document::getEncoding() const { return encoding; } -Parser::Parser(std::string filename, std::string source) +Parser::Parser(const std::string& filename, const std::string& source) : BasicParser(filename, source) { } diff --git a/src/coders/xml.h b/src/coders/xml.h index 21ee96d9..b04c8c8e 100644 --- a/src/coders/xml.h +++ b/src/coders/xml.h @@ -118,7 +118,7 @@ namespace xml { std::string parseText(); std::string parseXMLName(); public: - Parser(std::string filename, std::string source); + Parser(const std::string& filename, const std::string& source); xmldocument parse(); }; diff --git a/src/engine.cpp b/src/engine.cpp index 4f7c03ac..f1bff5d8 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -11,6 +11,7 @@ #include "content/ContentLoader.h" #include "core_defs.h" #include "files/files.h" +#include "files/settings_io.hpp" #include "frontend/locale/langs.h" #include "frontend/menu.hpp" #include "frontend/screens/Screen.hpp" @@ -56,14 +57,14 @@ inline void create_channel(Engine* engine, std::string name, NumberSetting& sett })); } -Engine::Engine(EngineSettings& settings, EnginePaths* paths) - : settings(settings), settingsHandler(settings), paths(paths) +Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, EnginePaths* paths) + : settings(settings), settingsHandler(settingsHandler), paths(paths) { controller = std::make_unique(this); if (Window::initialize(&this->settings.display)){ throw initialize_error("could not initialize window"); } - audio::initialize(settings.audio.enabled); + audio::initialize(settings.audio.enabled.get()); create_channel(this, "master", settings.audio.volumeMaster); create_channel(this, "regular", settings.audio.volumeRegular); create_channel(this, "music", settings.audio.volumeMusic); diff --git a/src/engine.h b/src/engine.h index 938fd0d4..f99dadf4 100644 --- a/src/engine.h +++ b/src/engine.h @@ -10,7 +10,6 @@ #include "content/ContentPack.h" #include "content/PacksManager.hpp" #include "files/engine_paths.h" -#include "files/settings_io.h" #include "util/ObjectsKeeper.hpp" #include @@ -27,6 +26,7 @@ class EnginePaths; class ResPaths; class Batch2D; class EngineController; +class SettingsHandler; namespace fs = std::filesystem; @@ -41,7 +41,7 @@ public: class Engine : public util::ObjectsKeeper { EngineSettings& settings; - SettingsHandler settingsHandler; + SettingsHandler& settingsHandler; EnginePaths* paths; std::unique_ptr assets = nullptr; @@ -65,7 +65,7 @@ class Engine : public util::ObjectsKeeper { void processPostRunnables(); void loadAssets(); public: - Engine(EngineSettings& settings, EnginePaths* paths); + Engine(EngineSettings& settings, SettingsHandler& settingsHandler, EnginePaths* paths); ~Engine(); /// @brief Start main engine input/update/render loop. diff --git a/src/files/WorldFiles.cpp b/src/files/WorldFiles.cpp index 997821a0..d3c3c0dc 100644 --- a/src/files/WorldFiles.cpp +++ b/src/files/WorldFiles.cpp @@ -35,8 +35,8 @@ WorldFiles::WorldFiles(fs::path directory) : directory(directory), regions(direc WorldFiles::WorldFiles(fs::path directory, const DebugSettings& settings) : WorldFiles(directory) { - generatorTestMode = settings.generatorTestMode; - doWriteLights = settings.doWriteLights; + generatorTestMode = settings.generatorTestMode.get(); + doWriteLights = settings.doWriteLights.get(); regions.generatorTestMode = generatorTestMode; regions.doWriteLights = doWriteLights; } diff --git a/src/files/settings_io.cpp b/src/files/settings_io.cpp index 66cf60d6..94fdcd86 100644 --- a/src/files/settings_io.cpp +++ b/src/files/settings_io.cpp @@ -1,4 +1,4 @@ -#include "settings_io.h" +#include "settings_io.hpp" #include "../window/Events.h" #include "../window/input.h" @@ -10,30 +10,70 @@ static debug::Logger logger("settings_io"); +struct SectionsBuilder { + std::unordered_map& map; + std::vector
& sections; + + SectionsBuilder( + std::unordered_map& map, + std::vector
& sections + ) : map(map), sections(sections) { + } + + void section(std::string name) { + sections.push_back(Section {name, {}}); + } + + void add(std::string name, Setting* setting, bool writeable=true) { + Section& section = sections.at(sections.size()-1); + map[section.name+"."+name] = setting; + section.keys.push_back(name); + } +}; + SettingsHandler::SettingsHandler(EngineSettings& settings) { - // public settings - map.emplace("audio.volume-master", &settings.audio.volumeMaster); - map.emplace("audio.volume-regular", &settings.audio.volumeRegular); - map.emplace("audio.volume-ui", &settings.audio.volumeUI); - map.emplace("audio.volume-ambient", &settings.audio.volumeAmbient); - map.emplace("audio.volume-music", &settings.audio.volumeMusic); + SectionsBuilder builder(map, sections); - map.emplace("display.vsync", &settings.display.vsync); - map.emplace("display.fullscreen", &settings.display.fullscreen); + builder.section("audio"); + builder.add("enabled", &settings.audio.enabled, false); + builder.add("volume-master", &settings.audio.volumeMaster); + builder.add("volume-regular", &settings.audio.volumeRegular); + builder.add("volume-ui", &settings.audio.volumeUI); + builder.add("volume-ambient", &settings.audio.volumeAmbient); + builder.add("volume-music", &settings.audio.volumeMusic); - map.emplace("camera.sensitivity", &settings.camera.sensitivity); - map.emplace("camera.fov", &settings.camera.fov); - map.emplace("camera.fov-effects", &settings.camera.fovEffects); - map.emplace("camera.shaking", &settings.camera.shaking); + builder.section("display"); + builder.add("width", &settings.display.width); + builder.add("height", &settings.display.height); + builder.add("samples", &settings.display.samples); + builder.add("vsync", &settings.display.vsync); + builder.add("fullscreen", &settings.display.fullscreen); - map.emplace("chunks.load-distance", &settings.chunks.loadDistance); - map.emplace("chunks.load-speed", &settings.chunks.loadSpeed); + builder.section("camera"); + builder.add("sensitivity", &settings.camera.sensitivity); + builder.add("fov", &settings.camera.fov); + builder.add("fov-effects", &settings.camera.fovEffects); + builder.add("shaking", &settings.camera.shaking); - map.emplace("graphics.fog-curve", &settings.graphics.fogCurve); - map.emplace("graphics.backlight", &settings.graphics.backlight); - map.emplace("graphics.gamma", &settings.graphics.gamma); + builder.section("chunks"); + builder.add("load-distance", &settings.chunks.loadDistance); + builder.add("load-speed", &settings.chunks.loadSpeed); + builder.add("padding", &settings.chunks.padding); - map.emplace("ui.language", &settings.ui.language); + builder.section("graphics"); + builder.add("fog-curve", &settings.graphics.fogCurve); + builder.add("backlight", &settings.graphics.backlight); + builder.add("gamma", &settings.graphics.gamma); + builder.add("frustum-culling", &settings.graphics.frustumCulling); + builder.add("skybox-resolution", &settings.graphics.skyboxResolution); + + builder.section("ui"); + builder.add("language", &settings.ui.language); + builder.add("world-preview-size", &settings.ui.worldPreviewSize); + + builder.section("debug"); + builder.add("generator-test-mode", &settings.debug.generatorTestMode); + builder.add("do-write-lights", &settings.debug.doWriteLights); } std::unique_ptr SettingsHandler::getValue(const std::string& name) const { @@ -123,51 +163,8 @@ void SettingsHandler::setValue(const std::string& name, const dynamic::Value& va } } -toml::Wrapper* create_wrapper(EngineSettings& settings) { - auto wrapper = std::make_unique(); - - toml::Section& audio = wrapper->add("audio"); - audio.add("enabled", &settings.audio.enabled); - audio.add("volume-master", &*settings.audio.volumeMaster); - audio.add("volume-regular", &*settings.audio.volumeRegular); - audio.add("volume-ui", &*settings.audio.volumeUI); - audio.add("volume-ambient", &*settings.audio.volumeAmbient); - audio.add("volume-music", &*settings.audio.volumeMusic); - - toml::Section& display = wrapper->add("display"); - display.add("fullscreen", &*settings.display.fullscreen); - display.add("width", &settings.display.width); - display.add("height", &settings.display.height); - display.add("samples", &settings.display.samples); - display.add("vsync", &*settings.display.vsync); - - toml::Section& chunks = wrapper->add("chunks"); - chunks.add("load-distance", &*settings.chunks.loadDistance); - chunks.add("load-speed", &*settings.chunks.loadSpeed); - chunks.add("padding", &*settings.chunks.padding); - - toml::Section& camera = wrapper->add("camera"); - camera.add("fov-effects", &*settings.camera.fovEffects); - camera.add("fov", &*settings.camera.fov); - camera.add("shaking", &*settings.camera.shaking); - camera.add("sensitivity", &*settings.camera.sensitivity); - - toml::Section& graphics = wrapper->add("graphics"); - graphics.add("gamma", &*settings.graphics.gamma); - graphics.add("fog-curve", &*settings.graphics.fogCurve); - graphics.add("backlight", &*settings.graphics.backlight); - graphics.add("frustum-culling", &*settings.graphics.frustumCulling); - graphics.add("skybox-resolution", &*settings.graphics.skyboxResolution); - - toml::Section& debug = wrapper->add("debug"); - debug.add("generator-test-mode", &settings.debug.generatorTestMode); - debug.add("show-chunk-borders", &settings.debug.showChunkBorders); - debug.add("do-write-lights", &settings.debug.doWriteLights); - - toml::Section& ui = wrapper->add("ui"); - ui.add("language", &*settings.ui.language); - ui.add("world-preview-size", &*settings.ui.worldPreviewSize); - return wrapper.release(); +std::vector
& SettingsHandler::getSections() { + return sections; } std::string write_controls() { diff --git a/src/files/settings_io.h b/src/files/settings_io.hpp similarity index 58% rename from src/files/settings_io.h rename to src/files/settings_io.hpp index 11bd9f46..20c0d8af 100644 --- a/src/files/settings_io.h +++ b/src/files/settings_io.hpp @@ -1,18 +1,21 @@ -#ifndef FILES_SETTINGS_IO_H_ -#define FILES_SETTINGS_IO_H_ +#ifndef FILES_SETTINGS_IO_HPP_ +#define FILES_SETTINGS_IO_HPP_ #include #include +#include #include #include "../settings.h" #include "../data/dynamic.h" -namespace toml { - class Wrapper; -} +struct Section { + std::string name; + std::vector keys; +}; class SettingsHandler { std::unordered_map map; + std::vector
sections; public: SettingsHandler(EngineSettings& settings); @@ -20,10 +23,12 @@ public: void setValue(const std::string& name, const dynamic::Value& value); std::string toString(const std::string& name) const; Setting* getSetting(const std::string& name) const; + + std::vector
& getSections(); }; -extern std::string write_controls(); -extern toml::Wrapper* create_wrapper(EngineSettings& settings); -extern void load_controls(std::string filename, std::string source); +std::string write_controls(); -#endif // FILES_SETTINGS_IO_H_ +void load_controls(std::string filename, std::string source); + +#endif // FILES_SETTINGS_IO_HPP_ diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index 70b37b9f..9590f379 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -146,10 +146,10 @@ std::shared_ptr create_debug_panel( L"Show Chunk Borders", glm::vec2(400, 24) ); checkbox->setSupplier([=]() { - return engine->getSettings().debug.showChunkBorders; + return WorldRenderer::showChunkBorders; }); checkbox->setConsumer([=](bool checked) { - engine->getSettings().debug.showChunkBorders = checked; + WorldRenderer::showChunkBorders = checked; }); panel->add(checkbox); } diff --git a/src/frontend/locale/langs.cpp b/src/frontend/locale/langs.cpp index 45d103a1..3294e96d 100644 --- a/src/frontend/locale/langs.cpp +++ b/src/frontend/locale/langs.cpp @@ -45,7 +45,7 @@ class Reader : public BasicParser { } } public: - Reader(std::string file, std::string source) : BasicParser(file, source) { + Reader(const std::string& file, const std::string& source) : BasicParser(file, source) { } void read(langs::Lang& lang, std::string prefix) { diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 5f46dd6a..420acfc4 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -35,6 +35,8 @@ #include #include +bool WorldRenderer::showChunkBorders = false; + WorldRenderer::WorldRenderer(Engine* engine, LevelFrontend* frontend, Player* player) : engine(engine), level(frontend->getLevel()), @@ -225,7 +227,7 @@ void WorldRenderer::renderDebugLines( linesShader->use(); - if (settings.debug.showChunkBorders){ + if (showChunkBorders){ linesShader->uniformMatrix("u_projview", camera->getProjView()); glm::vec3 coord = player->camera->position; if (coord.x < 0) coord.x--; diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index bae0bfea..3f0b8e25 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -56,6 +56,8 @@ class WorldRenderer { const EngineSettings& settings ); public: + static bool showChunkBorders; + WorldRenderer(Engine* engine, LevelFrontend* frontend, Player* player); ~WorldRenderer(); diff --git a/src/logic/scripting/lua/libcore.cpp b/src/logic/scripting/lua/libcore.cpp index 7f5954de..4cd90244 100644 --- a/src/logic/scripting/lua/libcore.cpp +++ b/src/logic/scripting/lua/libcore.cpp @@ -1,189 +1,190 @@ -#include "lua_commons.h" -#include "api_lua.h" - -#include "../../../engine.h" -#include "../../../files/engine_paths.h" -#include "../../../frontend/menu.hpp" -#include "../../../frontend/screens/MenuScreen.hpp" -#include "../../../logic/LevelController.h" -#include "../../../logic/EngineController.hpp" -#include "../../../window/Events.h" -#include "../../../window/Window.h" -#include "../../../world/WorldGenerators.h" -#include "../scripting.h" - -#include -#include - -namespace scripting { - extern lua::LuaState* state; -} - -static int l_new_world(lua_State* L) { - auto name = lua_tostring(L, 1); - auto seed = lua_tostring(L, 2); - auto generator = lua_tostring(L, 3); - auto controller = scripting::engine->getController(); - controller->createWorld(name, seed, generator); - return 0; -} - -static int l_open_world(lua_State* L) { - auto name = lua_tostring(L, 1); - - auto controller = scripting::engine->getController(); - controller->openWorld(name, false); - return 0; -} - -static int l_close_world(lua_State* L) { - if (scripting::controller == nullptr) { - luaL_error(L, "no world open"); - } - bool save_world = lua_toboolean(L, 1); - if (save_world) { - scripting::controller->saveWorld(); - } - // destroy LevelScreen and run quit callbacks - scripting::engine->setScreen(nullptr); - // create and go to menu screen - scripting::engine->setScreen(std::make_shared(scripting::engine)); - return 0; -} - -static int l_delete_world(lua_State* L) { - auto name = lua_tostring(L, 1); - auto controller = scripting::engine->getController(); - controller->deleteWorld(name); - return 0; -} - -static int l_remove_packs(lua_State* L) { - if (!lua_istable(L, 1)) { - luaL_error(L, "strings array expected as an argument"); - } - std::vector packs; - int len = lua_objlen(L, 1); - for (int i = 0; i < len; i++) { - lua_rawgeti(L, -1, i+1); - packs.push_back(lua_tostring(L, -1)); - lua_pop(L, 1); - } - auto controller = scripting::engine->getController(); - controller->removePacks(scripting::controller, packs); - return 0; -} - -static int l_add_packs(lua_State* L) { - if (!lua_istable(L, 1)) { - luaL_error(L, "strings array expected as an argument"); - } - std::vector packs; - int len = lua_objlen(L, 1); - for (int i = 0; i < len; i++) { - lua_rawgeti(L, -1, i+1); - packs.push_back(lua_tostring(L, -1)); - lua_pop(L, 1); - } - auto controller = scripting::engine->getController(); - controller->addPacks(scripting::controller, packs); - return 0; -} - -static int l_get_bindings(lua_State* L) { - auto& bindings = Events::bindings; - lua_createtable(L, bindings.size(), 0); - - int i = 0; - for (auto& entry : bindings) { - lua_pushstring(L, entry.first.c_str()); - lua_rawseti(L, -2, i + 1); - i++; - } - return 1; -} - -static int l_get_setting(lua_State* L) { - auto name = lua_tostring(L, 1); - const auto value = scripting::engine->getSettingsHandler().getValue(name); - scripting::state->pushvalue(*value); - return 1; -} - -static int l_set_setting(lua_State* L) { - auto name = lua_tostring(L, 1); - const auto value = scripting::state->tovalue(2); - scripting::engine->getSettingsHandler().setValue(name, *value); - return 0; -} - -static int l_str_setting(lua_State* L) { - auto name = lua_tostring(L, 1); - const auto string = scripting::engine->getSettingsHandler().toString(name); - scripting::state->pushstring(string); - return 1; -} - -static int l_get_setting_info(lua_State* L) { - auto name = lua_tostring(L, 1); - auto setting = scripting::engine->getSettingsHandler().getSetting(name); - lua_createtable(L, 0, 1); - if (auto number = dynamic_cast(setting)) { - lua_pushnumber(L, number->getMin()); - lua_setfield(L, -2, "min"); - lua_pushnumber(L, number->getMax()); - lua_setfield(L, -2, "max"); - return 1; - } - if (auto integer = dynamic_cast(setting)) { - lua_pushinteger(L, integer->getMin()); - lua_setfield(L, -2, "min"); - lua_pushinteger(L, integer->getMax()); - lua_setfield(L, -2, "max"); - return 1; - } - lua_pop(L, 1); - luaL_error(L, "unsupported setting type"); - return 0; -} - -static int l_quit(lua_State* L) { - Window::setShouldClose(true); - return 0; -} - -static int l_get_default_generator(lua_State* L) { - lua_pushstring(L, WorldGenerators::getDefaultGeneratorID().c_str()); - return 1; -} - -static int l_get_generators(lua_State* L) { - const auto& generators = WorldGenerators::getGeneratorsIDs(); - lua_createtable(L, generators.size(), 0); - - int i = 0; - for (auto& id : generators) { - lua_pushstring(L, id.c_str()); - lua_rawseti(L, -2, i + 1); - i++; - } - return 1; -} - -const luaL_Reg corelib [] = { - {"new_world", lua_wrap_errors}, - {"open_world", lua_wrap_errors}, - {"close_world", lua_wrap_errors}, - {"delete_world", lua_wrap_errors}, - {"add_packs", lua_wrap_errors}, - {"remove_packs", lua_wrap_errors}, - {"get_bindings", lua_wrap_errors}, - {"get_setting", lua_wrap_errors}, - {"set_setting", lua_wrap_errors}, - {"str_setting", lua_wrap_errors}, - {"get_setting_info", lua_wrap_errors}, - {"quit", lua_wrap_errors}, - {"get_default_generator", lua_wrap_errors}, - {"get_generators", lua_wrap_errors}, - {NULL, NULL} -}; +#include "lua_commons.h" +#include "api_lua.h" + +#include "../../../engine.h" +#include "../../../files/settings_io.hpp" +#include "../../../files/engine_paths.h" +#include "../../../frontend/menu.hpp" +#include "../../../frontend/screens/MenuScreen.hpp" +#include "../../../logic/LevelController.h" +#include "../../../logic/EngineController.hpp" +#include "../../../window/Events.h" +#include "../../../window/Window.h" +#include "../../../world/WorldGenerators.h" +#include "../scripting.h" + +#include +#include + +namespace scripting { + extern lua::LuaState* state; +} + +static int l_new_world(lua_State* L) { + auto name = lua_tostring(L, 1); + auto seed = lua_tostring(L, 2); + auto generator = lua_tostring(L, 3); + auto controller = scripting::engine->getController(); + controller->createWorld(name, seed, generator); + return 0; +} + +static int l_open_world(lua_State* L) { + auto name = lua_tostring(L, 1); + + auto controller = scripting::engine->getController(); + controller->openWorld(name, false); + return 0; +} + +static int l_close_world(lua_State* L) { + if (scripting::controller == nullptr) { + luaL_error(L, "no world open"); + } + bool save_world = lua_toboolean(L, 1); + if (save_world) { + scripting::controller->saveWorld(); + } + // destroy LevelScreen and run quit callbacks + scripting::engine->setScreen(nullptr); + // create and go to menu screen + scripting::engine->setScreen(std::make_shared(scripting::engine)); + return 0; +} + +static int l_delete_world(lua_State* L) { + auto name = lua_tostring(L, 1); + auto controller = scripting::engine->getController(); + controller->deleteWorld(name); + return 0; +} + +static int l_remove_packs(lua_State* L) { + if (!lua_istable(L, 1)) { + luaL_error(L, "strings array expected as an argument"); + } + std::vector packs; + int len = lua_objlen(L, 1); + for (int i = 0; i < len; i++) { + lua_rawgeti(L, -1, i+1); + packs.push_back(lua_tostring(L, -1)); + lua_pop(L, 1); + } + auto controller = scripting::engine->getController(); + controller->removePacks(scripting::controller, packs); + return 0; +} + +static int l_add_packs(lua_State* L) { + if (!lua_istable(L, 1)) { + luaL_error(L, "strings array expected as an argument"); + } + std::vector packs; + int len = lua_objlen(L, 1); + for (int i = 0; i < len; i++) { + lua_rawgeti(L, -1, i+1); + packs.push_back(lua_tostring(L, -1)); + lua_pop(L, 1); + } + auto controller = scripting::engine->getController(); + controller->addPacks(scripting::controller, packs); + return 0; +} + +static int l_get_bindings(lua_State* L) { + auto& bindings = Events::bindings; + lua_createtable(L, bindings.size(), 0); + + int i = 0; + for (auto& entry : bindings) { + lua_pushstring(L, entry.first.c_str()); + lua_rawseti(L, -2, i + 1); + i++; + } + return 1; +} + +static int l_get_setting(lua_State* L) { + auto name = lua_tostring(L, 1); + const auto value = scripting::engine->getSettingsHandler().getValue(name); + scripting::state->pushvalue(*value); + return 1; +} + +static int l_set_setting(lua_State* L) { + auto name = lua_tostring(L, 1); + const auto value = scripting::state->tovalue(2); + scripting::engine->getSettingsHandler().setValue(name, *value); + return 0; +} + +static int l_str_setting(lua_State* L) { + auto name = lua_tostring(L, 1); + const auto string = scripting::engine->getSettingsHandler().toString(name); + scripting::state->pushstring(string); + return 1; +} + +static int l_get_setting_info(lua_State* L) { + auto name = lua_tostring(L, 1); + auto setting = scripting::engine->getSettingsHandler().getSetting(name); + lua_createtable(L, 0, 1); + if (auto number = dynamic_cast(setting)) { + lua_pushnumber(L, number->getMin()); + lua_setfield(L, -2, "min"); + lua_pushnumber(L, number->getMax()); + lua_setfield(L, -2, "max"); + return 1; + } + if (auto integer = dynamic_cast(setting)) { + lua_pushinteger(L, integer->getMin()); + lua_setfield(L, -2, "min"); + lua_pushinteger(L, integer->getMax()); + lua_setfield(L, -2, "max"); + return 1; + } + lua_pop(L, 1); + luaL_error(L, "unsupported setting type"); + return 0; +} + +static int l_quit(lua_State* L) { + Window::setShouldClose(true); + return 0; +} + +static int l_get_default_generator(lua_State* L) { + lua_pushstring(L, WorldGenerators::getDefaultGeneratorID().c_str()); + return 1; +} + +static int l_get_generators(lua_State* L) { + const auto& generators = WorldGenerators::getGeneratorsIDs(); + lua_createtable(L, generators.size(), 0); + + int i = 0; + for (auto& id : generators) { + lua_pushstring(L, id.c_str()); + lua_rawseti(L, -2, i + 1); + i++; + } + return 1; +} + +const luaL_Reg corelib [] = { + {"new_world", lua_wrap_errors}, + {"open_world", lua_wrap_errors}, + {"close_world", lua_wrap_errors}, + {"delete_world", lua_wrap_errors}, + {"add_packs", lua_wrap_errors}, + {"remove_packs", lua_wrap_errors}, + {"get_bindings", lua_wrap_errors}, + {"get_setting", lua_wrap_errors}, + {"set_setting", lua_wrap_errors}, + {"str_setting", lua_wrap_errors}, + {"get_setting_info", lua_wrap_errors}, + {"quit", lua_wrap_errors}, + {"get_default_generator", lua_wrap_errors}, + {"get_generators", lua_wrap_errors}, + {NULL, NULL} +}; diff --git a/src/settings.h b/src/settings.h index f066e79d..c1ae858e 100644 --- a/src/settings.h +++ b/src/settings.h @@ -10,7 +10,7 @@ struct AudioSettings { /// @brief try to initialize AL backend - bool enabled = true; + FlagSetting enabled {true}; NumberSetting volumeMaster {1.0f, 0.0f, 1.0f, setting_format::percent}; NumberSetting volumeRegular {1.0f, 0.0f, 1.0f, setting_format::percent}; @@ -23,17 +23,13 @@ struct DisplaySettings { /// @brief Is window in full screen mode FlagSetting fullscreen {false}; /// @brief Window width (pixels) - int width = 1280; + IntegerSetting width {1280}; /// @brief Window height (pixels) - int height = 720; + IntegerSetting height {720}; /// @brief Anti-aliasing samples - int samples = 0; + IntegerSetting samples {0}; /// @brief VSync on FlagSetting vsync {true}; - /// @brief Window title - std::string title = "VoxelEngine-Cpp v" + - std::to_string(ENGINE_VERSION_MAJOR) + "." + - std::to_string(ENGINE_VERSION_MINOR); }; struct ChunksSettings { @@ -60,6 +56,7 @@ struct GraphicsSettings { /// @brief Fog opacity is calculated as `pow(depth*k, fogCurve)` where k depends on chunksLoadDistance. /// 1.0 is linear, 2.0 is quadratic NumberSetting fogCurve {1.6f, 1.0f, 6.0f}; + /// @brief Lighting gamma NumberSetting gamma {1.0f, 0.5f, 2.0f}; /// @brief Enable blocks backlight to prevent complete darkness FlagSetting backlight {true}; @@ -70,9 +67,8 @@ struct GraphicsSettings { struct DebugSettings { /// @brief Turns off chunks saving/loading - bool generatorTestMode = false; - bool showChunkBorders = false; - bool doWriteLights = true; + FlagSetting generatorTestMode {false}; + FlagSetting doWriteLights {true}; }; struct UiSettings { diff --git a/src/voxel_engine.cpp b/src/voxel_engine.cpp index 02486fb5..a5d82d45 100644 --- a/src/voxel_engine.cpp +++ b/src/voxel_engine.cpp @@ -9,7 +9,7 @@ #include "engine.h" #include "coders/toml.h" #include "files/files.h" -#include "files/settings_io.h" +#include "files/settings_io.hpp" #include "files/engine_paths.h" #include "util/platform.h" #include "util/command_line.h" @@ -33,18 +33,17 @@ int main(int argc, char** argv) { fs::path userfiles = paths.getUserfiles(); try { EngineSettings settings; - std::unique_ptr wrapper (create_wrapper(settings)); + SettingsHandler handler(settings); fs::path settings_file = userfiles/fs::path(SETTINGS_FILE); fs::path controls_file = userfiles/fs::path(CONTROLS_FILE); if (fs::is_regular_file(settings_file)) { logger.info() << "loading settings"; std::string text = files::read_string(settings_file); - toml::Reader reader(wrapper.get(), settings_file.string(), text); - reader.read(); + toml::parse(handler, settings_file.string(), text); } corecontent::setup_bindings(); - Engine engine(settings, &paths); + Engine engine(settings, handler, &paths); if (fs::is_regular_file(controls_file)) { logger.info() << "loading controls"; std::string text = files::read_string(controls_file); @@ -53,7 +52,7 @@ int main(int argc, char** argv) { engine.mainloop(); logger.info() << "saving settings"; - files::write_string(settings_file, wrapper->write()); + files::write_string(settings_file, toml::stringify(handler)); files::write_string(controls_file, write_controls()); } catch (const initialize_error& err) { diff --git a/src/window/Window.cpp b/src/window/Window.cpp index e604e6e3..f1201892 100644 --- a/src/window/Window.cpp +++ b/src/window/Window.cpp @@ -72,8 +72,8 @@ void window_size_callback(GLFWwindow*, int width, int height) { } if (!Window::isFullscreen() && !Window::isMaximized()) { - Window::getSettings()->width = width; - Window::getSettings()->height = height; + Window::getSettings()->width.set(width); + Window::getSettings()->height.set(height); } } Window::resetScissor(); @@ -110,8 +110,12 @@ void error_callback(int error, const char* description) { int Window::initialize(DisplaySettings* settings){ Window::settings = settings; - Window::width = settings->width; - Window::height = settings->height; + Window::width = settings->width.get(); + Window::height = settings->height.get(); + + std::string title = "VoxelEngine-Cpp v" + + std::to_string(ENGINE_VERSION_MAJOR) + "." + + std::to_string(ENGINE_VERSION_MINOR); glfwSetErrorCallback(error_callback); if (glfwInit() == GLFW_FALSE) { @@ -129,9 +133,9 @@ int Window::initialize(DisplaySettings* settings){ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE); #endif glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); - glfwWindowHint(GLFW_SAMPLES, settings->samples); + glfwWindowHint(GLFW_SAMPLES, settings->samples.get()); - window = glfwCreateWindow(width, height, settings->title.c_str(), nullptr, nullptr); + window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); if (window == nullptr){ logger.error() << "failed to create GLFW window"; glfwTerminate(); @@ -286,7 +290,11 @@ void Window::toggleFullscreen(){ glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, GLFW_DONT_CARE); } else { - glfwSetWindowMonitor(window, nullptr, posX, posY, settings->width, settings->height, GLFW_DONT_CARE); + glfwSetWindowMonitor(window, nullptr, + posX, posY, + settings->width.get(), settings->height.get(), + GLFW_DONT_CARE + ); glfwSetWindowAttrib(window, GLFW_MAXIMIZED, GLFW_FALSE); } diff --git a/src/world/World.cpp b/src/world/World.cpp index 2a280c9a..b400aae7 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -57,7 +57,7 @@ void World::write(Level* level) { if (chunk == nullptr || !chunk->isLighted()) continue; bool lightsUnsaved = !chunk->isLoadedLights() && - settings.debug.doWriteLights; + settings.debug.doWriteLights.get(); if (!chunk->isUnsaved() && !lightsUnsaved) continue; regions.put(chunk.get());