lua console library

This commit is contained in:
MihailRis
2024-05-10 23:35:46 +03:00
parent 9dc3c40b69
commit c2ea0d5326
12 changed files with 156 additions and 34 deletions
+8 -1
View File
@@ -23,6 +23,7 @@
#include "graphics/core/Shader.hpp" #include "graphics/core/Shader.hpp"
#include "graphics/ui/GUI.hpp" #include "graphics/ui/GUI.hpp"
#include "logic/EngineController.hpp" #include "logic/EngineController.hpp"
#include "logic/CommandsInterpreter.hpp"
#include "logic/scripting/scripting.hpp" #include "logic/scripting/scripting.hpp"
#include "util/listutil.hpp" #include "util/listutil.hpp"
#include "util/platform.hpp" #include "util/platform.hpp"
@@ -59,7 +60,8 @@ inline void create_channel(Engine* engine, std::string name, NumberSetting& sett
} }
Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, EnginePaths* paths) Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, EnginePaths* paths)
: settings(settings), settingsHandler(settingsHandler), paths(paths) : settings(settings), settingsHandler(settingsHandler), paths(paths),
interpreter(std::make_unique<cmd::CommandsInterpreter>())
{ {
corecontent::setup_bindings(); corecontent::setup_bindings();
loadSettings(); loadSettings();
@@ -198,6 +200,7 @@ Engine::~Engine() {
} }
content.reset(); content.reset();
assets.reset(); assets.reset();
interpreter.reset();
gui.reset(); gui.reset();
logger.info() << "gui finished"; logger.info() << "gui finished";
audio::close(); audio::close();
@@ -211,6 +214,10 @@ EngineController* Engine::getController() {
return controller.get(); return controller.get();
} }
cmd::CommandsInterpreter* Engine::getCommandsInterpreter() {
return interpreter.get();
}
PacksManager Engine::createPacksManager(const fs::path& worldFolder) { PacksManager Engine::createPacksManager(const fs::path& worldFolder) {
PacksManager manager; PacksManager manager;
manager.setSources({ manager.setSources({
+6
View File
@@ -34,6 +34,10 @@ namespace gui {
class GUI; class GUI;
} }
namespace cmd {
class CommandsInterpreter;
}
class initialize_error : public std::runtime_error { class initialize_error : public std::runtime_error {
public: public:
initialize_error(const std::string& message) : std::runtime_error(message) {} initialize_error(const std::string& message) : std::runtime_error(message) {}
@@ -52,6 +56,7 @@ class Engine : public util::ObjectsKeeper {
std::queue<runnable> postRunnables; std::queue<runnable> postRunnables;
std::recursive_mutex postRunnablesMutex; std::recursive_mutex postRunnablesMutex;
std::unique_ptr<EngineController> controller; std::unique_ptr<EngineController> controller;
std::unique_ptr<cmd::CommandsInterpreter> interpreter;
std::vector<std::string> basePacks {"base"}; std::vector<std::string> basePacks {"base"};
uint64_t frame = 0; uint64_t frame = 0;
@@ -136,6 +141,7 @@ public:
void saveScreenshot(); void saveScreenshot();
EngineController* getController(); EngineController* getController();
cmd::CommandsInterpreter* getCommandsInterpreter();
PacksManager createPacksManager(const fs::path& worldFolder); PacksManager createPacksManager(const fs::path& worldFolder);
+36 -8
View File
@@ -8,12 +8,12 @@
using namespace cmd; using namespace cmd;
inline bool is_cmd_identifier_part(char c, bool allowColon) { inline bool is_cmd_identifier_part(char c, bool allowColon) {
return is_identifier_part(c) || c == '.' || c == '$' || c == '@' || return is_identifier_part(c) || c == '.' || c == '$' ||
(allowColon && c == ':'); (allowColon && c == ':');
} }
inline bool is_cmd_identifier_start(char c) { inline bool is_cmd_identifier_start(char c) {
return is_identifier_start(c) || c == '.' || c == '$' || c == '@'; return is_identifier_start(c) || c == '.' || c == '$';
} }
class CommandParser : BasicParser { class CommandParser : BasicParser {
@@ -37,7 +37,7 @@ class CommandParser : BasicParser {
{"num", ArgType::number}, {"num", ArgType::number},
{"int", ArgType::integer}, {"int", ArgType::integer},
{"str", ArgType::string}, {"str", ArgType::string},
{"@", ArgType::selector}, {"sel", ArgType::selector},
{"enum", ArgType::enumvalue}, {"enum", ArgType::enumvalue},
}; };
public: public:
@@ -60,7 +60,7 @@ public:
dynamic::Value parseValue() { dynamic::Value parseValue() {
char c = peek(); char c = peek();
if (is_cmd_identifier_start(c)) { if (is_cmd_identifier_start(c) || c == '@') {
auto str = parseIdentifier(true); auto str = parseIdentifier(true);
if (str == "true") { if (str == "true") {
return true; return true;
@@ -171,7 +171,7 @@ public:
} }
template<typename T> template<typename T>
bool typeCheck(Argument* arg, const dynamic::Value& value, const std::string& tname) { inline bool typeCheck(Argument* arg, const dynamic::Value& value, const std::string& tname) {
if (!std::holds_alternative<T>(value)) { if (!std::holds_alternative<T>(value)) {
if (arg->optional) { if (arg->optional) {
return false; return false;
@@ -182,6 +182,22 @@ public:
return true; return true;
} }
inline bool selectorCheck(Argument* arg, const dynamic::Value& value) {
if (auto string = std::get_if<std::string>(&value)) {
if ((*string)[0] == '@') {
if (!util::is_integer((*string).substr(1))) {
throw argumentError(arg->name, "invalid selector");
}
return true;
}
}
if (arg->optional) {
return false;
} else {
throw typeError(arg->name, "selector", value);
}
}
bool typeCheck(Argument* arg, const dynamic::Value& value) { bool typeCheck(Argument* arg, const dynamic::Value& value) {
switch (arg->type) { switch (arg->type) {
case ArgType::enumvalue: { case ArgType::enumvalue: {
@@ -208,12 +224,12 @@ public:
} }
} }
break; break;
case ArgType::selector:
return selectorCheck(arg, value);
case ArgType::integer: case ArgType::integer:
return typeCheck<integer_t>(arg, value, "integer"); return typeCheck<integer_t>(arg, value, "integer");
case ArgType::string: case ArgType::string:
return typeCheck<std::string>(arg, value, "string"); return typeCheck<std::string>(arg, value, "string");
case ArgType::selector:
return typeCheck<integer_t>(arg, value, "id");
} }
return true; return true;
} }
@@ -309,8 +325,20 @@ public:
} }
// positional argument // positional argument
Argument* arg; Argument* arg = nullptr;
do { do {
if (arg) {
std::cout << "skipped arg " << arg->name << std::endl;
if (auto string = std::get_if<std::string>(&arg->def)) {
if ((*string)[0] == '$') {
args->put((*interpreter)[*string]);
} else {
args->put(arg->def);
}
} else {
args->put(arg->def);
}
}
arg = command->getArgument(arg_index++); arg = command->getArgument(arg_index++);
if (arg == nullptr) { if (arg == nullptr) {
throw error("extra positional argument"); throw error("extra positional argument");
+2
View File
@@ -91,6 +91,8 @@ namespace cmd {
std::unique_ptr<CommandsRepository> repository; std::unique_ptr<CommandsRepository> repository;
std::unordered_map<std::string, dynamic::Value> variables; std::unordered_map<std::string, dynamic::Value> variables;
public: public:
CommandsInterpreter() : repository(std::make_unique<CommandsRepository>()) {}
CommandsInterpreter(std::unique_ptr<CommandsRepository> repository) CommandsInterpreter(std::unique_ptr<CommandsRepository> repository)
: repository(std::move(repository)){} : repository(std::move(repository)){}
+43 -17
View File
@@ -12,6 +12,10 @@ inline std::string LAMBDAS_TABLE = "$L";
static debug::Logger logger("lua-state"); static debug::Logger logger("lua-state");
namespace scripting {
extern lua::LuaState* state;
}
lua::luaerror::luaerror(const std::string& message) : std::runtime_error(message) { lua::luaerror::luaerror(const std::string& message) : std::runtime_error(message) {
} }
@@ -123,19 +127,20 @@ void lua::LuaState::remove(const std::string& name) {
} }
void lua::LuaState::createLibs() { void lua::LuaState::createLibs() {
openlib("audio", audiolib, 0); openlib("audio", audiolib);
openlib("block", blocklib, 0); openlib("block", blocklib);
openlib("core", corelib, 0); openlib("console", consolelib);
openlib("file", filelib, 0); openlib("core", corelib);
openlib("gui", guilib, 0); openlib("file", filelib);
openlib("input", inputlib, 0); openlib("gui", guilib);
openlib("inventory", inventorylib, 0); openlib("input", inputlib);
openlib("item", itemlib, 0); openlib("inventory", inventorylib);
openlib("json", jsonlib, 0); openlib("item", itemlib);
openlib("pack", packlib, 0); openlib("json", jsonlib);
openlib("player", playerlib, 0); openlib("pack", packlib);
openlib("time", timelib, 0); openlib("player", playerlib);
openlib("world", worldlib, 0); openlib("time", timelib);
openlib("world", worldlib);
addfunc("print", lua_wrap_errors<l_print>); addfunc("print", lua_wrap_errors<l_print>);
} }
@@ -364,9 +369,9 @@ bool lua::LuaState::isfunction(int idx) {
return lua_isfunction(L, idx); return lua_isfunction(L, idx);
} }
void lua::LuaState::openlib(const std::string& name, const luaL_Reg* libfuncs, int nup) { void lua::LuaState::openlib(const std::string& name, const luaL_Reg* libfuncs) {
lua_newtable(L); lua_newtable(L);
luaL_setfuncs(L, libfuncs, nup); luaL_setfuncs(L, libfuncs, 0);
lua_setglobal(L, name.c_str()); lua_setglobal(L, name.c_str());
} }
@@ -377,7 +382,7 @@ const std::string lua::LuaState::storeAnonymous() {
return funcName; return funcName;
} }
runnable lua::LuaState::createRunnable() { std::shared_ptr<std::string> lua::LuaState::createLambdaHandler() {
auto ptr = reinterpret_cast<ptrdiff_t>(lua_topointer(L, -1)); auto ptr = reinterpret_cast<ptrdiff_t>(lua_topointer(L, -1));
auto name = util::mangleid(ptr); auto name = util::mangleid(ptr);
lua_getglobal(L, LAMBDAS_TABLE.c_str()); lua_getglobal(L, LAMBDAS_TABLE.c_str());
@@ -385,13 +390,17 @@ runnable lua::LuaState::createRunnable() {
lua_setfield(L, -2, name.c_str()); lua_setfield(L, -2, name.c_str());
lua_pop(L, 2); lua_pop(L, 2);
std::shared_ptr<std::string> funcptr(new std::string(name), [=](auto* name) { return std::shared_ptr<std::string>(new std::string(name), [=](auto* name) {
lua_getglobal(L, LAMBDAS_TABLE.c_str()); lua_getglobal(L, LAMBDAS_TABLE.c_str());
lua_pushnil(L); lua_pushnil(L);
lua_setfield(L, -2, name->c_str()); lua_setfield(L, -2, name->c_str());
lua_pop(L, 1); lua_pop(L, 1);
delete name; delete name;
}); });
}
runnable lua::LuaState::createRunnable() {
auto funcptr = createLambdaHandler();
return [=]() { return [=]() {
lua_getglobal(L, LAMBDAS_TABLE.c_str()); lua_getglobal(L, LAMBDAS_TABLE.c_str());
lua_getfield(L, -1, funcptr->c_str()); lua_getfield(L, -1, funcptr->c_str());
@@ -399,6 +408,23 @@ runnable lua::LuaState::createRunnable() {
}; };
} }
scripting::common_func lua::LuaState::createLambda() {
auto funcptr = createLambdaHandler();
return [=](const std::vector<dynamic::Value>& args) {
lua_getglobal(L, LAMBDAS_TABLE.c_str());
lua_getfield(L, -1, funcptr->c_str());
for (const auto& arg : args) {
pushvalue(arg);
}
if (call(args.size())) {
auto result = tovalue(-1);
pop(1);
return result;
}
return dynamic::Value(dynamic::NONE);
};
}
int lua::LuaState::createEnvironment(int parent) { int lua::LuaState::createEnvironment(int parent) {
int id = nextEnvironment++; int id = nextEnvironment++;
+6 -1
View File
@@ -3,6 +3,7 @@
#include "lua_commons.hpp" #include "lua_commons.hpp"
#include "../scripting_functional.hpp"
#include "../../../data/dynamic.hpp" #include "../../../data/dynamic.hpp"
#include "../../../delegates.hpp" #include "../../../delegates.hpp"
@@ -26,6 +27,8 @@ namespace lua {
void logError(const std::string& text); void logError(const std::string& text);
void removeLibFuncs(const char* libname, const char* funcs[]); void removeLibFuncs(const char* libname, const char* funcs[]);
void createLibs(); void createLibs();
std::shared_ptr<std::string> createLambdaHandler();
public: public:
LuaState(); LuaState();
~LuaState(); ~LuaState();
@@ -60,7 +63,7 @@ namespace lua {
int callNoThrow(int argc); int callNoThrow(int argc);
int execute(int env, const std::string& src, const std::string& file="<string>"); int execute(int env, const std::string& src, const std::string& file="<string>");
int eval(int env, const std::string& src, const std::string& file="<eval>"); int eval(int env, const std::string& src, const std::string& file="<eval>");
void openlib(const std::string& name, const luaL_Reg* libfuncs, int nup); void openlib(const std::string& name, const luaL_Reg* libfuncs);
void addfunc(const std::string& name, lua_CFunction func); void addfunc(const std::string& name, lua_CFunction func);
bool getglobal(const std::string& name); bool getglobal(const std::string& name);
void setglobal(const std::string& name); void setglobal(const std::string& name);
@@ -68,6 +71,8 @@ namespace lua {
bool rename(const std::string& from, const std::string& to); bool rename(const std::string& from, const std::string& to);
void remove(const std::string& name);; void remove(const std::string& name);;
runnable createRunnable(); runnable createRunnable();
scripting::common_func createLambda();
int createEnvironment(int parent); int createEnvironment(int parent);
void removeEnvironment(int id); void removeEnvironment(int id);
const std::string storeAnonymous(); const std::string storeAnonymous();
+1
View File
@@ -20,6 +20,7 @@ extern const luaL_Reg timelib [];
extern const luaL_Reg worldlib []; extern const luaL_Reg worldlib [];
extern const luaL_Reg jsonlib []; extern const luaL_Reg jsonlib [];
extern const luaL_Reg inputlib []; extern const luaL_Reg inputlib [];
extern const luaL_Reg consolelib [];
// Lua Overrides // Lua Overrides
extern int l_print(lua_State* L); extern int l_print(lua_State* L);
+43
View File
@@ -0,0 +1,43 @@
#include "api_lua.hpp"
#include "lua_commons.hpp"
#include "LuaState.hpp"
#include "../scripting.hpp"
#include "../../CommandsInterpreter.hpp"
#include "../../../engine.hpp"
#include "../../../coders/commons.hpp"
namespace scripting {
extern lua::LuaState* state;
}
using namespace scripting;
static int l_add_command(lua_State* L) {
auto scheme = lua_tostring(L, 1);
lua_pushvalue(L, 2);
auto func = state->createLambda();
try {
engine->getCommandsInterpreter()->getRepository()->add(
scheme, [func](auto, auto args, auto kwargs) {
return func({args, kwargs});
}
);
} catch (const parsing_error& err) {
luaL_error(L, ("command scheme error:\n"+err.errorLog()).c_str());
}
return 0;
}
static int l_execute(lua_State* L) {
auto prompt = lua_tostring(L, 1);
auto result = engine->getCommandsInterpreter()->execute(prompt);
state->pushvalue(result);
return 1;
}
const luaL_Reg consolelib [] = {
{"add_command", lua_wrap_errors<l_add_command>},
{"execute", lua_wrap_errors<l_execute>},
{NULL, NULL}
};
+6 -2
View File
@@ -1,12 +1,16 @@
#ifndef LOGIC_SCRIPTING_SCRIPTING_FUNCTIONAL_HPP_ #ifndef LOGIC_SCRIPTING_SCRIPTING_FUNCTIONAL_HPP_
#define LOGIC_SCRIPTING_SCRIPTING_FUNCTIONAL_HPP_ #define LOGIC_SCRIPTING_SCRIPTING_FUNCTIONAL_HPP_
#include <glm/glm.hpp>
#include <string>
#include "../../typedefs.hpp" #include "../../typedefs.hpp"
#include "../../delegates.hpp" #include "../../delegates.hpp"
#include "../../data/dynamic.hpp"
#include <glm/glm.hpp>
#include <string>
namespace scripting { namespace scripting {
using common_func = std::function<dynamic::Value(const std::vector<dynamic::Value>&)>;
runnable create_runnable( runnable create_runnable(
const scriptenv& env, const scriptenv& env,
const std::string& src, const std::string& src,
+1 -1
View File
@@ -20,7 +20,7 @@ Hud* scripting::hud = nullptr;
void scripting::on_frontend_init(Hud* hud) { void scripting::on_frontend_init(Hud* hud) {
scripting::hud = hud; scripting::hud = hud;
scripting::state->openlib("hud", hudlib, 0); scripting::state->openlib("hud", hudlib);
for (auto& pack : scripting::engine->getContentPacks()) { for (auto& pack : scripting::engine->getContentPacks()) {
state->emit_event(pack.id + ".hudopen", [&] (lua::LuaState* state) { state->emit_event(pack.id + ".hudopen", [&] (lua::LuaState* state) {
+2 -2
View File
@@ -151,7 +151,7 @@ std::wstring util::str2wstr_utf8(const std::string s) {
return std::wstring(chars.data(), chars.size()); return std::wstring(chars.data(), chars.size());
} }
bool util::is_integer(std::string text) { bool util::is_integer(const std::string& text) {
for (char c : text) { for (char c : text) {
if (c < '0' || c > '9') if (c < '0' || c > '9')
return false; return false;
@@ -159,7 +159,7 @@ bool util::is_integer(std::string text) {
return true; return true;
} }
bool util::is_integer(std::wstring text) { bool util::is_integer(const std::wstring& text) {
for (wchar_t c : text) { for (wchar_t c : text) {
if (c < L'0' || c > L'9') if (c < L'0' || c > L'9')
return false; return false;
+2 -2
View File
@@ -20,8 +20,8 @@ namespace util {
uint32_t decode_utf8(uint& size, const char* bytes); uint32_t decode_utf8(uint& size, const char* bytes);
std::string wstr2str_utf8(const std::wstring ws); std::string wstr2str_utf8(const std::wstring ws);
std::wstring str2wstr_utf8(const std::string s); std::wstring str2wstr_utf8(const std::string s);
bool is_integer(std::string text); bool is_integer(const std::string& text);
bool is_integer(std::wstring text); bool is_integer(const std::wstring& text);
bool is_valid_filename(std::wstring name); bool is_valid_filename(std::wstring name);
void ltrim(std::string &s); void ltrim(std::string &s);