Add console variable support
This commit is contained in:
parent
df0d5d1ad4
commit
83b47b31b4
4 changed files with 101 additions and 7 deletions
|
@ -4,16 +4,31 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace console {
|
namespace console {
|
||||||
using ConsoleArgument = std::variant<std::string, int, bool>;
|
|
||||||
|
|
||||||
enum class ArgType {
|
enum class ArgType {
|
||||||
String,
|
String,
|
||||||
Integer,
|
Integer,
|
||||||
Boolean
|
Boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ConsoleArgument {
|
||||||
|
ConsoleArgument(bool data) : data(data) {}
|
||||||
|
ConsoleArgument(int data) : data(data) {}
|
||||||
|
|
||||||
|
ArgType query_type() const {
|
||||||
|
if(std::holds_alternative<std::string>(data))
|
||||||
|
return ArgType::String;
|
||||||
|
else if(std::holds_alternative<int>(data))
|
||||||
|
return ArgType::Integer;
|
||||||
|
else if(std::holds_alternative<bool>(data))
|
||||||
|
return ArgType::Boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::variant<std::string, int, bool> data;
|
||||||
|
};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
std::vector<ConsoleArgument> arguments;
|
std::vector<ConsoleArgument> arguments;
|
||||||
};
|
};
|
||||||
|
@ -30,4 +45,7 @@ namespace console {
|
||||||
void register_command(const std::string_view name, const ArgumentFormat expected_format, const FunctionPtr function);
|
void register_command(const std::string_view name, const ArgumentFormat expected_format, const FunctionPtr function);
|
||||||
|
|
||||||
void invoke_command(const std::string_view name, const Arguments arguments);
|
void invoke_command(const std::string_view name, const Arguments arguments);
|
||||||
|
void parse_and_invoke_command(const std::string_view command);
|
||||||
|
|
||||||
|
void register_variable(const std::string_view name, bool& variable);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "log.hpp"
|
#include "log.hpp"
|
||||||
|
#include "string_utils.hpp"
|
||||||
|
|
||||||
struct RegisteredCommand {
|
struct RegisteredCommand {
|
||||||
RegisteredCommand(const console::ArgumentFormat format, const console::FunctionPtr function) : expected_format(format), function(function) {}
|
RegisteredCommand(const console::ArgumentFormat format, const console::FunctionPtr function) : expected_format(format), function(function) {}
|
||||||
|
@ -14,6 +15,16 @@ struct RegisteredCommand {
|
||||||
|
|
||||||
static std::unordered_map<std::string, RegisteredCommand> registered_commands;
|
static std::unordered_map<std::string, RegisteredCommand> registered_commands;
|
||||||
|
|
||||||
|
struct RegisteredVariable {
|
||||||
|
RegisteredVariable(bool& data) : type(console::ArgType::Boolean), data(&data) {}
|
||||||
|
|
||||||
|
console::ArgType type;
|
||||||
|
|
||||||
|
std::variant<bool*> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unordered_map<std::string, RegisteredVariable> registered_variables;
|
||||||
|
|
||||||
void console::register_command(const std::string_view name, const ArgumentFormat expected_format, const console::FunctionPtr function) {
|
void console::register_command(const std::string_view name, const ArgumentFormat expected_format, const console::FunctionPtr function) {
|
||||||
registered_commands.try_emplace(name.data(), RegisteredCommand(expected_format, function));
|
registered_commands.try_emplace(name.data(), RegisteredCommand(expected_format, function));
|
||||||
}
|
}
|
||||||
|
@ -36,5 +47,70 @@ void console::invoke_command(const std::string_view name, const Arguments argume
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console::info(System::Core, "{} is not a valid command!", name.data());
|
for(auto& [variable_name, variable_data] : registered_variables) {
|
||||||
|
if(variable_name == name) {
|
||||||
|
bool invalid_format = false;
|
||||||
|
|
||||||
|
if(arguments.arguments.empty())
|
||||||
|
invalid_format = true;
|
||||||
|
|
||||||
|
if(arguments.arguments[0].query_type() != variable_data.type)
|
||||||
|
invalid_format = true;
|
||||||
|
|
||||||
|
if(invalid_format) {
|
||||||
|
console::info(System::Core, "Wrong or empty variable type!");
|
||||||
|
} else {
|
||||||
|
auto argument = arguments.arguments[0];
|
||||||
|
switch(argument.query_type()) {
|
||||||
|
case console::ArgType::Boolean:
|
||||||
|
*std::get<bool*>(variable_data.data) = std::get<bool>(argument.data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console::info(System::Core, "{} is not the name of a valid command or variable!", name.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_number(const std::string_view& s)
|
||||||
|
{
|
||||||
|
return !s.empty() && std::find_if(s.begin(),
|
||||||
|
s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::parse_and_invoke_command(const std::string_view command) {
|
||||||
|
const auto tokens = tokenize(command, " ");
|
||||||
|
if(tokens.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto try_parse_argument = [](std::string_view arg) -> std::optional<ConsoleArgument> {
|
||||||
|
if(arg == "true") {
|
||||||
|
return ConsoleArgument(true);
|
||||||
|
} else if(arg == "false") {
|
||||||
|
return ConsoleArgument(false);
|
||||||
|
} else if(is_number(arg)) {
|
||||||
|
return ConsoleArgument(std::stoi(arg.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<ConsoleArgument> arguments;
|
||||||
|
|
||||||
|
for(int i = 1; i < tokens.size(); i++) {
|
||||||
|
if(auto arg = try_parse_argument(tokens[i]); arg)
|
||||||
|
arguments.push_back(*arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
console::Arguments args;
|
||||||
|
args.arguments = arguments;
|
||||||
|
|
||||||
|
console::invoke_command(tokens[0], args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::register_variable(const std::string_view name, bool& variable) {
|
||||||
|
registered_variables.try_emplace(name.data(), RegisteredVariable(variable));
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,12 +29,12 @@ Engine::Engine(const int argc, char* argv[]) {
|
||||||
console::info(System::Core, "Test cmd!");
|
console::info(System::Core, "Test cmd!");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console::register_variable("rs_dynamic_resolution", render_options.dynamic_resolution);
|
||||||
|
|
||||||
console::register_command("quit", console::ArgumentFormat(0), [this](const console::Arguments) {
|
console::register_command("quit", console::ArgumentFormat(0), [this](const console::Arguments) {
|
||||||
quit();
|
quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
console::invoke_command("test_cmd", console::Arguments());
|
|
||||||
|
|
||||||
for(int i = 0; i < argc; i++)
|
for(int i = 0; i < argc; i++)
|
||||||
command_line_arguments.push_back(argv[i]);
|
command_line_arguments.push_back(argv[i]);
|
||||||
|
|
||||||
|
|
|
@ -1040,7 +1040,7 @@ void CommonEditor::drawConsole() {
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
if(ImGui::Button("Run")) {
|
if(ImGui::Button("Run")) {
|
||||||
console::invoke_command(command_buffer, console::Arguments());
|
console::parse_and_invoke_command(command_buffer);
|
||||||
command_buffer.clear();
|
command_buffer.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue