From 3008bddddc2b7fa3dd074950a86adda265093e72 Mon Sep 17 00:00:00 2001 From: JaDoma <149813486+DSDemen@users.noreply.github.com> Date: Sat, 3 Feb 2024 18:09:35 +0200 Subject: [PATCH 01/39] Update fi_FI.txt --- res/texts/fi_FI.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/texts/fi_FI.txt b/res/texts/fi_FI.txt index 4da41a1c..124a2956 100644 --- a/res/texts/fi_FI.txt +++ b/res/texts/fi_FI.txt @@ -13,8 +13,8 @@ menu.New World = Uusi Maailma menu.Quit=Poistu menu.Continue=Jatka menu.Save and Quit to Menu=Tallenna ja poistu valikkoon -menu.missing-content=Puuttuu jotkut lisäosat! -menu.Content Error=Sisältövirhe! +menu.missing-content=Puuttuu lisäosia! +menu.Content Error=Lisäosa virhe! menu.Controls=Ohjaus menu.Back to Main Menu=Takaisin Valikoon menu.Settings=Asetukset From 3cd648e10c9117ad96d26f6efac3127b62133127 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 4 Feb 2024 09:32:26 +0300 Subject: [PATCH 02/39] UiXmlReader update --- src/frontend/gui/controls.cpp | 9 ++++++++- src/frontend/gui/controls.h | 3 ++- src/frontend/gui/gui_xml.cpp | 24 +++++++++++++++++++----- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/frontend/gui/controls.cpp b/src/frontend/gui/controls.cpp index e05c66d2..22279e1e 100644 --- a/src/frontend/gui/controls.cpp +++ b/src/frontend/gui/controls.cpp @@ -177,13 +177,20 @@ Button* Button::listenAction(onaction action) { return this; } -void Button::textAlign(Align align) { +void Button::setTextAlign(Align align) { if (label) { label->setAlign(align); refresh(); } } +Align Button::getTextAlign() const { + if (label) { + return label->getAlign(); + } + return Align::left; +} + // ============================== RichButton ================================== RichButton::RichButton(vec2 size) : Container(vec2(), size) { setHoverColor(glm::vec4(0.05f, 0.1f, 0.15f, 0.75f)); diff --git a/src/frontend/gui/controls.h b/src/frontend/gui/controls.h index 281ec1ee..93969e17 100644 --- a/src/frontend/gui/controls.h +++ b/src/frontend/gui/controls.h @@ -73,7 +73,8 @@ namespace gui { virtual void mouseRelease(GUI*, int x, int y) override; virtual Button* listenAction(onaction action); - virtual void textAlign(Align align); + virtual Align getTextAlign() const; + virtual void setTextAlign(Align align); virtual void setText(std::wstring text); virtual std::wstring getText() const; diff --git a/src/frontend/gui/gui_xml.cpp b/src/frontend/gui/gui_xml.cpp index 6601ad22..0a015653 100644 --- a/src/frontend/gui/gui_xml.cpp +++ b/src/frontend/gui/gui_xml.cpp @@ -98,6 +98,13 @@ static glm::vec4 readColor(const std::string& str) { } } +static Align align_from_string(const std::string& str, Align def) { + if (str == "left") return Align::left; + if (str == "center") return Align::center; + if (str == "right") return Align::right; + return def; +} + /* Read basic UINode properties */ static void readUINode(xml::xmlelement element, UINode& node) { if (element->has("coord")) { @@ -109,6 +116,12 @@ static void readUINode(xml::xmlelement element, UINode& node) { if (element->has("color")) { node.setColor(readColor(element->attr("color").getText())); } + if (element->has("margin")) { + node.setMargin(readVec4(element->attr("margin").getText())); + } + + std::string alignName = element->attr("align", "").getText(); + node.setAlign(align_from_string(alignName, node.getAlign())); } static void _readContainer(UiXmlReader& reader, xml::xmlelement element, Container& container) { @@ -128,10 +141,6 @@ static void _readPanel(UiXmlReader& reader, xml::xmlelement element, Panel& pane panel.setPadding(readVec4(element->attr("padding").getText())); } - if (element->has("margin")) { - panel.setMargin(readVec4(element->attr("margin").getText())); - } - if (element->has("size")) { panel.setResizing(false); } @@ -147,7 +156,9 @@ static void _readPanel(UiXmlReader& reader, xml::xmlelement element, Panel& pane static std::wstring readAndProcessInnerText(xml::xmlelement element) { std::wstring text = L""; if (element->size() == 1) { - text = util::str2wstr_utf8(element->sub(0)->attr("#").getText()); + std::string source = element->sub(0)->attr("#").getText(); + util::trim(source); + text = util::str2wstr_utf8(source); if (text[0] == '@') { text = langs::get(text.substr(1)); } @@ -179,6 +190,9 @@ static std::shared_ptr readButton(UiXmlReader& reader, xml::xmlelement e callback(); }); } + if (element->has("text-align")) { + button->setTextAlign(align_from_string(element->attr("text-align").getText(), button->getTextAlign())); + } return button; } From b38afe9e3401d3a68be4e722e3290a5ee5f3add1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 4 Feb 2024 23:45:15 +0300 Subject: [PATCH 03/39] refactor --- src/coders/xml.cpp | 71 +++++ src/coders/xml.h | 5 + src/delegates.h | 5 +- src/frontend/gui/gui_xml.cpp | 111 ++------ src/frontend/gui/gui_xml.h | 3 + src/logic/scripting/LuaState.cpp | 168 ++++++++++++ src/logic/scripting/LuaState.h | 48 ++++ src/logic/scripting/api_lua.cpp | 432 ------------------------------ src/logic/scripting/api_lua.h | 396 ++++++++++++++++++++++++++- src/logic/scripting/lua_util.h | 15 ++ src/logic/scripting/scripting.cpp | 255 +++++++----------- src/logic/scripting/scripting.h | 7 + src/util/stringutil.cpp | 4 + src/util/stringutil.h | 1 + 14 files changed, 837 insertions(+), 684 deletions(-) create mode 100644 src/logic/scripting/LuaState.cpp create mode 100644 src/logic/scripting/LuaState.h delete mode 100644 src/logic/scripting/api_lua.cpp create mode 100644 src/logic/scripting/lua_util.h diff --git a/src/coders/xml.cpp b/src/coders/xml.cpp index aeb08dd1..03b4b3cd 100644 --- a/src/coders/xml.cpp +++ b/src/coders/xml.cpp @@ -32,6 +32,77 @@ bool Attribute::asBool() const { return text == "true" || text == "1"; } +/* Read 2d vector formatted `x,y`*/ +glm::vec2 Attribute::asVec2() const { + size_t pos = text.find(','); + if (pos == std::string::npos) { + throw std::runtime_error("invalid vec2 value "+escape_string(text)); + } + return glm::vec2( + util::parse_double(text, 0, pos), + util::parse_double(text, pos+1, text.length()-pos-1) + ); +} + +/* Read 3d vector formatted `x,y,z`*/ +glm::vec3 Attribute::asVec3() const { + size_t pos1 = text.find(','); + if (pos1 == std::string::npos) { + throw std::runtime_error("invalid vec3 value "+escape_string(text)); + } + size_t pos2 = text.find(',', pos1+1); + if (pos2 == std::string::npos) { + throw std::runtime_error("invalid vec3 value "+escape_string(text)); + } + return glm::vec3( + util::parse_double(text, 0, pos1), + util::parse_double(text, pos1+1, pos2), + util::parse_double(text, pos2+1, text.length()-pos2-1) + ); +} + +/* Read 4d vector formatted `x,y,z,w`*/ +glm::vec4 Attribute::asVec4() const { + size_t pos1 = text.find(','); + if (pos1 == std::string::npos) { + throw std::runtime_error("invalid vec4 value "+escape_string(text)); + } + size_t pos2 = text.find(',', pos1+1); + if (pos2 == std::string::npos) { + throw std::runtime_error("invalid vec4 value "+escape_string(text)); + } + size_t pos3 = text.find(',', pos2+1); + if (pos3 == std::string::npos) { + throw std::runtime_error("invalid vec4 value "+escape_string(text)); + } + return glm::vec4( + util::parse_double(text, 0, pos1), + util::parse_double(text, pos1+1, pos2-pos1-1), + util::parse_double(text, pos2+1, pos3-pos2-1), + util::parse_double(text, pos3+1, text.length()-pos3-1) + ); +} + +/* Read RGBA color. Supported formats: + - "#RRGGBB" or "#RRGGBBAA" hex color */ +glm::vec4 Attribute::asColor() const { + if (text[0] == '#') { + if (text.length() != 7 && text.length() != 9) { + throw std::runtime_error("#RRGGBB or #RRGGBBAA required"); + } + int a = 255; + int r = (hexchar2int(text[1]) << 4) | hexchar2int(text[2]); + int g = (hexchar2int(text[3]) << 4) | hexchar2int(text[4]); + int b = (hexchar2int(text[5]) << 4) | hexchar2int(text[6]); + if (text.length() == 9) { + a = (hexchar2int(text[7]) << 4) | hexchar2int(text[8]); + } + return glm::vec4(r / 255.f, g / 255.f, b / 255.f, a / 255.f); + } else { + throw std::runtime_error("hex colors are only supported"); + } +} + Node::Node(std::string tag) : tag(tag) { } diff --git a/src/coders/xml.h b/src/coders/xml.h index 275bd279..824b8209 100644 --- a/src/coders/xml.h +++ b/src/coders/xml.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "commons.h" @@ -30,6 +31,10 @@ namespace xml { int64_t asInt() const; double asFloat() const; bool asBool() const; + glm::vec2 asVec2() const; + glm::vec3 asVec3() const; + glm::vec4 asVec4() const; + glm::vec4 asColor() const; }; /* XML element class. Text element has tag 'text' and attribute 'text' */ diff --git a/src/delegates.h b/src/delegates.h index 06f0d3a5..f179251d 100644 --- a/src/delegates.h +++ b/src/delegates.h @@ -4,7 +4,8 @@ #include #include -typedef std::function runnable; -typedef std::function stringconsumer; +using runnable = std::function; +using stringconsumer = std::function; +using wstringconsumer = std::function; #endif // DELEGATES_H_ diff --git a/src/frontend/gui/gui_xml.cpp b/src/frontend/gui/gui_xml.cpp index 0a015653..15206130 100644 --- a/src/frontend/gui/gui_xml.cpp +++ b/src/frontend/gui/gui_xml.cpp @@ -12,92 +12,6 @@ using namespace gui; -static double readDouble(const std::string& str, size_t offset, size_t len) { - double value; - auto res = std::from_chars(str.data()+offset, str.data()+offset+len, value); - if (res.ptr != str.data()+offset+len) { - throw std::runtime_error("invalid number format "+escape_string(str)); - } - return value; -} - -/* Read 2d vector formatted `x,y`*/ -static glm::vec2 readVec2(const std::string& str) { - size_t pos = str.find(','); - if (pos == std::string::npos) { - throw std::runtime_error("invalid vec2 value "+escape_string(str)); - } - return glm::vec2( - readDouble(str, 0, pos), - readDouble(str, pos+1, str.length()-pos-1) - ); -} - -/* Read 3d vector formatted `x,y,z`*/ -[[maybe_unused]] -static glm::vec3 readVec3(const std::string& str) { - size_t pos1 = str.find(','); - if (pos1 == std::string::npos) { - throw std::runtime_error("invalid vec3 value "+escape_string(str)); - } - size_t pos2 = str.find(',', pos1+1); - if (pos2 == std::string::npos) { - throw std::runtime_error("invalid vec3 value "+escape_string(str)); - } - return glm::vec3( - readDouble(str, 0, pos1), - readDouble(str, pos1+1, pos2), - readDouble(str, pos2+1, str.length()-pos2-1) - ); -} - -/* Read 4d vector formatted `x,y,z,w`*/ -static glm::vec4 readVec4(const std::string& str) { - size_t pos1 = str.find(','); - if (pos1 == std::string::npos) { - throw std::runtime_error("invalid vec4 value "+escape_string(str)); - } - size_t pos2 = str.find(',', pos1+1); - if (pos2 == std::string::npos) { - throw std::runtime_error("invalid vec4 value "+escape_string(str)); - } - size_t pos3 = str.find(',', pos2+1); - if (pos3 == std::string::npos) { - throw std::runtime_error("invalid vec4 value "+escape_string(str)); - } - return glm::vec4( - readDouble(str, 0, pos1), - readDouble(str, pos1+1, pos2-pos1-1), - readDouble(str, pos2+1, pos3-pos2-1), - readDouble(str, pos3+1, str.length()-pos3-1) - ); -} - -/* Read RGBA color. Supported formats: - - "#RRGGBB" or "#RRGGBBAA" hex color */ -static glm::vec4 readColor(const std::string& str) { - if (str[0] == '#') { - if (str.length() != 7 && str.length() != 9) { - throw std::runtime_error("#RRGGBB or #RRGGBBAA required"); - } - int a = 255; - int r = (hexchar2int(str[1]) << 4) | hexchar2int(str[2]); - int g = (hexchar2int(str[3]) << 4) | hexchar2int(str[4]); - int b = (hexchar2int(str[5]) << 4) | hexchar2int(str[6]); - if (str.length() == 9) { - a = (hexchar2int(str[7]) << 4) | hexchar2int(str[8]); - } - return glm::vec4( - r / 255.f, - g / 255.f, - b / 255.f, - a / 255.f - ); - } else { - throw std::runtime_error("hex colors are only supported"); - } -} - static Align align_from_string(const std::string& str, Align def) { if (str == "left") return Align::left; if (str == "center") return Align::center; @@ -108,16 +22,16 @@ static Align align_from_string(const std::string& str, Align def) { /* Read basic UINode properties */ static void readUINode(xml::xmlelement element, UINode& node) { if (element->has("coord")) { - node.setCoord(readVec2(element->attr("coord").getText())); + node.setCoord(element->attr("coord").asVec2()); } if (element->has("size")) { - node.setSize(readVec2(element->attr("size").getText())); + node.setSize(element->attr("size").asVec2()); } if (element->has("color")) { - node.setColor(readColor(element->attr("color").getText())); + node.setColor(element->attr("color").asColor()); } if (element->has("margin")) { - node.setMargin(readVec4(element->attr("margin").getText())); + node.setMargin(element->attr("margin").asVec4()); } std::string alignName = element->attr("align", "").getText(); @@ -138,7 +52,7 @@ static void _readPanel(UiXmlReader& reader, xml::xmlelement element, Panel& pane readUINode(element, panel); if (element->has("padding")) { - panel.setPadding(readVec4(element->attr("padding").getText())); + panel.setPadding(element->attr("padding").asVec4()); } if (element->has("size")) { @@ -185,7 +99,7 @@ static std::shared_ptr readButton(UiXmlReader& reader, xml::xmlelement e _readPanel(reader, element, *button); if (element->has("onclick")) { - runnable callback = scripting::create_runnable("", element->attr("onclick").getText()); + auto callback = scripting::create_runnable("", element->attr("onclick").getText()); button->listenAction([callback](GUI*) { callback(); }); @@ -202,6 +116,14 @@ static std::shared_ptr readTextBox(UiXmlReader& reader, xml::xmlelement auto textbox = std::make_shared(placeholder, glm::vec4(0.0f)); _readPanel(reader, element, *textbox); textbox->setText(text); + + if (element->has("consumer")) { + auto consumer = scripting::create_wstring_consumer( + element->attr("consumer").getText(), + reader.getFilename() + ); + textbox->textConsumer(consumer); + } return textbox; } @@ -230,7 +152,12 @@ std::shared_ptr UiXmlReader::readXML( const std::string& filename, const std::string& source ) { + this->filename = filename; auto document = xml::parse(filename, source); auto root = document->getRoot(); return readUINode(root); } + +const std::string& UiXmlReader::getFilename() const { + return filename; +} diff --git a/src/frontend/gui/gui_xml.h b/src/frontend/gui/gui_xml.h index e83252bc..288e72ff 100644 --- a/src/frontend/gui/gui_xml.h +++ b/src/frontend/gui/gui_xml.h @@ -14,6 +14,7 @@ namespace gui { class UiXmlReader { std::unordered_map readers; + std::string filename; public: UiXmlReader(); @@ -25,6 +26,8 @@ namespace gui { const std::string& filename, const std::string& source ); + + const std::string& getFilename() const; }; } diff --git a/src/logic/scripting/LuaState.cpp b/src/logic/scripting/LuaState.cpp new file mode 100644 index 00000000..3cd52dec --- /dev/null +++ b/src/logic/scripting/LuaState.cpp @@ -0,0 +1,168 @@ +#include "LuaState.h" + +#include "api_lua.h" + +void lua::LuaState::addfunc(const std::string& name, lua_CFunction func) { + lua_pushcfunction(L, func); + lua_setglobal(L, name.c_str()); +} + +bool lua::LuaState::getglobal(const std::string& name) { + lua_getglobal(L, name.c_str()); + if (lua_isnil(L, lua_gettop(L))) { + lua_pop(L, lua_gettop(L)); + return false; + } + return true; +} + +void lua::LuaState::setglobal(const std::string& name) { + lua_setglobal(L, name.c_str()); +} + +bool lua::LuaState::rename(const std::string& from, const std::string& to) { + const char* src = from.c_str(); + lua_getglobal(L, src); + if (lua_isnil(L, lua_gettop(L))) { + lua_pop(L, lua_gettop(L)); + return false; + } + lua_setglobal(L, to.c_str()); + + // remove previous + lua_pushnil(L); + lua_setglobal(L, src); + return true; +} + +void lua::LuaState::remove(const std::string& name) { + lua_pushnil(L); + lua_setglobal(L, name.c_str()); +} + +void lua::LuaState::createFuncs() { + openlib("pack", packlib, 0); + openlib("world", worldlib, 0); + openlib("player", playerlib, 0); + openlib("time", timelib, 0); + openlib("file", filelib, 0); + + addfunc("print", l_print); + + addfunc("block_index", l_block_index); + addfunc("block_name", l_block_name); + addfunc("blocks_count", l_blocks_count); + addfunc("is_solid_at", l_is_solid_at); + addfunc("is_replaceable_at", l_is_replaceable_at); + addfunc("set_block", l_set_block); + addfunc("get_block", l_get_block); + addfunc("get_block_X", l_get_block_x); + addfunc("get_block_Y", l_get_block_y); + addfunc("get_block_Z", l_get_block_z); + addfunc("get_block_states", l_get_block_states); + addfunc("get_block_user_bits", l_get_block_user_bits); + addfunc("set_block_user_bits", l_set_block_user_bits); +} + +lua::luaerror::luaerror(const std::string& message) : std::runtime_error(message) { +} + +lua::LuaState::LuaState() { + L = luaL_newstate(); + if (L == nullptr) { + throw lua::luaerror("could not to initialize Lua"); + } + // Allowed standard libraries + luaopen_base(L); + luaopen_math(L); + luaopen_string(L); + luaopen_table(L); + + std::cout << LUA_VERSION << std::endl; +# ifdef LUAJIT_VERSION + luaopen_jit(L); + std::cout << LUAJIT_VERSION << std::endl; +# endif // LUAJIT_VERSION + + createFuncs(); +} + +lua::LuaState::~LuaState() { + lua_close(L); +} + +void lua::LuaState::logError(const std::string& text) { + std::cerr << text << std::endl; +} + +void lua::LuaState::loadbuffer(const std::string& src, const std::string& file) { + if (luaL_loadbuffer(L, src.c_str(), src.length(), file.c_str())) { + throw lua::luaerror(lua_tostring(L, -1)); + } +} + +int lua::LuaState::call(int argc) { + if (lua_pcall(L, argc, LUA_MULTRET, 0)) { + throw lua::luaerror(lua_tostring(L, -1)); + } + return 1; +} + +int lua::LuaState::callNoThrow(int argc) { + if (lua_pcall(L, argc, LUA_MULTRET, 0)) { + logError(lua_tostring(L, -1)); + return 0; + } + return 1; +} + +int lua::LuaState::eval(const std::string& src, const std::string& file) { + auto srcText = "return ("+src+")"; + loadbuffer(srcText, file); + return call(0); +} + +int lua::LuaState::gettop() const { + return lua_gettop(L); +} + +int lua::LuaState::pushinteger(luaint x) { + lua_pushinteger(L, x); + return 1; +} + +int lua::LuaState::pushnumber(luanumber x) { + lua_pushnumber(L, x); + return 1; +} + +int lua::LuaState::pushivec3(luaint x, luaint y, luaint z) { + lua::pushivec3(L, x, y, z); + return 3; +} + +int lua::LuaState::pushstring(const std::string& str) { + lua_pushstring(L, str.c_str()); + return 1; +} + +bool lua::LuaState::toboolean(int index) { + return lua_toboolean(L, index); +} + +lua::luaint lua::LuaState::tointeger(int index) { + return lua_tointeger(L, index); +} + +void lua::LuaState::openlib(const std::string& name, const luaL_Reg* libfuncs, int nup) { + lua_newtable(L); + luaL_setfuncs(L, libfuncs, nup); + lua_setglobal(L, name.c_str()); +} + +const std::string lua::LuaState::storeAnonymous() { + auto funcId = uintptr_t(lua_topointer(L, lua_gettop(L))); + auto funcName = "F$"+std::to_string(funcId); + lua_setglobal(L, funcName.c_str()); + return funcName; +} \ No newline at end of file diff --git a/src/logic/scripting/LuaState.h b/src/logic/scripting/LuaState.h new file mode 100644 index 00000000..44f30003 --- /dev/null +++ b/src/logic/scripting/LuaState.h @@ -0,0 +1,48 @@ +#ifndef LOGIC_SCRIPTING_LUA_STATE_H_ +#define LOGIC_SCRIPTING_LUA_STATE_H_ + +#include +#include +#include + +namespace lua { + using luaint = lua_Integer; + using luanumber = lua_Number; + + class luaerror : public std::runtime_error { + public: + luaerror(const std::string& message); + }; + + class LuaState { + lua_State* L; + + void logError(const std::string& text); + public: + LuaState(); + ~LuaState(); + + void loadbuffer(const std::string& src, const std::string& file); + int gettop() const; + int pushivec3(luaint x, luaint y, luaint z); + int pushinteger(luaint x); + int pushnumber(luanumber x); + int pushstring(const std::string& str); + bool toboolean(int index); + luaint tointeger(int index); + int call(int argc); + int callNoThrow(int argc); + int eval(const std::string& src, const std::string& file=""); + void openlib(const std::string& name, const luaL_Reg* libfuncs, int nup); + void addfunc(const std::string& name, lua_CFunction func); + bool getglobal(const std::string& name); + void setglobal(const std::string& name); + bool rename(const std::string& from, const std::string& to); + void remove(const std::string& name);; + void createFuncs(); + + const std::string storeAnonymous(); + }; +} + +#endif // LOGIC_SCRIPTING_LUA_STATE_H_ diff --git a/src/logic/scripting/api_lua.cpp b/src/logic/scripting/api_lua.cpp deleted file mode 100644 index e9918ab5..00000000 --- a/src/logic/scripting/api_lua.cpp +++ /dev/null @@ -1,432 +0,0 @@ -#include "api_lua.h" -#include "scripting.h" - -#include -#include - -#include "../../files/files.h" -#include "../../physics/Hitbox.h" -#include "../../objects/Player.h" -#include "../../world/Level.h" -#include "../../world/World.h" -#include "../../content/Content.h" -#include "../../voxels/Block.h" -#include "../../voxels/Chunks.h" -#include "../../voxels/voxel.h" -#include "../../lighting/Lighting.h" -#include "../../logic/BlocksController.h" -#include "../../window/Window.h" -#include "../../engine.h" - -inline int lua_pushivec3(lua_State* L, int x, int y, int z) { - lua_pushinteger(L, x); - lua_pushinteger(L, y); - lua_pushinteger(L, z); - return 3; -} - -inline void openlib(lua_State* L, const char* name, const luaL_Reg* libfuncs, int nup) { - lua_newtable(L); - luaL_setfuncs(L, libfuncs, nup); - lua_setglobal(L, name); -} - -/* == file library == */ -static int l_file_resolve(lua_State* L) { - std::string path = lua_tostring(L, 1); - fs::path resolved = scripting::engine->getPaths()->resolve(path); - lua_pushstring(L, resolved.u8string().c_str()); - return 1; -} - -static int l_file_read(lua_State* L) { - auto paths = scripting::engine->getPaths(); - fs::path path = paths->resolve(lua_tostring(L, 1)); - if (fs::is_regular_file(path)) { - lua_pushstring(L, files::read_string(path).c_str()); - return 1; - } - return luaL_error(L, "file does not exists '%s'", path.u8string().c_str()); -} - -static int l_file_write(lua_State* L) { - auto paths = scripting::engine->getPaths(); - fs::path path = paths->resolve(lua_tostring(L, 1)); - const char* text = lua_tostring(L, 2); - files::write_string(path, text); - return 1; -} - -static int l_file_exists(lua_State* L) { - auto paths = scripting::engine->getPaths(); - fs::path path = paths->resolve(lua_tostring(L, 1)); - lua_pushboolean(L, fs::exists(path)); - return 1; -} - -static int l_file_isfile(lua_State* L) { - auto paths = scripting::engine->getPaths(); - fs::path path = paths->resolve(lua_tostring(L, 1)); - lua_pushboolean(L, fs::is_regular_file(path)); - return 1; -} - -static int l_file_isdir(lua_State* L) { - auto paths = scripting::engine->getPaths(); - fs::path path = paths->resolve(lua_tostring(L, 1)); - lua_pushboolean(L, fs::is_directory(path)); - return 1; -} - -static int l_file_length(lua_State* L) { - auto paths = scripting::engine->getPaths(); - fs::path path = paths->resolve(lua_tostring(L, 1)); - if (fs::exists(path)){ - lua_pushinteger(L, fs::file_size(path)); - } else { - lua_pushinteger(L, -1); - } - return 1; -} - -static int l_file_mkdir(lua_State* L) { - auto paths = scripting::engine->getPaths(); - fs::path path = paths->resolve(lua_tostring(L, 1)); - lua_pushboolean(L, fs::create_directory(path)); - return 1; -} - -static const luaL_Reg filelib [] = { - {"resolve", l_file_resolve}, - {"read", l_file_read}, - {"write", l_file_write}, - {"exists", l_file_exists}, - {"isfile", l_file_isfile}, - {"isdir", l_file_isdir}, - {"length", l_file_length}, - {"mkdir", l_file_mkdir}, - {NULL, NULL} -}; - -/* == time library == */ -static int l_time_uptime(lua_State* L) { - lua_pushnumber(L, Window::time()); - return 1; -} - -static const luaL_Reg timelib [] = { - {"uptime", l_time_uptime}, - {NULL, NULL} -}; - -/* == pack library == */ -static int l_pack_get_folder(lua_State* L) { - std::string packName = lua_tostring(L, 1); - if (packName == "core") { - auto folder = scripting::engine->getPaths() - ->getResources().u8string()+"/"; - lua_pushstring(L, folder.c_str()); - return 1; - } - for (auto& pack : scripting::engine->getContentPacks()) { - if (pack.id == packName) { - lua_pushstring(L, (pack.folder.u8string()+"/").c_str()); - return 1; - } - } - lua_pushstring(L, ""); - return 1; -} - -static const luaL_Reg packlib [] = { - {"get_folder", l_pack_get_folder}, - {NULL, NULL} -}; - -/* == world library == */ -static int l_world_get_total_time(lua_State* L) { - lua_pushnumber(L, scripting::level->world->totalTime); - return 1; -} - -static int l_world_get_day_time(lua_State* L) { - lua_pushnumber(L, scripting::level->world->daytime); - return 1; -} - -static int l_world_set_day_time(lua_State* L) { - double value = lua_tonumber(L, 1); - scripting::level->world->daytime = fmod(value, 1.0); - return 0; -} - -static int l_world_get_seed(lua_State* L) { - lua_pushinteger(L, scripting::level->world->getSeed()); - return 1; -} - -static const luaL_Reg worldlib [] = { - {"get_total_time", l_world_get_total_time}, - {"get_day_time", l_world_get_day_time}, - {"set_day_time", l_world_set_day_time}, - {"get_seed", l_world_get_seed}, - {NULL, NULL} -}; - -/* == player library ==*/ -static int l_player_get_pos(lua_State* L) { - int playerid = lua_tointeger(L, 1); - if (playerid != 1) - return 0; - glm::vec3 pos = scripting::level->player->hitbox->position; - lua_pushnumber(L, pos.x); - lua_pushnumber(L, pos.y); - lua_pushnumber(L, pos.z); - return 3; -} - -static int l_player_get_rot(lua_State* L) { - int playerid = lua_tointeger(L, 1); - if (playerid != 1) - return 0; - glm::vec2 rot = scripting::level->player->cam; - lua_pushnumber(L, rot.x); - lua_pushnumber(L, rot.y); - return 2; -} - -static int l_player_set_rot(lua_State* L) { - int playerid = lua_tointeger(L, 1); - if (playerid != 1) - return 0; - double x = lua_tonumber(L, 2); - double y = lua_tonumber(L, 3); - glm::vec2& cam = scripting::level->player->cam; - cam.x = x; - cam.y = y; - return 0; -} - -static int l_player_set_pos(lua_State* L) { - int playerid = lua_tointeger(L, 1); - if (playerid != 1) - return 0; - double x = lua_tonumber(L, 2); - double y = lua_tonumber(L, 3); - double z = lua_tonumber(L, 4); - scripting::level->player->hitbox->position = glm::vec3(x, y, z); - return 0; -} - -static const luaL_Reg playerlib [] = { - {"get_pos", l_player_get_pos}, - {"set_pos", l_player_set_pos}, - {"get_rot", l_player_get_rot}, - {"set_rot", l_player_set_rot}, - {NULL, NULL} -}; - -/* == blocks-related functions == */ -static int l_block_name(lua_State* L) { - int id = lua_tointeger(L, 1); - auto def = scripting::content->getIndices()->getBlockDef(id); - lua_pushstring(L, def->name.c_str()); - return 1; -} - -static int l_is_solid_at(lua_State* L) { - int x = lua_tointeger(L, 1); - int y = lua_tointeger(L, 2); - int z = lua_tointeger(L, 3); - - lua_pushboolean(L, scripting::level->chunks->isSolidBlock(x, y, z)); - return 1; -} - -static int l_blocks_count(lua_State* L) { - lua_pushinteger(L, scripting::content->getIndices()->countBlockDefs()); - return 1; -} - -static int l_block_index(lua_State* L) { - auto name = lua_tostring(L, 1); - lua_pushinteger(L, scripting::content->requireBlock(name)->rt.id); - return 1; -} - -static int l_set_block(lua_State* L) { - int x = lua_tointeger(L, 1); - int y = lua_tointeger(L, 2); - int z = lua_tointeger(L, 3); - int id = lua_tointeger(L, 4); - int states = lua_tointeger(L, 5); - bool noupdate = lua_toboolean(L, 6); - scripting::level->chunks->set(x, y, z, id, states); - scripting::level->lighting->onBlockSet(x,y,z, id); - if (!noupdate) - scripting::blocks->updateSides(x, y, z); - return 0; -} - -static int l_get_block(lua_State* L) { - int x = lua_tointeger(L, 1); - int y = lua_tointeger(L, 2); - int z = lua_tointeger(L, 3); - voxel* vox = scripting::level->chunks->get(x, y, z); - int id = vox == nullptr ? -1 : vox->id; - lua_pushinteger(L, id); - return 1; -} - -static int l_get_block_x(lua_State* L) { - int x = lua_tointeger(L, 1); - int y = lua_tointeger(L, 2); - int z = lua_tointeger(L, 3); - voxel* vox = scripting::level->chunks->get(x, y, z); - if (vox == nullptr) { - return lua_pushivec3(L, 1, 0, 0); - } - auto def = scripting::level->content->getIndices()->getBlockDef(vox->id); - if (!def->rotatable) { - return lua_pushivec3(L, 1, 0, 0); - } else { - const CoordSystem& rot = def->rotations.variants[vox->rotation()]; - return lua_pushivec3(L, rot.axisX.x, rot.axisX.y, rot.axisX.z); - } -} - -static int l_get_block_y(lua_State* L) { - int x = lua_tointeger(L, 1); - int y = lua_tointeger(L, 2); - int z = lua_tointeger(L, 3); - voxel* vox = scripting::level->chunks->get(x, y, z); - if (vox == nullptr) { - return lua_pushivec3(L, 0, 1, 0); - } - auto def = scripting::level->content->getIndices()->getBlockDef(vox->id); - if (!def->rotatable) { - return lua_pushivec3(L, 0, 1, 0); - } else { - const CoordSystem& rot = def->rotations.variants[vox->rotation()]; - return lua_pushivec3(L, rot.axisY.x, rot.axisY.y, rot.axisY.z); - } -} - -static int l_get_block_z(lua_State* L) { - int x = lua_tointeger(L, 1); - int y = lua_tointeger(L, 2); - int z = lua_tointeger(L, 3); - voxel* vox = scripting::level->chunks->get(x, y, z); - if (vox == nullptr) { - return lua_pushivec3(L, 0, 0, 1); - } - auto def = scripting::level->content->getIndices()->getBlockDef(vox->id); - if (!def->rotatable) { - return lua_pushivec3(L, 0, 0, 1); - } else { - const CoordSystem& rot = def->rotations.variants[vox->rotation()]; - return lua_pushivec3(L, rot.axisZ.x, rot.axisZ.y, rot.axisZ.z); - } -} - -static int l_get_block_states(lua_State* L) { - int x = lua_tointeger(L, 1); - int y = lua_tointeger(L, 2); - int z = lua_tointeger(L, 3); - voxel* vox = scripting::level->chunks->get(x, y, z); - int states = vox == nullptr ? 0 : vox->states; - lua_pushinteger(L, states); - return 1; -} - -static int l_get_block_user_bits(lua_State* L) { - int x = lua_tointeger(L, 1); - int y = lua_tointeger(L, 2); - int z = lua_tointeger(L, 3); - int offset = lua_tointeger(L, 4) + VOXEL_USER_BITS_OFFSET; - int bits = lua_tointeger(L, 5); - - voxel* vox = scripting::level->chunks->get(x, y, z); - if (vox == nullptr) { - lua_pushinteger(L, 0); - return 1; - } - uint mask = ((1 << bits) - 1) << offset; - uint data = (vox->states & mask) >> offset; - lua_pushinteger(L, data); - return 1; -} - -static int l_set_block_user_bits(lua_State* L) { - int x = lua_tointeger(L, 1); - int y = lua_tointeger(L, 2); - int z = lua_tointeger(L, 3); - int offset = lua_tointeger(L, 4) + VOXEL_USER_BITS_OFFSET; - int bits = lua_tointeger(L, 5); - - uint mask = ((1 << bits) - 1) << offset; - int value = (lua_tointeger(L, 6) << offset) & mask; - - voxel* vox = scripting::level->chunks->get(x, y, z); - if (vox == nullptr) { - return 0; - } - vox->states = (vox->states & (~mask)) | value; - return 0; -} - -static int l_is_replaceable_at(lua_State* L) { - int x = lua_tointeger(L, 1); - int y = lua_tointeger(L, 2); - int z = lua_tointeger(L, 3); - - lua_pushboolean(L, scripting::level->chunks->isReplaceableBlock(x, y, z)); - return 1; -} - -// Modified version of luaB_print from lbaselib.c -static int l_print(lua_State* L) { - int n = lua_gettop(L); /* number of arguments */ - lua_getglobal(L, "tostring"); - for (int i=1; i<=n; i++) { - lua_pushvalue(L, -1); /* function to be called */ - lua_pushvalue(L, i); /* value to print */ - lua_call(L, 1, 1); - const char* s = lua_tostring(L, -1); /* get result */ - if (s == NULL) - return luaL_error(L, LUA_QL("tostring") " must return a string to " - LUA_QL("print")); - if (i > 1) - std::cout << "\t"; - std::cout << s; - lua_pop(L, 1); /* pop result */ - } - std::cout << std::endl; - return 0; -} - -#define lua_addfunc(L, FUNC, NAME) (lua_pushcfunction(L, FUNC),\ - lua_setglobal(L, NAME)) - -void apilua::create_funcs(lua_State* L) { - openlib(L, "pack", packlib, 0); - openlib(L, "world", worldlib, 0); - openlib(L, "player", playerlib, 0); - openlib(L, "time", timelib, 0); - openlib(L, "file", filelib, 0); - - lua_addfunc(L, l_print, "print"); - lua_addfunc(L, l_block_index, "block_index"); - lua_addfunc(L, l_block_name, "block_name"); - lua_addfunc(L, l_blocks_count, "blocks_count"); - lua_addfunc(L, l_is_solid_at, "is_solid_at"); - lua_addfunc(L, l_is_replaceable_at, "is_replaceable_at"); - lua_addfunc(L, l_set_block, "set_block"); - lua_addfunc(L, l_get_block, "get_block"); - lua_addfunc(L, l_get_block_x, "get_block_X"); - lua_addfunc(L, l_get_block_y, "get_block_Y"); - lua_addfunc(L, l_get_block_z, "get_block_Z"); - lua_addfunc(L, l_get_block_states, "get_block_states"); - lua_addfunc(L, l_get_block_user_bits, "get_block_user_bits"); - lua_addfunc(L, l_set_block_user_bits, "set_block_user_bits"); -} diff --git a/src/logic/scripting/api_lua.h b/src/logic/scripting/api_lua.h index e48a6548..7ea0d477 100644 --- a/src/logic/scripting/api_lua.h +++ b/src/logic/scripting/api_lua.h @@ -1,14 +1,398 @@ #ifndef LOGIC_SCRIPTING_API_LUA_H_ #define LOGIC_SCRIPTING_API_LUA_H_ -#include +#include "scripting.h" +#include "lua_util.h" -#ifndef LUAJIT_VERSION -#pragma message("better use LuaJIT instead of plain Lua") -#endif // LUAJIT_VERSION +#include +#include -namespace apilua { - extern void create_funcs(lua_State* L); +#include "../../files/files.h" +#include "../../physics/Hitbox.h" +#include "../../objects/Player.h" +#include "../../world/Level.h" +#include "../../world/World.h" +#include "../../content/Content.h" +#include "../../voxels/Block.h" +#include "../../voxels/Chunks.h" +#include "../../voxels/voxel.h" +#include "../../lighting/Lighting.h" +#include "../../logic/BlocksController.h" +#include "../../window/Window.h" +#include "../../engine.h" + +/* == file library == */ +static int l_file_resolve(lua_State* L) { + std::string path = lua_tostring(L, 1); + fs::path resolved = scripting::engine->getPaths()->resolve(path); + lua_pushstring(L, resolved.u8string().c_str()); + return 1; +} + +static int l_file_read(lua_State* L) { + auto paths = scripting::engine->getPaths(); + fs::path path = paths->resolve(lua_tostring(L, 1)); + if (fs::is_regular_file(path)) { + lua_pushstring(L, files::read_string(path).c_str()); + return 1; + } + return luaL_error(L, "file does not exists '%s'", path.u8string().c_str()); +} + +static int l_file_write(lua_State* L) { + auto paths = scripting::engine->getPaths(); + fs::path path = paths->resolve(lua_tostring(L, 1)); + const char* text = lua_tostring(L, 2); + files::write_string(path, text); + return 1; +} + +static int l_file_exists(lua_State* L) { + auto paths = scripting::engine->getPaths(); + fs::path path = paths->resolve(lua_tostring(L, 1)); + lua_pushboolean(L, fs::exists(path)); + return 1; +} + +static int l_file_isfile(lua_State* L) { + auto paths = scripting::engine->getPaths(); + fs::path path = paths->resolve(lua_tostring(L, 1)); + lua_pushboolean(L, fs::is_regular_file(path)); + return 1; +} + +static int l_file_isdir(lua_State* L) { + auto paths = scripting::engine->getPaths(); + fs::path path = paths->resolve(lua_tostring(L, 1)); + lua_pushboolean(L, fs::is_directory(path)); + return 1; +} + +static int l_file_length(lua_State* L) { + auto paths = scripting::engine->getPaths(); + fs::path path = paths->resolve(lua_tostring(L, 1)); + if (fs::exists(path)){ + lua_pushinteger(L, fs::file_size(path)); + } else { + lua_pushinteger(L, -1); + } + return 1; +} + +static int l_file_mkdir(lua_State* L) { + auto paths = scripting::engine->getPaths(); + fs::path path = paths->resolve(lua_tostring(L, 1)); + lua_pushboolean(L, fs::create_directory(path)); + return 1; +} + +static const luaL_Reg filelib [] = { + {"resolve", l_file_resolve}, + {"read", l_file_read}, + {"write", l_file_write}, + {"exists", l_file_exists}, + {"isfile", l_file_isfile}, + {"isdir", l_file_isdir}, + {"length", l_file_length}, + {"mkdir", l_file_mkdir}, + {NULL, NULL} +}; + +/* == time library == */ +static int l_time_uptime(lua_State* L) { + lua_pushnumber(L, Window::time()); + return 1; +} + +static const luaL_Reg timelib [] = { + {"uptime", l_time_uptime}, + {NULL, NULL} +}; + +/* == pack library == */ +static int l_pack_get_folder(lua_State* L) { + std::string packName = lua_tostring(L, 1); + if (packName == "core") { + auto folder = scripting::engine->getPaths() + ->getResources().u8string()+"/"; + lua_pushstring(L, folder.c_str()); + return 1; + } + for (auto& pack : scripting::engine->getContentPacks()) { + if (pack.id == packName) { + lua_pushstring(L, (pack.folder.u8string()+"/").c_str()); + return 1; + } + } + lua_pushstring(L, ""); + return 1; +} + +static const luaL_Reg packlib [] = { + {"get_folder", l_pack_get_folder}, + {NULL, NULL} +}; + +/* == world library == */ +static int l_world_get_total_time(lua_State* L) { + lua_pushnumber(L, scripting::level->world->totalTime); + return 1; +} + +static int l_world_get_day_time(lua_State* L) { + lua_pushnumber(L, scripting::level->world->daytime); + return 1; +} + +static int l_world_set_day_time(lua_State* L) { + double value = lua_tonumber(L, 1); + scripting::level->world->daytime = fmod(value, 1.0); + return 0; +} + +static int l_world_get_seed(lua_State* L) { + lua_pushinteger(L, scripting::level->world->getSeed()); + return 1; +} + +static const luaL_Reg worldlib [] = { + {"get_total_time", l_world_get_total_time}, + {"get_day_time", l_world_get_day_time}, + {"set_day_time", l_world_set_day_time}, + {"get_seed", l_world_get_seed}, + {NULL, NULL} +}; + +/* == player library ==*/ +static int l_player_get_pos(lua_State* L) { + int playerid = lua_tointeger(L, 1); + if (playerid != 1) + return 0; + glm::vec3 pos = scripting::level->player->hitbox->position; + lua_pushnumber(L, pos.x); + lua_pushnumber(L, pos.y); + lua_pushnumber(L, pos.z); + return 3; +} + +static int l_player_get_rot(lua_State* L) { + int playerid = lua_tointeger(L, 1); + if (playerid != 1) + return 0; + glm::vec2 rot = scripting::level->player->cam; + lua_pushnumber(L, rot.x); + lua_pushnumber(L, rot.y); + return 2; +} + +static int l_player_set_rot(lua_State* L) { + int playerid = lua_tointeger(L, 1); + if (playerid != 1) + return 0; + double x = lua_tonumber(L, 2); + double y = lua_tonumber(L, 3); + glm::vec2& cam = scripting::level->player->cam; + cam.x = x; + cam.y = y; + return 0; +} + +static int l_player_set_pos(lua_State* L) { + int playerid = lua_tointeger(L, 1); + if (playerid != 1) + return 0; + double x = lua_tonumber(L, 2); + double y = lua_tonumber(L, 3); + double z = lua_tonumber(L, 4); + scripting::level->player->hitbox->position = glm::vec3(x, y, z); + return 0; +} + +static const luaL_Reg playerlib [] = { + {"get_pos", l_player_get_pos}, + {"set_pos", l_player_set_pos}, + {"get_rot", l_player_get_rot}, + {"set_rot", l_player_set_rot}, + {NULL, NULL} +}; + +/* == blocks-related functions == */ +static int l_block_name(lua_State* L) { + int id = lua_tointeger(L, 1); + auto def = scripting::content->getIndices()->getBlockDef(id); + lua_pushstring(L, def->name.c_str()); + return 1; +} + +static int l_is_solid_at(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + + lua_pushboolean(L, scripting::level->chunks->isSolidBlock(x, y, z)); + return 1; +} + +static int l_blocks_count(lua_State* L) { + lua_pushinteger(L, scripting::content->getIndices()->countBlockDefs()); + return 1; +} + +static int l_block_index(lua_State* L) { + auto name = lua_tostring(L, 1); + lua_pushinteger(L, scripting::content->requireBlock(name)->rt.id); + return 1; +} + +static int l_set_block(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + int id = lua_tointeger(L, 4); + int states = lua_tointeger(L, 5); + bool noupdate = lua_toboolean(L, 6); + scripting::level->chunks->set(x, y, z, id, states); + scripting::level->lighting->onBlockSet(x,y,z, id); + if (!noupdate) + scripting::blocks->updateSides(x, y, z); + return 0; +} + +static int l_get_block(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + voxel* vox = scripting::level->chunks->get(x, y, z); + int id = vox == nullptr ? -1 : vox->id; + lua_pushinteger(L, id); + return 1; +} + +static int l_get_block_x(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + voxel* vox = scripting::level->chunks->get(x, y, z); + if (vox == nullptr) { + return lua::pushivec3(L, 1, 0, 0); + } + auto def = scripting::level->content->getIndices()->getBlockDef(vox->id); + if (!def->rotatable) { + return lua::pushivec3(L, 1, 0, 0); + } else { + const CoordSystem& rot = def->rotations.variants[vox->rotation()]; + return lua::pushivec3(L, rot.axisX.x, rot.axisX.y, rot.axisX.z); + } +} + +static int l_get_block_y(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + voxel* vox = scripting::level->chunks->get(x, y, z); + if (vox == nullptr) { + return lua::pushivec3(L, 0, 1, 0); + } + auto def = scripting::level->content->getIndices()->getBlockDef(vox->id); + if (!def->rotatable) { + return lua::pushivec3(L, 0, 1, 0); + } else { + const CoordSystem& rot = def->rotations.variants[vox->rotation()]; + return lua::pushivec3(L, rot.axisY.x, rot.axisY.y, rot.axisY.z); + } +} + +static int l_get_block_z(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + voxel* vox = scripting::level->chunks->get(x, y, z); + if (vox == nullptr) { + return lua::pushivec3(L, 0, 0, 1); + } + auto def = scripting::level->content->getIndices()->getBlockDef(vox->id); + if (!def->rotatable) { + return lua::pushivec3(L, 0, 0, 1); + } else { + const CoordSystem& rot = def->rotations.variants[vox->rotation()]; + return lua::pushivec3(L, rot.axisZ.x, rot.axisZ.y, rot.axisZ.z); + } +} + +static int l_get_block_states(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + voxel* vox = scripting::level->chunks->get(x, y, z); + int states = vox == nullptr ? 0 : vox->states; + lua_pushinteger(L, states); + return 1; +} + +static int l_get_block_user_bits(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + int offset = lua_tointeger(L, 4) + VOXEL_USER_BITS_OFFSET; + int bits = lua_tointeger(L, 5); + + voxel* vox = scripting::level->chunks->get(x, y, z); + if (vox == nullptr) { + lua_pushinteger(L, 0); + return 1; + } + uint mask = ((1 << bits) - 1) << offset; + uint data = (vox->states & mask) >> offset; + lua_pushinteger(L, data); + return 1; +} + +static int l_set_block_user_bits(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + int offset = lua_tointeger(L, 4) + VOXEL_USER_BITS_OFFSET; + int bits = lua_tointeger(L, 5); + + uint mask = ((1 << bits) - 1) << offset; + int value = (lua_tointeger(L, 6) << offset) & mask; + + voxel* vox = scripting::level->chunks->get(x, y, z); + if (vox == nullptr) { + return 0; + } + vox->states = (vox->states & (~mask)) | value; + return 0; +} + +static int l_is_replaceable_at(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + + lua_pushboolean(L, scripting::level->chunks->isReplaceableBlock(x, y, z)); + return 1; +} + +// Modified version of luaB_print from lbaselib.c +static int l_print(lua_State* L) { + int n = lua_gettop(L); /* number of arguments */ + lua_getglobal(L, "tostring"); + for (int i=1; i<=n; i++) { + lua_pushvalue(L, -1); /* function to be called */ + lua_pushvalue(L, i); /* value to print */ + lua_call(L, 1, 1); + const char* s = lua_tostring(L, -1); /* get result */ + if (s == NULL) + return luaL_error(L, LUA_QL("tostring") " must return a string to " + LUA_QL("print")); + if (i > 1) + std::cout << "\t"; + std::cout << s; + lua_pop(L, 1); /* pop result */ + } + std::cout << std::endl; + return 0; } #endif // LOGIC_SCRIPTING_API_LUA_H_ diff --git a/src/logic/scripting/lua_util.h b/src/logic/scripting/lua_util.h new file mode 100644 index 00000000..2ad5c9e2 --- /dev/null +++ b/src/logic/scripting/lua_util.h @@ -0,0 +1,15 @@ +#ifndef LOGIC_SCRIPTING_LUA_UTIL_H_ +#define LOGIC_SCRIPTING_LUA_UTIL_H_ + +#include + +namespace lua { + inline int pushivec3(lua_State* L, luaint x, luaint y, luaint z) { + lua_pushinteger(L, x); + lua_pushinteger(L, y); + lua_pushinteger(L, z); + return 3; + } +} + +#endif // LOGIC_SCRIPTING_LUA_UTIL_H_ diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 064dfbae..f604cd56 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -2,7 +2,6 @@ #include #include -#include #include "../../content/ContentPack.h" #include "../../files/engine_paths.h" @@ -13,105 +12,68 @@ #include "../../items/ItemDef.h" #include "../../logic/BlocksController.h" #include "../../engine.h" -#include "api_lua.h" +#include "LuaState.h" +#include "../../util/stringutil.h" using namespace scripting; namespace scripting { - extern lua_State* L; + extern lua::LuaState* state; } Engine* scripting::engine = nullptr; -lua_State* scripting::L = nullptr; +lua::LuaState* scripting::state = nullptr; Level* scripting::level = nullptr; const Content* scripting::content = nullptr; BlocksController* scripting::blocks = nullptr; -static void handleError(lua_State* L) { - std::cerr << "lua error: " << lua_tostring(L,-1) << std::endl; -} - -inline int lua_pushivec3(lua_State* L, int x, int y, int z) { - lua_pushinteger(L, x); - lua_pushinteger(L, y); - lua_pushinteger(L, z); - return 3; -} - -void delete_global(lua_State* L, const char* name) { - lua_pushnil(L); - lua_setglobal(L, name); -} - -bool rename_global(lua_State* L, const char* src, const char* dst) { - lua_getglobal(L, src); - if (lua_isnil(L, lua_gettop(L))) { - lua_pop(L, lua_gettop(L)); - return false; - } - lua_setglobal(L, dst); - delete_global(L, src); - return true; -} - -int call_func(lua_State* L, int argc, const std::string& name) { - if (lua_pcall(L, argc, LUA_MULTRET, 0)) { - handleError(L); - return 0; - } - return 1; -} - void load_script(fs::path name) { auto paths = scripting::engine->getPaths(); fs::path file = paths->getResources()/fs::path("scripts")/name; std::string src = files::read_string(file); - if (luaL_loadbuffer(L, src.c_str(), src.length(), file.u8string().c_str())) { - handleError(L); - return; - } - call_func(L, 0, file.u8string()); + + state->loadbuffer(src, file.u8string()); + state->callNoThrow(0); } void scripting::initialize(Engine* engine) { scripting::engine = engine; - L = luaL_newstate(); - if (L == nullptr) { - throw std::runtime_error("could not to initialize Lua"); - } - - // Allowed standard libraries - luaopen_base(L); - luaopen_math(L); - luaopen_string(L); - luaopen_table(L); - - // io-manipulations will be implemented via api functions - - std::cout << LUA_VERSION << std::endl; -# ifdef LUAJIT_VERSION - luaopen_jit(L); - std::cout << LUAJIT_VERSION << std::endl; -# endif // LUAJIT_VERSION - - apilua::create_funcs(L); + state = new lua::LuaState(); load_script(fs::path("stdlib.lua")); } // todo: luaL state check runnable scripting::create_runnable( - const std::string& filename, + const std::string& file, const std::string& src ) { return [=](){ - if (luaL_loadbuffer(L, src.c_str(), src.length(), filename.c_str())) { - handleError(L); - return; + state->loadbuffer(src, file); + state->callNoThrow(0); + }; +} + +wstringconsumer scripting::create_wstring_consumer( + const std::string& src, + const std::string& file +) { + try { + if (state->eval(src, file) == 0) + return [](const std::wstring& _) {}; + } catch (const lua::luaerror& err) { + std::cerr << err.what() << std::endl; + return [](const std::wstring& _) {}; + } + + auto funcName = state->storeAnonymous(); + return [=](const std::wstring& x){ + if (state->getglobal(funcName)) { + state->pushstring(util::wstr2str_utf8(x)); + state->callNoThrow(1); } - call_func(L, 0, filename); }; } @@ -122,37 +84,25 @@ void scripting::on_world_load(Level* level, BlocksController* blocks) { load_script("world.lua"); for (auto& pack : scripting::engine->getContentPacks()) { - std::string name = pack.id+".worldopen"; - lua_getglobal(L, name.c_str()); - if (lua_isnil(L, lua_gettop(L))) { - lua_pop(L, lua_gettop(L)); - continue; + if (state->getglobal(pack.id+".worldopen")) { + state->callNoThrow(0); } - call_func(L, 0, name); } } void scripting::on_world_save() { for (auto& pack : scripting::engine->getContentPacks()) { - std::string name = pack.id+".worldsave"; - lua_getglobal(L, name.c_str()); - if (lua_isnil(L, lua_gettop(L))) { - lua_pop(L, lua_gettop(L)); - continue; + if (state->getglobal(pack.id+".worldsave")) { + state->callNoThrow(0); } - call_func(L, 0, name); } } void scripting::on_world_quit() { for (auto& pack : scripting::engine->getContentPacks()) { - std::string name = pack.id+".worldquit"; - lua_getglobal(L, name.c_str()); - if (lua_isnil(L, lua_gettop(L))) { - lua_pop(L, lua_gettop(L)); - continue; + if (state->getglobal(pack.id+".worldquit")) { + state->callNoThrow(0); } - call_func(L, 0, name); } scripting::level = nullptr; scripting::content = nullptr; @@ -160,124 +110,125 @@ void scripting::on_world_quit() { void scripting::on_blocks_tick(const Block* block, int tps) { std::string name = block->name+".blockstick"; - lua_getglobal(L, name.c_str()); - lua_pushinteger(L, tps); - call_func(L, 1, name); + if (state->getglobal(name)) { + state->pushinteger(tps); + state->callNoThrow(1); + } } void scripting::update_block(const Block* block, int x, int y, int z) { std::string name = block->name+".update"; - lua_getglobal(L, name.c_str()); - lua_pushivec3(L, x, y, z); - call_func(L, 3, name); + if (state->getglobal(name)) { + state->pushivec3(x, y, z); + state->callNoThrow(3); + } } void scripting::random_update_block(const Block* block, int x, int y, int z) { std::string name = block->name+".randupdate"; - lua_getglobal(L, name.c_str()); - lua_pushivec3(L, x, y, z); - call_func(L, 3, name); + if (state->getglobal(name)) { + state->pushivec3(x, y, z); + state->callNoThrow(3); + } } void scripting::on_block_placed(Player* player, const Block* block, int x, int y, int z) { std::string name = block->name+".placed"; - lua_getglobal(L, name.c_str()); - lua_pushivec3(L, x, y, z); - lua_pushinteger(L, 1); // player id placeholder - call_func(L, 4, name); + if (state->getglobal(name)) { + state->pushivec3(x, y, z); + state->pushinteger(1); // playerid placeholder + state->callNoThrow(4); + } } void scripting::on_block_broken(Player* player, const Block* block, int x, int y, int z) { std::string name = block->name+".broken"; - lua_getglobal(L, name.c_str()); - lua_pushivec3(L, x, y, z); - lua_pushinteger(L, 1); // player id placeholder - call_func(L, 4, name); + if (state->getglobal(name)) { + state->pushivec3(x, y, z); + state->pushinteger(1); // playerid placeholder + state->callNoThrow(4); + } } bool scripting::on_block_interact(Player* player, const Block* block, int x, int y, int z) { std::string name = block->name+".oninteract"; - lua_getglobal(L, name.c_str()); - lua_pushivec3(L, x, y, z); - lua_pushinteger(L, 1); - if (call_func(L, 4, name)) { - return lua_toboolean(L, -1); + if (state->getglobal(name)) { + state->pushivec3(x, y, z); + state->pushinteger(1); // playerid placeholder + if (state->callNoThrow(4)) { + return state->toboolean(-1); + } } return false; } bool scripting::on_item_use_on_block(Player* player, const ItemDef* item, int x, int y, int z) { std::string name = item->name+".useon"; - lua_getglobal(L, name.c_str()); - lua_pushivec3(L, x, y, z); - lua_pushinteger(L, 1); // player id placeholder - if (call_func(L, 4, name)) { - return lua_toboolean(L, -1); + if (state->getglobal(name)) { + state->pushivec3(x, y, z); + state->pushinteger(1); // playerid placeholder + if (state->callNoThrow(4)) { + return state->toboolean(-1); + } } return false; } bool scripting::on_item_break_block(Player* player, const ItemDef* item, int x, int y, int z) { std::string name = item->name+".blockbreakby"; - lua_getglobal(L, name.c_str()); - lua_pushivec3(L, x, y, z); - lua_pushinteger(L, 1); // player id placeholder - if (call_func(L, 4, name)) { - return lua_toboolean(L, -1); + if (state->getglobal(name)) { + state->pushivec3(x, y, z); + state->pushinteger(1); // playerid placeholder + if (state->callNoThrow(4)) { + return state->toboolean(-1); + } } return false; } -// todo: refactor - void scripting::load_block_script(std::string prefix, fs::path file, block_funcs_set* funcsset) { std::string src = files::read_string(file); std::cout << "loading script " << file.u8string() << std::endl; - if (luaL_loadbuffer(L, src.c_str(), src.size(), file.string().c_str())) { - handleError(L); - return; - } - call_func(L, 0, "