diff --git a/res/scripts/stdcmd.lua b/res/scripts/stdcmd.lua index 7ab5ac0a..25191f79 100644 --- a/res/scripts/stdcmd.lua +++ b/res/scripts/stdcmd.lua @@ -1,23 +1,65 @@ +local SEPARATOR = "________________" +SEPARATOR = SEPARATOR..SEPARATOR..SEPARATOR + +console.add_command( + "help name:str=''", + "Show help infomation for the specified command", + function (args, kwargs) + local name = args[1] + if #name == 0 then + local commands = console.get_commands_list() + table.sort(commands) + return "available commands: ".. + table.tostring(commands).. + "\nuse 'help [command]'" + end + local command = console.get_command_info(name) + if command == nil then + return string.format("command %q not found", name) + end + local where = "" + local str = SEPARATOR.."\n"..command.description.."\n"..name.." " + for i,arg in ipairs(command.args) do + where = where.."\n "..arg.name.." - "..arg.type + if arg.optional then + str = str.."["..arg.name.."] " + where = where.." (optional)" + else + str = str.."<"..arg.name.."> " + end + end + if #command.args then + str = str.."\nwhere"..where + end + + return str.."\n"..SEPARATOR + end +) + console.add_command( "tp obj:sel=$obj.id x:num~pos.x y:num~pos.y z:num~pos.z", + "Teleport object", function (args, kwargs) player.set_pos(unpack(args)) end ) console.add_command( "echo value:str", + "Print value to the console", function (args, kwargs) return args[1] end ) console.add_command( "time.set value:num", + "Set day time [0..1] where 0 is midnight, 0.5 is noon", function (args, kwargs) return world.set_day_time(args[1]) end ) console.add_command( - "fill id:str x:num~pos.x y:num~pos.y z:num~pos.z w:int h:int d:int", + "blocks.fill id:str x:num~pos.x y:num~pos.y z:num~pos.z w:int h:int d:int", + "Fill specified zone with blocks", function (args, kwargs) local name, x, y, z, w, h, d = unpack(args) local id = block.index(name) diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index a73b148e..5f4aba2c 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -32,22 +32,6 @@ void TextBox::draw(const DrawContext* pctx, Assets* assets) { font = assets->getFont(label->getFontName()); - if (autoresize && font) { - auto size = getSize(); - int newy = glm::min(static_cast(parent->getSize().y), - static_cast( - label->getLinesNumber() * - label->getLineInterval() * - font->getLineHeight()) + 1 - ); - if (newy != static_cast(size.y)) { - size.y = newy; - setSize(size); - if (positionfunc) { - pos = positionfunc(); - } - } - } if (!isFocused()) { return; } @@ -141,6 +125,24 @@ void TextBox::drawBackground(const DrawContext* pctx, Assets*) { void TextBox::refreshLabel() { label->setColor(glm::vec4(input.empty() ? 0.5f : 1.0f)); label->setText(getText()); + + if (autoresize && font) { + auto size = getSize(); + int newy = glm::min(static_cast(parent->getSize().y), + static_cast( + label->getLinesNumber() * + label->getLineInterval() * + font->getLineHeight()) + 1 + ); + if (newy != static_cast(size.y)) { + size.y = newy; + setSize(size); + if (positionfunc) { + pos = positionfunc(); + } + } + } + if (multiline && font) { setScrollable(true); uint height = label->getLinesNumber() * font->getLineHeight() * label->getLineInterval(); diff --git a/src/logic/CommandsInterpreter.cpp b/src/logic/CommandsInterpreter.cpp index d43a11b1..54a99901 100644 --- a/src/logic/CommandsInterpreter.cpp +++ b/src/logic/CommandsInterpreter.cpp @@ -138,7 +138,7 @@ public: return Argument {name, type, optional, def, origin, enumname}; } - Command parseScheme(executor_func executor) { + Command parseScheme(executor_func executor, std::string_view description) { std::string name = parseIdentifier(true); std::vector args; std::unordered_map kwargs; @@ -154,7 +154,10 @@ public: args.push_back(parseArgument()); } } - return Command(name, std::move(args), std::move(kwargs), executor); + return Command( + name, std::move(args), std::move(kwargs), + std::string(description), executor + ); } inline parsing_error argumentError( @@ -368,18 +371,24 @@ public: while (auto arg = command->getArgument(arg_index++)) { if (!arg->optional) { throw error("missing argument "+util::quote(arg->name)); + } else { + args->put(arg->def); } } return Prompt {command, args, kwargs}; } }; -Command Command::create(std::string_view scheme, executor_func executor) { - return CommandParser("", scheme).parseScheme(executor); +Command Command::create(std::string_view scheme, std::string_view description, executor_func executor) { + return CommandParser("", scheme).parseScheme(executor, description); } -void CommandsRepository::add(std::string_view scheme, executor_func executor) { - Command command = Command::create(scheme, executor); +void CommandsRepository::add( + std::string_view scheme, + std::string_view description, + executor_func executor +) { + Command command = Command::create(scheme, description, executor); commands[command.getName()] = command; } diff --git a/src/logic/CommandsInterpreter.hpp b/src/logic/CommandsInterpreter.hpp index c5686e0c..60cdce97 100644 --- a/src/logic/CommandsInterpreter.hpp +++ b/src/logic/CommandsInterpreter.hpp @@ -13,6 +13,17 @@ namespace cmd { number, integer, enumvalue, selector, string }; + inline std::string argtype_name(ArgType type) { + switch (type) { + case ArgType::number: return "number"; + case ArgType::integer: return "integer"; + case ArgType::enumvalue: return "enumeration"; + case ArgType::selector: return "selector"; + case ArgType::string: return "string"; + default: return ""; + } + } + struct Argument { std::string name; ArgType type; @@ -41,6 +52,7 @@ namespace cmd { std::string name; std::vector args; std::unordered_map kwargs; + std::string description; executor_func executor; public: Command() {} @@ -49,10 +61,12 @@ namespace cmd { std::string name, std::vector args, std::unordered_map kwargs, + std::string description, executor_func executor - ) : name(name), - args(std::move(args)), - kwargs(std::move(kwargs)), + ) : name(name), + args(std::move(args)), + kwargs(std::move(kwargs)), + description(description), executor(executor) {} Argument* getArgument(size_t index) { @@ -77,14 +91,38 @@ namespace cmd { return name; } - static Command create(std::string_view scheme, executor_func); + const std::vector& getArgs() const { + return args; + } + + const std::unordered_map& getKwArgs() const { + return kwargs; + } + + const std::string& getDescription() const { + return description; + } + + static Command create( + std::string_view scheme, + std::string_view description, + executor_func + ); }; class CommandsRepository { std::unordered_map commands; public: - void add(std::string_view scheme, executor_func); + void add( + std::string_view scheme, + std::string_view description, + executor_func + ); Command* get(const std::string& name); + + const std::unordered_map getCommands() const { + return commands; + } }; class CommandsInterpreter { diff --git a/src/logic/scripting/lua/libconsole.cpp b/src/logic/scripting/lua/libconsole.cpp index 5fd9a498..104b6078 100644 --- a/src/logic/scripting/lua/libconsole.cpp +++ b/src/logic/scripting/lua/libconsole.cpp @@ -15,11 +15,12 @@ using namespace scripting; static int l_add_command(lua_State* L) { auto scheme = lua_tostring(L, 1); - lua_pushvalue(L, 2); + auto description = lua_tostring(L, 2); + lua_pushvalue(L, 3); auto func = state->createLambda(); try { engine->getCommandsInterpreter()->getRepository()->add( - scheme, [func](auto, auto args, auto kwargs) { + scheme, description, [func](auto, auto args, auto kwargs) { return func({args, kwargs}); } ); @@ -43,9 +44,74 @@ static int l_set(lua_State* L) { return 0; } +static int l_get_commands_list(lua_State* L) { + auto interpreter = engine->getCommandsInterpreter(); + auto repo = interpreter->getRepository(); + const auto& commands = repo->getCommands(); + + lua_createtable(L, commands.size(), 0); + size_t index = 1; + for (const auto& entry : commands) { + lua_pushstring(L, entry.first.c_str()); + lua_rawseti(L, -2, index++); + } + return 1; +} + +static int l_get_command_info(lua_State* L) { + auto name = lua_tostring(L, 1); + auto interpreter = engine->getCommandsInterpreter(); + auto repo = interpreter->getRepository(); + auto command = repo->get(name); + const auto& args = command->getArgs(); + const auto& kwargs = command->getKwArgs(); + + lua_createtable(L, 0, 4); + + lua_pushstring(L, name); + lua_setfield(L, -2, "name"); + + lua_pushstring(L, command->getDescription().c_str()); + lua_setfield(L, -2, "description"); + + lua_createtable(L, args.size(), 0); + for (size_t i = 0; i < args.size(); i++) { + auto& arg = args.at(i); + lua_createtable(L, 0, 2); + + lua_pushstring(L, arg.name.c_str()); + lua_setfield(L, -2, "name"); + + lua_pushstring(L, cmd::argtype_name(arg.type).c_str()); + lua_setfield(L, -2, "type"); + + if (arg.optional) { + lua_pushboolean(L, true); + lua_setfield(L, -2, "optional"); + } + lua_rawseti(L, -2, i+1); + } + lua_setfield(L, -2, "args"); + + lua_createtable(L, 0, kwargs.size()); + for (auto& entry : kwargs) { + auto& arg = entry.second; + lua_createtable(L, 0, 1); + + lua_pushstring(L, cmd::argtype_name(arg.type).c_str()); + lua_setfield(L, -2, "type"); + + lua_setfield(L, -2, arg.name.c_str()); + } + lua_setfield(L, -2, "kwargs"); + return 1; +} + const luaL_Reg consolelib [] = { {"add_command", lua_wrap_errors}, {"execute", lua_wrap_errors}, {"set", lua_wrap_errors}, + {"get_commands_list", lua_wrap_errors}, + {"get_command_info", lua_wrap_errors}, {NULL, NULL} };