diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 4968b675..50218698 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -120,6 +120,26 @@ function color_mt.__tostring(self) return "rgba("..self[1]..", "..self[2]..", "..self[3]..", "..self[4]..")" end +-- events +events = { + handlers = {} +} + +function events.on(event, func) + events.handlers[event] = events.handlers[event] or {} + table.insert(events.handlers[event], func) +end + +function events.emit(event, ...) + result = nil + if events.handlers[event] then + for _, func in ipairs(events.handlers[event]) do + result = result or func(...) + end + end + return result +end + -- class designed for simple UI-nodes access via properties syntax local Element = {} function Element.new(docname, name) diff --git a/src/logic/scripting/lua/LuaState.cpp b/src/logic/scripting/lua/LuaState.cpp index e7988dd5..e472831d 100644 --- a/src/logic/scripting/lua/LuaState.cpp +++ b/src/logic/scripting/lua/LuaState.cpp @@ -1,5 +1,6 @@ #include "LuaState.h" +#include #include #include "lua_util.h" #include "api_lua.h" @@ -217,8 +218,8 @@ int lua::LuaState::pushnil() { return 1; } -bool lua::LuaState::getfield(const std::string& name) { - lua_getfield(L, -1, name.c_str()); +bool lua::LuaState::getfield(const std::string& name, int idx) { + lua_getfield(L, idx, name.c_str()); if (lua_isnil(L, -1)) { lua_pop(L, -1); return false; @@ -287,3 +288,28 @@ void lua::LuaState::removeEnvironment(int id) { lua_pushnil(L); setglobal(envName(id)); } + +void lua::LuaState::dumpStack() { + int top = gettop(); + for (int i = 1; i <= top; i++) { + std::cout << std::setw(3) << i << std::setw(20) << luaL_typename(L, i) << std::setw(30); + switch (lua_type(L, i)) { + case LUA_TNUMBER: + std::cout << tonumber(i); + break; + case LUA_TSTRING: + std::cout << tostring(i); + break; + case LUA_TBOOLEAN: + std::cout << (toboolean(i) ? "true" : "false"); + break; + case LUA_TNIL: + std::cout << "nil"; + break; + default: + std::cout << lua_topointer(L, i); + break; + } + std::cout << std::endl; + } +} diff --git a/src/logic/scripting/lua/LuaState.h b/src/logic/scripting/lua/LuaState.h index 319ee280..d9a7e49d 100644 --- a/src/logic/scripting/lua/LuaState.h +++ b/src/logic/scripting/lua/LuaState.h @@ -41,8 +41,8 @@ namespace lua { int pushnil(); int pushglobals(); void pop(int n=1); - bool getfield(const std::string& name); - void setfield(const std::string& name, int idx=-2); + bool getfield(const std::string& name, int idx = -1); + void setfield(const std::string& name, int idx = -2); bool toboolean(int idx); luaint tointeger(int idx); luanumber tonumber(int idx); @@ -61,6 +61,8 @@ namespace lua { int createEnvironment(int parent); void removeEnvironment(int id); const std::string storeAnonymous(); + + void dumpStack(); }; } diff --git a/src/logic/scripting/lua/libblock.cpp b/src/logic/scripting/lua/libblock.cpp index 56b3d963..2f0c18a8 100644 --- a/src/logic/scripting/lua/libblock.cpp +++ b/src/logic/scripting/lua/libblock.cpp @@ -228,6 +228,8 @@ const luaL_Reg blocklib [] = { {"get_Z", lua_wrap_errors}, {"get_states", lua_wrap_errors}, {"set_states", lua_wrap_errors}, + {"get_rotation", lua_wrap_errors}, + {"set_rotation", lua_wrap_errors}, {"get_user_bits", lua_wrap_errors}, {"set_user_bits", lua_wrap_errors}, {NULL, NULL} diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index d5bc3e6c..350e2949 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -105,25 +105,19 @@ void scripting::on_world_load(Level* level, BlocksController* blocks) { load_script("world.lua"); for (auto& pack : scripting::engine->getContentPacks()) { - if (state->getglobal(pack.id+".worldopen")) { - state->callNoThrow(0); - } + emit_event(pack.id + ".worldopen"); } } void scripting::on_world_save() { for (auto& pack : scripting::engine->getContentPacks()) { - if (state->getglobal(pack.id+".worldsave")) { - state->callNoThrow(0); - } + emit_event(pack.id + ".worldsave"); } } void scripting::on_world_quit() { for (auto& pack : scripting::engine->getContentPacks()) { - if (state->getglobal(pack.id+".worldquit")) { - state->callNoThrow(0); - } + emit_event(pack.id + ".worldquit"); } if (state->getglobal("__scripts_cleanup")) { state->callNoThrow(0); @@ -134,109 +128,97 @@ void scripting::on_world_quit() { } void scripting::on_blocks_tick(const Block* block, int tps) { - std::string name = block->name+".blockstick"; - if (state->getglobal(name)) { + std::string name = block->name + ".blockstick"; + emit_event(name, [tps] (lua::LuaState* state) { state->pushinteger(tps); - state->callNoThrow(1); - } + return 1; + }); } void scripting::update_block(const Block* block, int x, int y, int z) { - std::string name = block->name+".update"; - if (state->getglobal(name)) { + std::string name = block->name + ".update"; + emit_event(name, [x, y, z] (lua::LuaState* state) { state->pushivec3(x, y, z); - state->callNoThrow(3); - } + return 3; + }); } void scripting::random_update_block(const Block* block, int x, int y, int z) { - std::string name = block->name+".randupdate"; - if (state->getglobal(name)) { + std::string name = block->name + ".randupdate"; + emit_event(name, [x, y, z] (lua::LuaState* state) { state->pushivec3(x, y, z); - state->callNoThrow(3); - } + return 3; + }); } void scripting::on_block_placed(Player* player, const Block* block, int x, int y, int z) { - std::string name = block->name+".placed"; - if (state->getglobal(name)) { + std::string name = block->name + ".placed"; + emit_event(name, [x, y, z, player] (lua::LuaState* state) { state->pushivec3(x, y, z); state->pushinteger(player->getId()); - state->callNoThrow(4); - } + return 4; + }); } void scripting::on_block_broken(Player* player, const Block* block, int x, int y, int z) { - std::string name = block->name+".broken"; - if (state->getglobal(name)) { + std::string name = block->name + ".broken"; + emit_event(name, [x, y, z, player] (lua::LuaState* state) { state->pushivec3(x, y, z); state->pushinteger(player->getId()); - state->callNoThrow(4); - } + return 4; + }); } bool scripting::on_block_interact(Player* player, const Block* block, int x, int y, int z) { - std::string name = block->name+".interact"; - if (state->getglobal(name)) { + std::string name = block->name + ".interact"; + return emit_event(name, [x, y, z, player] (lua::LuaState* state) { state->pushivec3(x, y, z); state->pushinteger(player->getId()); - if (state->callNoThrow(4)) { - return state->toboolean(-1); - } - } - return false; + return 4; + }); } bool scripting::on_item_use(Player* player, const ItemDef* item) { - std::string name = item->name+".use"; - if (state->getglobal(name)) { + std::string name = item->name + ".use"; + return emit_event(name, [player] (lua::LuaState* state) { state->pushinteger(player->getId()); - if (state->callNoThrow(1)) { - return state->toboolean(-1); - } - } - return false; + return 1; + }); } bool scripting::on_item_use_on_block(Player* player, const ItemDef* item, int x, int y, int z) { - std::string name = item->name+".useon"; - if (state->getglobal(name)) { + std::string name = item->name + ".useon"; + return emit_event(name, [x, y, z, player] (lua::LuaState* state) { state->pushivec3(x, y, z); state->pushinteger(player->getId()); - if (state->callNoThrow(4)) { - return state->toboolean(-1); - } - } - return false; + return 4; + }); } bool scripting::on_item_break_block(Player* player, const ItemDef* item, int x, int y, int z) { - std::string name = item->name+".blockbreakby"; - if (state->getglobal(name)) { + std::string name = item->name + ".blockbreakby"; + return emit_event(name, [x, y, z, player] (lua::LuaState* state) { state->pushivec3(x, y, z); state->pushinteger(player->getId()); - if (state->callNoThrow(4)) { - return state->toboolean(-1); - } - } - return false; + return 4; + }); } void scripting::on_ui_open(UiDocument* layout, Inventory* inventory, glm::ivec3 blockcoord) { - std::string name = layout->getId()+".open"; - if (state->getglobal(name)) { + std::string name = layout->getId() + ".open"; + emit_event(name, [inventory, blockcoord] (lua::LuaState* state) { state->pushinteger(inventory == nullptr ? 0 : inventory->getId()); state->pushivec3(blockcoord.x, blockcoord.y, blockcoord.z); - state->callNoThrow(4); - } + return 4; + }); } void scripting::on_ui_close(UiDocument* layout, Inventory* inventory) { - std::string name = layout->getId()+".close"; - if (state->getglobal(name)) { - state->pushinteger(inventory->getId()); - state->callNoThrow(1); - } + std::string name = layout->getId() + ".close"; + emit_event(name, [inventory] (lua::LuaState* state) { + state->pushinteger(inventory == nullptr ? 0 : inventory->getId()); + return 1; + }); } bool scripting::register_event(int env, const std::string& name, const std::string& id) { @@ -244,17 +226,32 @@ bool scripting::register_event(int env, const std::string& name, const std::stri state->pushglobals(); } if (state->getfield(name)) { + state->pop(); + state->getglobal("events"); + state->getfield("on"); + state->pushstring(id); + state->getfield(name, -4); + state->callNoThrow(2); + state->pop(); + // remove previous name state->pushnil(); - state->setfield(name, -3); - // add new global name - state->setglobal(id); - state->pop(); + state->setfield(name); return true; } return false; } +bool scripting::emit_event(const std::string &name, std::function args) { + state->getglobal("events"); + state->getfield("emit"); + state->pushstring(name); + state->callNoThrow(args(state) + 1); + bool result = state->toboolean(-1); + state->pop(2); + return result; +} + void scripting::load_block_script(int env, 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; diff --git a/src/logic/scripting/scripting.h b/src/logic/scripting/scripting.h index b4d8c312..5dbc8878 100644 --- a/src/logic/scripting/scripting.h +++ b/src/logic/scripting/scripting.h @@ -4,6 +4,7 @@ #include "../../delegates.h" +#include "lua/LuaState.h" #include "scripting_functional.h" namespace fs = std::filesystem; @@ -44,6 +45,9 @@ namespace scripting { extern bool register_event(int env, const std::string& name, const std::string& id); + static int noargs(lua::LuaState *) { return 0; } + extern bool emit_event(const std::string& name, std::function args = noargs); + std::unique_ptr create_environment(int parent=0); std::unique_ptr create_pack_environment(const ContentPack& pack); std::unique_ptr create_doc_environment(int parent, const std::string& name); diff --git a/src/logic/scripting/scripting_frontend.cpp b/src/logic/scripting/scripting_frontend.cpp index 25828b70..65ee9ff9 100644 --- a/src/logic/scripting/scripting_frontend.cpp +++ b/src/logic/scripting/scripting_frontend.cpp @@ -22,20 +22,20 @@ void scripting::on_frontend_init(Hud* hud) { scripting::state->openlib("hud", hudlib, 0); for (auto& pack : scripting::engine->getContentPacks()) { - if (state->getglobal(pack.id+".hudopen")) { + emit_event(pack.id + ".hudopen", [&] (lua::LuaState* state) { state->pushinteger(hud->getPlayer()->getId()); - state->callNoThrow(1); - } + return 1; + }); } } void scripting::on_frontend_close() { scripting::hud = nullptr; for (auto& pack : scripting::engine->getContentPacks()) { - if (state->getglobal(pack.id+".hudclose")) { + emit_event(pack.id + ".hudclose", [&] (lua::LuaState* state) { state->pushinteger(hud->getPlayer()->getId()); - state->callNoThrow(1); - } + return 1; + }); } }