From 37b8dd27e15435fdf4243119b2cc7a337dd3ddbe Mon Sep 17 00:00:00 2001 From: VoidCosmos <62516552+VoidCosmo@users.noreply.github.com> Date: Sun, 19 Sep 2021 16:07:25 +0530 Subject: [PATCH] Add files via upload --- src/script/CMakeLists.txt | 21 + src/script/lua_api/CMakeLists.txt | 41 + src/script/lua_api/l_areastore.cpp | 396 ++++ src/script/lua_api/l_areastore.h | 64 + src/script/lua_api/l_auth.cpp | 216 ++ src/script/lua_api/l_auth.h | 54 + src/script/lua_api/l_base.cpp | 144 ++ src/script/lua_api/l_base.h | 92 + src/script/lua_api/l_camera.cpp | 241 +++ src/script/lua_api/l_camera.h | 60 + src/script/lua_api/l_client.cpp | 706 +++++++ src/script/lua_api/l_client.h | 149 ++ src/script/lua_api/l_clientobject.cpp | 287 +++ src/script/lua_api/l_clientobject.h | 85 + src/script/lua_api/l_craft.cpp | 529 +++++ src/script/lua_api/l_craft.h | 48 + src/script/lua_api/l_env.cpp | 1616 +++++++++++++++ src/script/lua_api/l_env.h | 333 +++ src/script/lua_api/l_http.cpp | 251 +++ src/script/lua_api/l_http.h | 54 + src/script/lua_api/l_internal.h | 79 + src/script/lua_api/l_inventory.cpp | 559 +++++ src/script/lua_api/l_inventory.h | 129 ++ src/script/lua_api/l_inventoryaction.cpp | 273 +++ src/script/lua_api/l_inventoryaction.h | 76 + src/script/lua_api/l_item.cpp | 681 ++++++ src/script/lua_api/l_item.h | 157 ++ src/script/lua_api/l_itemstackmeta.cpp | 139 ++ src/script/lua_api/l_itemstackmeta.h | 69 + src/script/lua_api/l_localplayer.cpp | 638 ++++++ src/script/lua_api/l_localplayer.h | 147 ++ src/script/lua_api/l_mainmenu.cpp | 1163 +++++++++++ src/script/lua_api/l_mainmenu.h | 166 ++ src/script/lua_api/l_mapgen.cpp | 1878 +++++++++++++++++ src/script/lua_api/l_mapgen.h | 149 ++ src/script/lua_api/l_metadata.cpp | 302 +++ src/script/lua_api/l_metadata.h | 81 + src/script/lua_api/l_minimap.cpp | 231 +++ src/script/lua_api/l_minimap.h | 62 + src/script/lua_api/l_modchannels.cpp | 153 ++ src/script/lua_api/l_modchannels.h | 66 + src/script/lua_api/l_nodemeta.cpp | 276 +++ src/script/lua_api/l_nodemeta.h | 95 + src/script/lua_api/l_nodetimer.cpp | 141 ++ src/script/lua_api/l_nodetimer.h | 61 + src/script/lua_api/l_noise.cpp | 712 +++++++ src/script/lua_api/l_noise.h | 194 ++ src/script/lua_api/l_object.cpp | 2398 ++++++++++++++++++++++ src/script/lua_api/l_object.h | 378 ++++ src/script/lua_api/l_particles.cpp | 279 +++ src/script/lua_api/l_particles.h | 32 + src/script/lua_api/l_particles_local.cpp | 183 ++ src/script/lua_api/l_particles_local.h | 34 + src/script/lua_api/l_playermeta.cpp | 123 ++ src/script/lua_api/l_playermeta.h | 57 + src/script/lua_api/l_rollback.cpp | 117 ++ src/script/lua_api/l_rollback.h | 35 + src/script/lua_api/l_server.cpp | 571 ++++++ src/script/lua_api/l_server.h | 114 + src/script/lua_api/l_settings.cpp | 341 +++ src/script/lua_api/l_settings.h | 88 + src/script/lua_api/l_sound.cpp | 53 + src/script/lua_api/l_sound.h | 33 + src/script/lua_api/l_storage.cpp | 155 ++ src/script/lua_api/l_storage.h | 60 + src/script/lua_api/l_util.cpp | 573 ++++++ src/script/lua_api/l_util.h | 110 + src/script/lua_api/l_vmanip.cpp | 479 +++++ src/script/lua_api/l_vmanip.h | 81 + src/script/scripting_client.cpp | 111 + src/script/scripting_client.h | 49 + src/script/scripting_mainmenu.cpp | 101 + src/script/scripting_mainmenu.h | 48 + src/script/scripting_server.cpp | 127 ++ src/script/scripting_server.h | 53 + 75 files changed, 20847 insertions(+) create mode 100644 src/script/CMakeLists.txt create mode 100644 src/script/lua_api/CMakeLists.txt create mode 100644 src/script/lua_api/l_areastore.cpp create mode 100644 src/script/lua_api/l_areastore.h create mode 100644 src/script/lua_api/l_auth.cpp create mode 100644 src/script/lua_api/l_auth.h create mode 100644 src/script/lua_api/l_base.cpp create mode 100644 src/script/lua_api/l_base.h create mode 100644 src/script/lua_api/l_camera.cpp create mode 100644 src/script/lua_api/l_camera.h create mode 100644 src/script/lua_api/l_client.cpp create mode 100644 src/script/lua_api/l_client.h create mode 100644 src/script/lua_api/l_clientobject.cpp create mode 100644 src/script/lua_api/l_clientobject.h create mode 100644 src/script/lua_api/l_craft.cpp create mode 100644 src/script/lua_api/l_craft.h create mode 100644 src/script/lua_api/l_env.cpp create mode 100644 src/script/lua_api/l_env.h create mode 100644 src/script/lua_api/l_http.cpp create mode 100644 src/script/lua_api/l_http.h create mode 100644 src/script/lua_api/l_internal.h create mode 100644 src/script/lua_api/l_inventory.cpp create mode 100644 src/script/lua_api/l_inventory.h create mode 100644 src/script/lua_api/l_inventoryaction.cpp create mode 100644 src/script/lua_api/l_inventoryaction.h create mode 100644 src/script/lua_api/l_item.cpp create mode 100644 src/script/lua_api/l_item.h create mode 100644 src/script/lua_api/l_itemstackmeta.cpp create mode 100644 src/script/lua_api/l_itemstackmeta.h create mode 100644 src/script/lua_api/l_localplayer.cpp create mode 100644 src/script/lua_api/l_localplayer.h create mode 100644 src/script/lua_api/l_mainmenu.cpp create mode 100644 src/script/lua_api/l_mainmenu.h create mode 100644 src/script/lua_api/l_mapgen.cpp create mode 100644 src/script/lua_api/l_mapgen.h create mode 100644 src/script/lua_api/l_metadata.cpp create mode 100644 src/script/lua_api/l_metadata.h create mode 100644 src/script/lua_api/l_minimap.cpp create mode 100644 src/script/lua_api/l_minimap.h create mode 100644 src/script/lua_api/l_modchannels.cpp create mode 100644 src/script/lua_api/l_modchannels.h create mode 100644 src/script/lua_api/l_nodemeta.cpp create mode 100644 src/script/lua_api/l_nodemeta.h create mode 100644 src/script/lua_api/l_nodetimer.cpp create mode 100644 src/script/lua_api/l_nodetimer.h create mode 100644 src/script/lua_api/l_noise.cpp create mode 100644 src/script/lua_api/l_noise.h create mode 100644 src/script/lua_api/l_object.cpp create mode 100644 src/script/lua_api/l_object.h create mode 100644 src/script/lua_api/l_particles.cpp create mode 100644 src/script/lua_api/l_particles.h create mode 100644 src/script/lua_api/l_particles_local.cpp create mode 100644 src/script/lua_api/l_particles_local.h create mode 100644 src/script/lua_api/l_playermeta.cpp create mode 100644 src/script/lua_api/l_playermeta.h create mode 100644 src/script/lua_api/l_rollback.cpp create mode 100644 src/script/lua_api/l_rollback.h create mode 100644 src/script/lua_api/l_server.cpp create mode 100644 src/script/lua_api/l_server.h create mode 100644 src/script/lua_api/l_settings.cpp create mode 100644 src/script/lua_api/l_settings.h create mode 100644 src/script/lua_api/l_sound.cpp create mode 100644 src/script/lua_api/l_sound.h create mode 100644 src/script/lua_api/l_storage.cpp create mode 100644 src/script/lua_api/l_storage.h create mode 100644 src/script/lua_api/l_util.cpp create mode 100644 src/script/lua_api/l_util.h create mode 100644 src/script/lua_api/l_vmanip.cpp create mode 100644 src/script/lua_api/l_vmanip.h create mode 100644 src/script/scripting_client.cpp create mode 100644 src/script/scripting_client.h create mode 100644 src/script/scripting_mainmenu.cpp create mode 100644 src/script/scripting_mainmenu.h create mode 100644 src/script/scripting_server.cpp create mode 100644 src/script/scripting_server.h diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt new file mode 100644 index 000000000..b01617a39 --- /dev/null +++ b/src/script/CMakeLists.txt @@ -0,0 +1,21 @@ +add_subdirectory(common) +add_subdirectory(cpp_api) +add_subdirectory(lua_api) + +# Used by server and client +set(common_SCRIPT_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/scripting_server.cpp + ${common_SCRIPT_COMMON_SRCS} + ${common_SCRIPT_CPP_API_SRCS} + ${common_SCRIPT_LUA_API_SRCS} + PARENT_SCOPE) + +# Used by client only +set(client_SCRIPT_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/scripting_mainmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/scripting_client.cpp + ${client_SCRIPT_COMMON_SRCS} + ${client_SCRIPT_CPP_API_SRCS} + ${client_SCRIPT_LUA_API_SRCS} + PARENT_SCOPE) + diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt new file mode 100644 index 000000000..d5d08af1c --- /dev/null +++ b/src/script/lua_api/CMakeLists.txt @@ -0,0 +1,41 @@ +set(common_SCRIPT_LUA_API_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/l_areastore.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_auth.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_base.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_craft.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_env.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_http.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_inventory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_inventoryaction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_item.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_itemstackmeta.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_mapgen.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_metadata.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_modchannels.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_nodemeta.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_nodetimer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_noise.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_object.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_particles.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_playermeta.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_rollback.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_server.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_settings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_storage.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_util.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_vmanip.cpp + PARENT_SCOPE) + +set(client_SCRIPT_LUA_API_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/l_camera.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_client.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_clientobject.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_inventoryaction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_localplayer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_mainmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_minimap.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_particles_local.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_sound.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_storage.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_clientobject.cpp + PARENT_SCOPE) diff --git a/src/script/lua_api/l_areastore.cpp b/src/script/lua_api/l_areastore.cpp new file mode 100644 index 000000000..5f546fc6a --- /dev/null +++ b/src/script/lua_api/l_areastore.cpp @@ -0,0 +1,396 @@ +/* +Minetest +Copyright (C) 2015 est31 + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#include "lua_api/l_areastore.h" +#include "lua_api/l_internal.h" +#include "common/c_converter.h" +#include "cpp_api/s_security.h" +#include "irr_v3d.h" +#include "util/areastore.h" +#include "filesys.h" +#include + +static inline void get_data_and_border_flags(lua_State *L, u8 start_i, + bool *borders, bool *data) +{ + if (!lua_isboolean(L, start_i)) + return; + *borders = lua_toboolean(L, start_i); + if (!lua_isboolean(L, start_i + 1)) + return; + *data = lua_toboolean(L, start_i + 1); +} + +static void push_area(lua_State *L, const Area *a, + bool include_borders, bool include_data) +{ + if (!include_borders && !include_data) { + lua_pushboolean(L, true); + return; + } + lua_newtable(L); + if (include_borders) { + push_v3s16(L, a->minedge); + lua_setfield(L, -2, "min"); + push_v3s16(L, a->maxedge); + lua_setfield(L, -2, "max"); + } + if (include_data) { + lua_pushlstring(L, a->data.c_str(), a->data.size()); + lua_setfield(L, -2, "data"); + } +} + +static inline void push_areas(lua_State *L, const std::vector &areas, + bool borders, bool data) +{ + lua_newtable(L); + size_t cnt = areas.size(); + for (size_t i = 0; i < cnt; i++) { + lua_pushnumber(L, areas[i]->id); + push_area(L, areas[i], borders, data); + lua_settable(L, -3); + } +} + +// Deserializes value and handles errors +static int deserialization_helper(lua_State *L, AreaStore *as, + std::istream &is) +{ + try { + as->deserialize(is); + } catch (const SerializationError &e) { + lua_pushboolean(L, false); + lua_pushstring(L, e.what()); + return 2; + } + + lua_pushboolean(L, true); + return 1; +} + +// garbage collector +int LuaAreaStore::gc_object(lua_State *L) +{ + LuaAreaStore *o = *(LuaAreaStore **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +// get_area(id, include_borders, include_data) +int LuaAreaStore::l_get_area(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + u32 id = luaL_checknumber(L, 2); + + bool include_borders = true; + bool include_data = false; + get_data_and_border_flags(L, 3, &include_borders, &include_data); + + const Area *res; + + res = ast->getArea(id); + if (!res) + return 0; + + push_area(L, res, include_borders, include_data); + + return 1; +} + +// get_areas_for_pos(pos, include_borders, include_data) +int LuaAreaStore::l_get_areas_for_pos(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + v3s16 pos = check_v3s16(L, 2); + + bool include_borders = true; + bool include_data = false; + get_data_and_border_flags(L, 3, &include_borders, &include_data); + + std::vector res; + + ast->getAreasForPos(&res, pos); + push_areas(L, res, include_borders, include_data); + + return 1; +} + +// get_areas_in_area(edge1, edge2, accept_overlap, include_borders, include_data) +int LuaAreaStore::l_get_areas_in_area(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + v3s16 minedge = check_v3s16(L, 2); + v3s16 maxedge = check_v3s16(L, 3); + + bool include_borders = true; + bool include_data = false; + bool accept_overlap = false; + if (lua_isboolean(L, 4)) { + accept_overlap = readParam(L, 4); + get_data_and_border_flags(L, 5, &include_borders, &include_data); + } + std::vector res; + + ast->getAreasInArea(&res, minedge, maxedge, accept_overlap); + push_areas(L, res, include_borders, include_data); + + return 1; +} + +// insert_area(edge1, edge2, data, id) +int LuaAreaStore::l_insert_area(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + Area a(check_v3s16(L, 2), check_v3s16(L, 3)); + + size_t d_len; + const char *data = luaL_checklstring(L, 4, &d_len); + + a.data = std::string(data, d_len); + + if (lua_isnumber(L, 5)) + a.id = lua_tonumber(L, 5); + + // Insert & assign a new ID if necessary + if (!ast->insertArea(&a)) + return 0; + + lua_pushnumber(L, a.id); + return 1; +} + +// reserve(count) +int LuaAreaStore::l_reserve(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + size_t count = luaL_checknumber(L, 2); + ast->reserve(count); + return 0; +} + +// remove_area(id) +int LuaAreaStore::l_remove_area(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + u32 id = luaL_checknumber(L, 2); + bool success = ast->removeArea(id); + + lua_pushboolean(L, success); + return 1; +} + +// set_cache_params(params) +int LuaAreaStore::l_set_cache_params(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + luaL_checktype(L, 2, LUA_TTABLE); + + bool enabled = getboolfield_default(L, 2, "enabled", true); + u8 block_radius = getintfield_default(L, 2, "block_radius", 64); + size_t limit = getintfield_default(L, 2, "block_radius", 1000); + + ast->setCacheParams(enabled, block_radius, limit); + + return 0; +} + +// to_string() +int LuaAreaStore::l_to_string(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + + std::ostringstream os(std::ios_base::binary); + o->as->serialize(os); + std::string str = os.str(); + + lua_pushlstring(L, str.c_str(), str.length()); + return 1; +} + +// to_file(filename) +int LuaAreaStore::l_to_file(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + const char *filename = luaL_checkstring(L, 2); + CHECK_SECURE_PATH(L, filename, true); + + std::ostringstream os(std::ios_base::binary); + ast->serialize(os); + + lua_pushboolean(L, fs::safeWriteToFile(filename, os.str())); + return 1; +} + +// from_string(str) +int LuaAreaStore::l_from_string(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + + size_t len; + const char *str = luaL_checklstring(L, 2, &len); + + std::istringstream is(std::string(str, len), std::ios::binary); + return deserialization_helper(L, o->as, is); +} + +// from_file(filename) +int LuaAreaStore::l_from_file(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + + const char *filename = luaL_checkstring(L, 2); + CHECK_SECURE_PATH(L, filename, false); + + std::ifstream is(filename, std::ios::binary); + return deserialization_helper(L, o->as, is); +} + +LuaAreaStore::LuaAreaStore() : as(AreaStore::getOptimalImplementation()) +{ +} + +LuaAreaStore::LuaAreaStore(const std::string &type) +{ +#if USE_SPATIAL + if (type == "LibSpatial") { + as = new SpatialAreaStore(); + } else +#endif + { + as = new VectorAreaStore(); + } +} + +LuaAreaStore::~LuaAreaStore() +{ + delete as; +} + +// LuaAreaStore() +// Creates an LuaAreaStore and leaves it on top of stack +int LuaAreaStore::create_object(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = (lua_isstring(L, 1)) ? + new LuaAreaStore(readParam(L, 1)) : + new LuaAreaStore(); + + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + +LuaAreaStore *LuaAreaStore::checkobject(lua_State *L, int narg) +{ + NO_MAP_LOCK_REQUIRED; + + luaL_checktype(L, narg, LUA_TUSERDATA); + + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + + return *(LuaAreaStore **)ud; // unbox pointer +} + +void LuaAreaStore::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Can be created from Lua (AreaStore()) + lua_register(L, className, create_object); +} + +const char LuaAreaStore::className[] = "AreaStore"; +const luaL_Reg LuaAreaStore::methods[] = { + luamethod(LuaAreaStore, get_area), + luamethod(LuaAreaStore, get_areas_for_pos), + luamethod(LuaAreaStore, get_areas_in_area), + luamethod(LuaAreaStore, insert_area), + luamethod(LuaAreaStore, reserve), + luamethod(LuaAreaStore, remove_area), + luamethod(LuaAreaStore, set_cache_params), + luamethod(LuaAreaStore, to_string), + luamethod(LuaAreaStore, to_file), + luamethod(LuaAreaStore, from_string), + luamethod(LuaAreaStore, from_file), + {0,0} +}; diff --git a/src/script/lua_api/l_areastore.h b/src/script/lua_api/l_areastore.h new file mode 100644 index 000000000..3137c26f4 --- /dev/null +++ b/src/script/lua_api/l_areastore.h @@ -0,0 +1,64 @@ +/* +Minetest +Copyright (C) 2015 est31 + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" + +class AreaStore; + +class LuaAreaStore : public ModApiBase +{ +private: + static const char className[]; + static const luaL_Reg methods[]; + + static int gc_object(lua_State *L); + + static int l_get_area(lua_State *L); + + static int l_get_areas_for_pos(lua_State *L); + static int l_get_areas_in_area(lua_State *L); + static int l_insert_area(lua_State *L); + static int l_reserve(lua_State *L); + static int l_remove_area(lua_State *L); + + static int l_set_cache_params(lua_State *L); + + static int l_to_string(lua_State *L); + static int l_to_file(lua_State *L); + + static int l_from_string(lua_State *L); + static int l_from_file(lua_State *L); + +public: + AreaStore *as = nullptr; + + LuaAreaStore(); + LuaAreaStore(const std::string &type); + ~LuaAreaStore(); + + // AreaStore() + // Creates a AreaStore and leaves it on top of stack + static int create_object(lua_State *L); + + static LuaAreaStore *checkobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; diff --git a/src/script/lua_api/l_auth.cpp b/src/script/lua_api/l_auth.cpp new file mode 100644 index 000000000..edbcdf1c5 --- /dev/null +++ b/src/script/lua_api/l_auth.cpp @@ -0,0 +1,216 @@ +/* +Minetest +Copyright (C) 2018 bendeutsch, Ben Deutsch + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_auth.h" +#include "lua_api/l_internal.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "cpp_api/s_base.h" +#include "server.h" +#include "environment.h" +#include "database/database.h" +#include + +// common start: ensure auth db +AuthDatabase *ModApiAuth::getAuthDb(lua_State *L) +{ + ServerEnvironment *server_environment = + dynamic_cast(getEnv(L)); + if (!server_environment) + return nullptr; + return server_environment->getAuthDatabase(); +} + +void ModApiAuth::pushAuthEntry(lua_State *L, const AuthEntry &authEntry) +{ + lua_newtable(L); + int table = lua_gettop(L); + // id + lua_pushnumber(L, authEntry.id); + lua_setfield(L, table, "id"); + // name + lua_pushstring(L, authEntry.name.c_str()); + lua_setfield(L, table, "name"); + // password + lua_pushstring(L, authEntry.password.c_str()); + lua_setfield(L, table, "password"); + // privileges + lua_newtable(L); + int privtable = lua_gettop(L); + for (const std::string &privs : authEntry.privileges) { + lua_pushboolean(L, true); + lua_setfield(L, privtable, privs.c_str()); + } + lua_setfield(L, table, "privileges"); + // last_login + lua_pushnumber(L, authEntry.last_login); + lua_setfield(L, table, "last_login"); + + lua_pushvalue(L, table); +} + +// auth_read(name) +int ModApiAuth::l_auth_read(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + AuthDatabase *auth_db = getAuthDb(L); + if (!auth_db) + return 0; + AuthEntry authEntry; + const char *name = luaL_checkstring(L, 1); + bool success = auth_db->getAuth(std::string(name), authEntry); + if (!success) + return 0; + + pushAuthEntry(L, authEntry); + return 1; +} + +// auth_save(table) +int ModApiAuth::l_auth_save(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + AuthDatabase *auth_db = getAuthDb(L); + if (!auth_db) + return 0; + luaL_checktype(L, 1, LUA_TTABLE); + int table = 1; + AuthEntry authEntry; + bool success; + success = getintfield(L, table, "id", authEntry.id); + success = success && getstringfield(L, table, "name", authEntry.name); + success = success && getstringfield(L, table, "password", authEntry.password); + lua_getfield(L, table, "privileges"); + if (lua_istable(L, -1)) { + lua_pushnil(L); + while (lua_next(L, -2)) { + authEntry.privileges.emplace_back( + lua_tostring(L, -2)); // the key, not the value + lua_pop(L, 1); + } + } else { + success = false; + } + lua_pop(L, 1); // the table + success = success && getintfield(L, table, "last_login", authEntry.last_login); + + if (!success) { + lua_pushboolean(L, false); + return 1; + } + + lua_pushboolean(L, auth_db->saveAuth(authEntry)); + return 1; +} + +// auth_create(table) +int ModApiAuth::l_auth_create(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + AuthDatabase *auth_db = getAuthDb(L); + if (!auth_db) + return 0; + luaL_checktype(L, 1, LUA_TTABLE); + int table = 1; + AuthEntry authEntry; + bool success; + // no meaningful id field, we assume + success = getstringfield(L, table, "name", authEntry.name); + success = success && getstringfield(L, table, "password", authEntry.password); + lua_getfield(L, table, "privileges"); + if (lua_istable(L, -1)) { + lua_pushnil(L); + while (lua_next(L, -2)) { + authEntry.privileges.emplace_back( + lua_tostring(L, -2)); // the key, not the value + lua_pop(L, 1); + } + } else { + success = false; + } + lua_pop(L, 1); // the table + success = success && getintfield(L, table, "last_login", authEntry.last_login); + + if (!success) + return 0; + + if (auth_db->createAuth(authEntry)) { + pushAuthEntry(L, authEntry); + return 1; + } + + return 0; +} + +// auth_delete(name) +int ModApiAuth::l_auth_delete(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + AuthDatabase *auth_db = getAuthDb(L); + if (!auth_db) + return 0; + std::string name(luaL_checkstring(L, 1)); + lua_pushboolean(L, auth_db->deleteAuth(name)); + return 1; +} + +// auth_list_names() +int ModApiAuth::l_auth_list_names(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + AuthDatabase *auth_db = getAuthDb(L); + if (!auth_db) + return 0; + std::vector names; + auth_db->listNames(names); + lua_createtable(L, names.size(), 0); + int table = lua_gettop(L); + int i = 1; + for (const std::string &name : names) { + lua_pushstring(L, name.c_str()); + lua_rawseti(L, table, i++); + } + return 1; +} + +// auth_reload() +int ModApiAuth::l_auth_reload(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + AuthDatabase *auth_db = getAuthDb(L); + if (auth_db) + auth_db->reload(); + return 0; +} + +void ModApiAuth::Initialize(lua_State *L, int top) +{ + + lua_newtable(L); + int auth_top = lua_gettop(L); + + registerFunction(L, "read", l_auth_read, auth_top); + registerFunction(L, "save", l_auth_save, auth_top); + registerFunction(L, "create", l_auth_create, auth_top); + registerFunction(L, "delete", l_auth_delete, auth_top); + registerFunction(L, "list_names", l_auth_list_names, auth_top); + registerFunction(L, "reload", l_auth_reload, auth_top); + + lua_setfield(L, top, "auth"); +} diff --git a/src/script/lua_api/l_auth.h b/src/script/lua_api/l_auth.h new file mode 100644 index 000000000..a2ee10651 --- /dev/null +++ b/src/script/lua_api/l_auth.h @@ -0,0 +1,54 @@ +/* +Minetest +Copyright (C) 2018 bendeutsch, Ben Deutsch + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" + +class AuthDatabase; +struct AuthEntry; + +class ModApiAuth : public ModApiBase +{ +private: + // auth_read(name) + static int l_auth_read(lua_State *L); + + // auth_save(table) + static int l_auth_save(lua_State *L); + + // auth_create(table) + static int l_auth_create(lua_State *L); + + // auth_delete(name) + static int l_auth_delete(lua_State *L); + + // auth_list_names() + static int l_auth_list_names(lua_State *L); + + // auth_reload() + static int l_auth_reload(lua_State *L); + + // helper for auth* methods + static AuthDatabase *getAuthDb(lua_State *L); + static void pushAuthEntry(lua_State *L, const AuthEntry &authEntry); + +public: + static void Initialize(lua_State *L, int top); +}; diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp new file mode 100644 index 000000000..04b61e76c --- /dev/null +++ b/src/script/lua_api/l_base.cpp @@ -0,0 +1,144 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_base.h" +#include "lua_api/l_internal.h" +#include "cpp_api/s_base.h" +#include "content/mods.h" +#include "profiler.h" +#include "server.h" +#include +#include + +ScriptApiBase *ModApiBase::getScriptApiBase(lua_State *L) +{ + // Get server from registry + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI); + ScriptApiBase *sapi_ptr; +#if INDIRECT_SCRIPTAPI_RIDX + sapi_ptr = (ScriptApiBase*) *(void**)(lua_touserdata(L, -1)); +#else + sapi_ptr = (ScriptApiBase*) lua_touserdata(L, -1); +#endif + lua_pop(L, 1); + return sapi_ptr; +} + +Server *ModApiBase::getServer(lua_State *L) +{ + return getScriptApiBase(L)->getServer(); +} + +ServerInventoryManager *ModApiBase::getServerInventoryMgr(lua_State *L) +{ + return getScriptApiBase(L)->getServer()->getInventoryMgr(); +} + +#ifndef SERVER +Client *ModApiBase::getClient(lua_State *L) +{ + return getScriptApiBase(L)->getClient(); +} + +Game *ModApiBase::getGame(lua_State *L) +{ + return getScriptApiBase(L)->getGame(); +} +#endif + +IGameDef *ModApiBase::getGameDef(lua_State *L) +{ + return getScriptApiBase(L)->getGameDef(); +} + +Environment *ModApiBase::getEnv(lua_State *L) +{ + return getScriptApiBase(L)->getEnv(); +} + +#ifndef SERVER +GUIEngine *ModApiBase::getGuiEngine(lua_State *L) +{ + return getScriptApiBase(L)->getGuiEngine(); +} +#endif + +std::string ModApiBase::getCurrentModPath(lua_State *L) +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); + std::string current_mod_name = readParam(L, -1, ""); + if (current_mod_name.empty()) + return "."; + + const ModSpec *mod = getServer(L)->getModSpec(current_mod_name); + if (!mod) + return "."; + + return mod->path; +} + + +bool ModApiBase::registerFunction(lua_State *L, const char *name, + lua_CFunction func, int top) +{ + // TODO: Check presence first! + + lua_pushcfunction(L, func); + lua_setfield(L, top, name); + + return true; +} + +int ModApiBase::l_deprecated_function(lua_State *L, const char *good, const char *bad, lua_CFunction func) +{ + thread_local std::vector deprecated_logged; + + DeprecatedHandlingMode dep_mode = get_deprecated_handling_mode(); + if (dep_mode == DeprecatedHandlingMode::Ignore) + return func(L); + + u64 start_time = porting::getTimeUs(); + lua_Debug ar; + + // Get caller name with line and script backtrace + FATAL_ERROR_IF(!lua_getstack(L, 1, &ar), "lua_getstack() failed"); + FATAL_ERROR_IF(!lua_getinfo(L, "Sl", &ar), "lua_getinfo() failed"); + + // Get backtrace and hash it to reduce the warning flood + std::string backtrace = ar.short_src; + backtrace.append(":").append(std::to_string(ar.currentline)); + u64 hash = murmur_hash_64_ua(backtrace.data(), backtrace.length(), 0xBADBABE); + + if (std::find(deprecated_logged.begin(), deprecated_logged.end(), hash) + == deprecated_logged.end()) { + + deprecated_logged.emplace_back(hash); + warningstream << "Call to deprecated function '" << bad << "', please use '" + << good << "' at " << backtrace << std::endl; + + if (dep_mode == DeprecatedHandlingMode::Error) + script_error(L, LUA_ERRRUN, NULL, NULL); + } + + u64 end_time = porting::getTimeUs(); + g_profiler->avg("l_deprecated_function", end_time - start_time); + + return func(L); +} + diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h new file mode 100644 index 000000000..19e63a23a --- /dev/null +++ b/src/script/lua_api/l_base.h @@ -0,0 +1,92 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "common/c_types.h" +#include "common/c_internal.h" +#include "common/helper.h" +#include "gamedef.h" +#include + +extern "C" { +#include +#include +} + +#ifndef SERVER +class Client; +class Game; +class GUIEngine; +#endif + +class ScriptApiBase; +class Server; +class Environment; +class ServerInventoryManager; + +class ModApiBase : protected LuaHelper { +public: + static ScriptApiBase* getScriptApiBase(lua_State *L); + static Server* getServer(lua_State *L); + static ServerInventoryManager *getServerInventoryMgr(lua_State *L); + #ifndef SERVER + static Client* getClient(lua_State *L); + static Game* getGame(lua_State *L); + static GUIEngine* getGuiEngine(lua_State *L); + #endif // !SERVER + + static IGameDef* getGameDef(lua_State *L); + static Environment* getEnv(lua_State *L); + + // When we are not loading the mod, this function returns "." + static std::string getCurrentModPath(lua_State *L); + + // Get an arbitrary subclass of ScriptApiBase + // by using dynamic_cast<> on getScriptApiBase() + template + static T* getScriptApi(lua_State *L) { + ScriptApiBase *scriptIface = getScriptApiBase(L); + T *scriptIfaceDowncast = dynamic_cast(scriptIface); + if (!scriptIfaceDowncast) { + throw LuaError("Requested unavailable ScriptApi - core engine bug!"); + } + return scriptIfaceDowncast; + } + + static bool registerFunction(lua_State *L, + const char* name, + lua_CFunction func, + int top); + + /** + * A wrapper for deprecated functions. + * + * When called, handles the deprecation according to user settings and then calls `func`. + * + * @throws Lua Error if required by the user settings. + * + * @param L Lua state + * @param good Name of good function/method + * @param bad Name of deprecated function/method + * @param func Actual implementation of function + * @return value from `func` + */ + static int l_deprecated_function(lua_State *L, const char *good, const char *bad, lua_CFunction func); +}; diff --git a/src/script/lua_api/l_camera.cpp b/src/script/lua_api/l_camera.cpp new file mode 100644 index 000000000..7da90b929 --- /dev/null +++ b/src/script/lua_api/l_camera.cpp @@ -0,0 +1,241 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "l_camera.h" +#include +#include "script/common/c_converter.h" +#include "l_internal.h" +#include "client/content_cao.h" +#include "client/camera.h" +#include "client/client.h" + +LuaCamera::LuaCamera(Camera *m) : m_camera(m) +{ +} + +void LuaCamera::create(lua_State *L, Camera *m) +{ + lua_getglobal(L, "core"); + luaL_checktype(L, -1, LUA_TTABLE); + int objectstable = lua_gettop(L); + lua_getfield(L, -1, "camera"); + + // Duplication check + if (lua_type(L, -1) == LUA_TUSERDATA) { + lua_pop(L, 1); + return; + } + + LuaCamera *o = new LuaCamera(m); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + + lua_pushvalue(L, lua_gettop(L)); + lua_setfield(L, objectstable, "camera"); +} + +// set_camera_mode(self, mode) +int LuaCamera::l_set_camera_mode(lua_State *L) +{ + Camera *camera = getobject(L, 1); + GenericCAO *playercao = getClient(L)->getEnv().getLocalPlayer()->getCAO(); + if (!camera) + return 0; + sanity_check(playercao); + if (!lua_isnumber(L, 2)) + return 0; + + camera->setCameraMode((CameraMode)((int)lua_tonumber(L, 2))); + // Make the player visible depending on camera mode. + playercao->updateMeshCulling(); + playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); + return 0; +} + +// get_camera_mode(self) +int LuaCamera::l_get_camera_mode(lua_State *L) +{ + Camera *camera = getobject(L, 1); + if (!camera) + return 0; + + lua_pushinteger(L, (int)camera->getCameraMode()); + + return 1; +} + +// get_fov(self) +int LuaCamera::l_get_fov(lua_State *L) +{ + Camera *camera = getobject(L, 1); + if (!camera) + return 0; + + lua_newtable(L); + lua_pushnumber(L, camera->getFovX() * core::RADTODEG); + lua_setfield(L, -2, "x"); + lua_pushnumber(L, camera->getFovY() * core::RADTODEG); + lua_setfield(L, -2, "y"); + lua_pushnumber(L, camera->getCameraNode()->getFOV() * core::RADTODEG); + lua_setfield(L, -2, "actual"); + lua_pushnumber(L, camera->getFovMax() * core::RADTODEG); + lua_setfield(L, -2, "max"); + return 1; +} + +// get_pos(self) +int LuaCamera::l_get_pos(lua_State *L) +{ + Camera *camera = getobject(L, 1); + if (!camera) + return 0; + + push_v3f(L, camera->getPosition() / BS); + return 1; +} + +// get_offset(self) +int LuaCamera::l_get_offset(lua_State *L) +{ + LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer(); + sanity_check(player); + + push_v3f(L, player->getEyeOffset() / BS); + return 1; +} + +// get_look_dir(self) +int LuaCamera::l_get_look_dir(lua_State *L) +{ + Camera *camera = getobject(L, 1); + if (!camera) + return 0; + + push_v3f(L, camera->getDirection()); + return 1; +} + +// get_look_horizontal(self) +// FIXME: wouldn't localplayer be a better place for this? +int LuaCamera::l_get_look_horizontal(lua_State *L) +{ + LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer(); + sanity_check(player); + + lua_pushnumber(L, (player->getYaw() + 90.f) * core::DEGTORAD); + return 1; +} + +// get_look_vertical(self) +// FIXME: wouldn't localplayer be a better place for this? +int LuaCamera::l_get_look_vertical(lua_State *L) +{ + LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer(); + sanity_check(player); + + lua_pushnumber(L, -1.0f * player->getPitch() * core::DEGTORAD); + return 1; +} + +// get_aspect_ratio(self) +int LuaCamera::l_get_aspect_ratio(lua_State *L) +{ + Camera *camera = getobject(L, 1); + if (!camera) + return 0; + + lua_pushnumber(L, camera->getCameraNode()->getAspectRatio()); + return 1; +} + +LuaCamera *LuaCamera::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + + return *(LuaCamera **)ud; +} + +Camera *LuaCamera::getobject(LuaCamera *ref) +{ + return ref->m_camera; +} + +Camera *LuaCamera::getobject(lua_State *L, int narg) +{ + LuaCamera *ref = checkobject(L, narg); + assert(ref); + Camera *camera = getobject(ref); + if (!camera) + return NULL; + return camera; +} + +int LuaCamera::gc_object(lua_State *L) +{ + LuaCamera *o = *(LuaCamera **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +void LuaCamera::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); + + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); +} + +// clang-format off +const char LuaCamera::className[] = "Camera"; +const luaL_Reg LuaCamera::methods[] = { + luamethod(LuaCamera, set_camera_mode), + luamethod(LuaCamera, get_camera_mode), + luamethod(LuaCamera, get_fov), + luamethod(LuaCamera, get_pos), + luamethod(LuaCamera, get_offset), + luamethod(LuaCamera, get_look_dir), + luamethod(LuaCamera, get_look_vertical), + luamethod(LuaCamera, get_look_horizontal), + luamethod(LuaCamera, get_aspect_ratio), + + {0, 0} +}; +// clang-format on diff --git a/src/script/lua_api/l_camera.h b/src/script/lua_api/l_camera.h new file mode 100644 index 000000000..9e4992ce9 --- /dev/null +++ b/src/script/lua_api/l_camera.h @@ -0,0 +1,60 @@ +/* +Minetest +Copyright (C) 2013-2017 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "l_base.h" + +class Camera; + +class LuaCamera : public ModApiBase +{ +private: + static const char className[]; + static const luaL_Reg methods[]; + + // garbage collector + static int gc_object(lua_State *L); + + static int l_set_camera_mode(lua_State *L); + static int l_get_camera_mode(lua_State *L); + + static int l_get_fov(lua_State *L); + + static int l_get_pos(lua_State *L); + static int l_get_offset(lua_State *L); + static int l_get_look_dir(lua_State *L); + static int l_get_look_vertical(lua_State *L); + static int l_get_look_horizontal(lua_State *L); + static int l_get_aspect_ratio(lua_State *L); + + Camera *m_camera = nullptr; + +public: + LuaCamera(Camera *m); + ~LuaCamera() = default; + + static void create(lua_State *L, Camera *m); + + static LuaCamera *checkobject(lua_State *L, int narg); + static Camera *getobject(LuaCamera *ref); + static Camera *getobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp new file mode 100644 index 000000000..3632eddea --- /dev/null +++ b/src/script/lua_api/l_client.cpp @@ -0,0 +1,706 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "l_client.h" +#include "chatmessage.h" +#include "client/client.h" +#include "client/clientevent.h" +#include "client/sound.h" +#include "client/clientenvironment.h" +#include "client/game.h" +#include "common/c_content.h" +#include "common/c_converter.h" +#include "cpp_api/s_base.h" +#include "gettext.h" +#include "l_internal.h" +#include "l_clientobject.h" +#include "lua_api/l_nodemeta.h" +#include "gui/mainmenumanager.h" +#include "map.h" +#include "util/string.h" +#include "nodedef.h" +#include "client/keycode.h" + +#define checkCSMRestrictionFlag(flag) \ + ( getClient(L)->checkCSMRestrictionFlag(CSMRestrictionFlags::flag) ) + +// Not the same as FlagDesc, which contains an `u32 flag` +struct CSMFlagDesc { + const char *name; + u64 flag; +}; + +/* + FIXME: This should eventually be moved somewhere else + It also needs to be kept in sync with the definition of CSMRestrictionFlags + in network/networkprotocol.h +*/ +const static CSMFlagDesc flagdesc_csm_restriction[] = { + {"load_client_mods", CSM_RF_LOAD_CLIENT_MODS}, + {"chat_messages", CSM_RF_CHAT_MESSAGES}, + {"read_itemdefs", CSM_RF_READ_ITEMDEFS}, + {"read_nodedefs", CSM_RF_READ_NODEDEFS}, + {"lookup_nodes", CSM_RF_LOOKUP_NODES}, + {"read_playerinfo", CSM_RF_READ_PLAYERINFO}, + {NULL, 0} +}; + +// get_current_modname() +int ModApiClient::l_get_current_modname(lua_State *L) +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); + return 1; +} + +// get_modpath(modname) +int ModApiClient::l_get_modpath(lua_State *L) +{ + std::string modname = readParam(L, 1); + // Client mods use a virtual filesystem, see Client::scanModSubfolder() + std::string path = modname + ":"; + lua_pushstring(L, path.c_str()); + return 1; +} + +// get_last_run_mod() +int ModApiClient::l_get_last_run_mod(lua_State *L) +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); + std::string current_mod = readParam(L, -1, ""); + if (current_mod.empty()) { + lua_pop(L, 1); + lua_pushstring(L, getScriptApiBase(L)->getOrigin().c_str()); + } + return 1; +} + +// set_last_run_mod(modname) +int ModApiClient::l_set_last_run_mod(lua_State *L) +{ + if (!lua_isstring(L, 1)) + return 0; + + const char *mod = lua_tostring(L, 1); + getScriptApiBase(L)->setOriginDirect(mod); + lua_pushboolean(L, true); + return 1; +} + +// print(text) +int ModApiClient::l_print(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string text = luaL_checkstring(L, 1); + rawstream << text << std::endl; + return 0; +} + +// display_chat_message(message) +int ModApiClient::l_display_chat_message(lua_State *L) +{ + if (!lua_isstring(L, 1)) + return 0; + + std::string message = luaL_checkstring(L, 1); + getClient(L)->pushToChatQueue(new ChatMessage(utf8_to_wide(message))); + lua_pushboolean(L, true); + return 1; +} + +// send_chat_message(message) +int ModApiClient::l_send_chat_message(lua_State *L) +{ + if (!lua_isstring(L, 1)) + return 0; + + // If server disabled this API, discard + + if (checkCSMRestrictionFlag(CSM_RF_CHAT_MESSAGES)) + return 0; + + std::string message = luaL_checkstring(L, 1); + getClient(L)->sendChatMessage(utf8_to_wide(message)); + return 0; +} + +// clear_out_chat_queue() +int ModApiClient::l_clear_out_chat_queue(lua_State *L) +{ + getClient(L)->clearOutChatQueue(); + return 0; +} + +// get_player_names() +int ModApiClient::l_get_player_names(lua_State *L) +{ + if (checkCSMRestrictionFlag(CSM_RF_READ_PLAYERINFO)) + return 0; + + const std::list &plist = getClient(L)->getConnectedPlayerNames(); + lua_createtable(L, plist.size(), 0); + int newTable = lua_gettop(L); + int index = 1; + std::list::const_iterator iter; + for (iter = plist.begin(); iter != plist.end(); ++iter) { + lua_pushstring(L, (*iter).c_str()); + lua_rawseti(L, newTable, index); + index++; + } + return 1; +} + +// show_formspec(formspec) +int ModApiClient::l_show_formspec(lua_State *L) +{ + if (!lua_isstring(L, 1) || !lua_isstring(L, 2)) + return 0; + + ClientEvent *event = new ClientEvent(); + event->type = CE_SHOW_LOCAL_FORMSPEC; + event->show_formspec.formname = new std::string(luaL_checkstring(L, 1)); + event->show_formspec.formspec = new std::string(luaL_checkstring(L, 2)); + getClient(L)->pushToEventQueue(event); + lua_pushboolean(L, true); + return 1; +} + +// send_respawn() +int ModApiClient::l_send_respawn(lua_State *L) +{ + getClient(L)->sendRespawn(); + return 0; +} + +// disconnect() +int ModApiClient::l_disconnect(lua_State *L) +{ + // Stops badly written Lua code form causing boot loops + if (getClient(L)->isShutdown()) { + lua_pushboolean(L, false); + return 1; + } + + g_gamecallback->disconnect(); + lua_pushboolean(L, true); + return 1; +} + +// gettext(text) +int ModApiClient::l_gettext(lua_State *L) +{ + std::string text = strgettext(std::string(luaL_checkstring(L, 1))); + lua_pushstring(L, text.c_str()); + + return 1; +} + +// get_node_or_nil(pos) +// pos = {x=num, y=num, z=num} +int ModApiClient::l_get_node_or_nil(lua_State *L) +{ + // pos + v3s16 pos = read_v3s16(L, 1); + + // Do it + bool pos_ok; + MapNode n = getClient(L)->CSMGetNode(pos, &pos_ok); + if (pos_ok) { + // Return node + pushnode(L, n, getClient(L)->ndef()); + } else { + lua_pushnil(L); + } + return 1; +} + +// get_langauge() +int ModApiClient::l_get_language(lua_State *L) +{ +#ifdef _WIN32 + char *locale = setlocale(LC_ALL, NULL); +#else + char *locale = setlocale(LC_MESSAGES, NULL); +#endif + std::string lang = gettext("LANG_CODE"); + if (lang == "LANG_CODE") + lang = ""; + + lua_pushstring(L, locale); + lua_pushstring(L, lang.c_str()); + return 2; +} + +// get_meta(pos) +int ModApiClient::l_get_meta(lua_State *L) +{ + v3s16 p = read_v3s16(L, 1); + + // check restrictions first + bool pos_ok; + getClient(L)->CSMGetNode(p, &pos_ok); + if (!pos_ok) + return 0; + + NodeMetadata *meta = getEnv(L)->getMap().getNodeMetadata(p); + NodeMetaRef::createClient(L, meta); + return 1; +} + +// sound_play(spec, parameters) +int ModApiClient::l_sound_play(lua_State *L) +{ + ISoundManager *sound = getClient(L)->getSoundManager(); + + SimpleSoundSpec spec; + read_soundspec(L, 1, spec); + + float gain = 1.0f; + float pitch = 1.0f; + bool looped = false; + s32 handle; + + if (lua_istable(L, 2)) { + getfloatfield(L, 2, "gain", gain); + getfloatfield(L, 2, "pitch", pitch); + getboolfield(L, 2, "loop", looped); + + lua_getfield(L, 2, "pos"); + if (!lua_isnil(L, -1)) { + v3f pos = read_v3f(L, -1) * BS; + lua_pop(L, 1); + handle = sound->playSoundAt( + spec.name, looped, gain * spec.gain, pos, pitch); + lua_pushinteger(L, handle); + return 1; + } + } + + handle = sound->playSound(spec.name, looped, gain * spec.gain, spec.fade, pitch); + lua_pushinteger(L, handle); + + return 1; +} + +// sound_stop(handle) +int ModApiClient::l_sound_stop(lua_State *L) +{ + s32 handle = luaL_checkinteger(L, 1); + + getClient(L)->getSoundManager()->stopSound(handle); + + return 0; +} + +// sound_fade(handle, step, gain) +int ModApiClient::l_sound_fade(lua_State *L) +{ + s32 handle = luaL_checkinteger(L, 1); + float step = readParam(L, 2); + float gain = readParam(L, 3); + getClient(L)->getSoundManager()->fadeSound(handle, step, gain); + return 0; +} + +// get_server_info() +int ModApiClient::l_get_server_info(lua_State *L) +{ + Client *client = getClient(L); + Address serverAddress = client->getServerAddress(); + lua_newtable(L); + lua_pushstring(L, client->getAddressName().c_str()); + lua_setfield(L, -2, "address"); + lua_pushstring(L, serverAddress.serializeString().c_str()); + lua_setfield(L, -2, "ip"); + lua_pushinteger(L, serverAddress.getPort()); + lua_setfield(L, -2, "port"); + lua_pushinteger(L, client->getProtoVersion()); + lua_setfield(L, -2, "protocol_version"); + return 1; +} + +// get_item_def(itemstring) +int ModApiClient::l_get_item_def(lua_State *L) +{ + IGameDef *gdef = getGameDef(L); + assert(gdef); + + IItemDefManager *idef = gdef->idef(); + assert(idef); + + if (checkCSMRestrictionFlag(CSM_RF_READ_ITEMDEFS)) + return 0; + + if (!lua_isstring(L, 1)) + return 0; + + std::string name = readParam(L, 1); + if (!idef->isKnown(name)) + return 0; + const ItemDefinition &def = idef->get(name); + + push_item_definition_full(L, def); + + return 1; +} + +// get_node_def(nodename) +int ModApiClient::l_get_node_def(lua_State *L) +{ + IGameDef *gdef = getGameDef(L); + assert(gdef); + + const NodeDefManager *ndef = gdef->ndef(); + assert(ndef); + + if (!lua_isstring(L, 1)) + return 0; + + if (checkCSMRestrictionFlag(CSM_RF_READ_NODEDEFS)) + return 0; + + std::string name = readParam(L, 1); + const ContentFeatures &cf = ndef->get(ndef->getId(name)); + if (cf.name != name) // Unknown node. | name = , cf.name = ignore + return 0; + + push_content_features(L, cf); + + return 1; +} + +// get_privilege_list() +int ModApiClient::l_get_privilege_list(lua_State *L) +{ + const Client *client = getClient(L); + lua_newtable(L); + for (const std::string &priv : client->getPrivilegeList()) { + lua_pushboolean(L, true); + lua_setfield(L, -2, priv.c_str()); + } + return 1; +} + +// get_builtin_path() +int ModApiClient::l_get_builtin_path(lua_State *L) +{ + lua_pushstring(L, BUILTIN_MOD_NAME ":"); + return 1; +} + +// get_csm_restrictions() +int ModApiClient::l_get_csm_restrictions(lua_State *L) +{ + u64 flags = getClient(L)->getCSMRestrictionFlags(); + const CSMFlagDesc *flagdesc = flagdesc_csm_restriction; + + lua_newtable(L); + for (int i = 0; flagdesc[i].name; i++) { + setboolfield(L, -1, flagdesc[i].name, !!(flags & flagdesc[i].flag)); + } + return 1; +} + +// send_damage(damage) +int ModApiClient::l_send_damage(lua_State *L) +{ + u16 damage = luaL_checknumber(L, 1); + getClient(L)->sendDamage(damage); + return 0; +} + +// place_node(pos) +int ModApiClient::l_place_node(lua_State *L) +{ + Client *client = getClient(L); + ClientMap &map = client->getEnv().getClientMap(); + LocalPlayer *player = client->getEnv().getLocalPlayer(); + ItemStack selected_item, hand_item; + player->getWieldedItem(&selected_item, &hand_item); + const ItemDefinition &selected_def = selected_item.getDefinition(getGameDef(L)->idef()); + v3s16 pos = read_v3s16(L, 1); + PointedThing pointed; + pointed.type = POINTEDTHING_NODE; + pointed.node_abovesurface = pos; + pointed.node_undersurface = pos; + NodeMetadata *meta = map.getNodeMetadata(pos); + g_game->nodePlacement(selected_def, selected_item, pos, pos, pointed, meta, true); // always force sneak + return 0; +} + +// dig_node(pos) +int ModApiClient::l_dig_node(lua_State *L) +{ + Client *client = getClient(L); + v3s16 pos = read_v3s16(L, 1); + PointedThing pointed; + pointed.type = POINTEDTHING_NODE; + pointed.node_abovesurface = pos; + pointed.node_undersurface = pos; + client->interact(INTERACT_START_DIGGING, pointed); + client->interact(INTERACT_DIGGING_COMPLETED, pointed); + return 0; +} + +// get_inventory(location) +int ModApiClient::l_get_inventory(lua_State *L) +{ + Client *client = getClient(L); + InventoryLocation inventory_location; + Inventory *inventory; + std::string location; + + location = readParam(L, 1); + + try { + inventory_location.deSerialize(location); + inventory = client->getInventory(inventory_location); + + if (!inventory) { + lua_pushnil(L); + return 1; + } + + push_inventory(L, inventory); + } catch (SerializationError &) { + lua_pushnil(L); + } + + return 1; +} + +// set_keypress(key_setting, pressed) -> returns true on success +int ModApiClient::l_set_keypress(lua_State *L) +{ + std::string setting_name = "keymap_" + readParam(L, 1); + bool pressed = lua_isboolean(L, 2) && readParam(L, 2); + try { + KeyPress keyCode = getKeySetting(setting_name.c_str()); + if (pressed) + g_game->input->setKeypress(keyCode); + else + g_game->input->unsetKeypress(keyCode); + lua_pushboolean(L, true); + } catch (SettingNotFoundException &) { + lua_pushboolean(L, false); + } + return 1; +} + +// drop_selected_item() +int ModApiClient::l_drop_selected_item(lua_State *L) +{ + g_game->dropSelectedItem(); + return 0; +} + +//take_screenshot() +int ModApiClient::l_take_screenshot(lua_State *L) +{ + getClient(L)->makeScreenshot(true); + lua_pushboolean(L, true); + return 1; +} + +StringMap *table_to_stringmap(lua_State *L, int index) +{ + StringMap *m = new StringMap; + + lua_pushvalue(L, index); + lua_pushnil(L); + + while (lua_next(L, -2)) { + lua_pushvalue(L, -2); + std::basic_string key = lua_tostring(L, -1); + std::basic_string value = lua_tostring(L, -2); + (*m)[key] = value; + lua_pop(L, 2); + } + + lua_pop(L, 1); + + return m; +} + +// send_inventory_fields(formname, fields) +// Only works if the inventory form was opened beforehand. +int ModApiClient::l_send_inventory_fields(lua_State *L) +{ + std::string formname = luaL_checkstring(L, 1); + StringMap *fields = table_to_stringmap(L, 2); + + getClient(L)->sendInventoryFields(formname, *fields); + return 0; +} + +// send_nodemeta_fields(position, formname, fields) +int ModApiClient::l_send_nodemeta_fields(lua_State *L) +{ + v3s16 pos = check_v3s16(L, 1); + std::string formname = luaL_checkstring(L, 2); + StringMap *m = table_to_stringmap(L, 3); + + getClient(L)->sendNodemetaFields(pos, formname, *m); + return 0; +} + +// interact(mode, pointed) +int ModApiClient::l_interact(lua_State *L) +{ + std::string mode = luaL_checkstring(L, 1); + PointedThing pointed; + int imode; + + if (lua_gettop(L) > 1) { + v3s16 pos = check_v3s16(L, 2); + pointed.type = POINTEDTHING_NODE; + pointed.node_abovesurface = pos; + pointed.node_undersurface = pos; + } else { + Camera *camera = getClient(L)->getCamera(); + const v3f camera_direction = camera->getDirection(); + const v3s16 camera_offset = camera->getOffset(); + + IItemDefManager *itemdef_manager = createItemDefManager(); + ItemStack selected_item, hand_item; + const ItemDefinition &selected_def = selected_item.getDefinition(itemdef_manager); + f32 d = getToolRange(selected_def, hand_item.getDefinition(itemdef_manager)); + + if (g_settings->getBool("increase_tool_range")) + d += 2; + if (g_settings->getBool("increase_tool_range_plus")) + d = 1000; + + core::line3d shootline; + + switch (camera->getCameraMode()) { + case CAMERA_MODE_FIRST: + // Shoot from camera position, with bobbing + shootline.start = camera->getPosition(); + break; + case CAMERA_MODE_THIRD: + // Shoot from player head, no bobbing + shootline.start = camera->getHeadPosition(); + break; + case CAMERA_MODE_THIRD_FRONT: + shootline.start = camera->getHeadPosition(); + // prevent player pointing anything in front-view + d = 0; + break; + } + shootline.end = shootline.start + camera_direction * BS * d; + GameRunData runData = GameRunData(); + + pointed = g_game->updatePointedThing(shootline, + selected_def.liquids_pointable, + !runData.btn_down_for_dig, + camera_offset); + } + + struct EnumString interact_modes[] = { + {INTERACT_START_DIGGING, "start_digging"}, + {INTERACT_STOP_DIGGING, "stop_digging"}, + {INTERACT_DIGGING_COMPLETED, "digging_completed"}, + {INTERACT_PLACE, "place"}, + {INTERACT_USE, "use"}, + {INTERACT_ACTIVATE, "activate"}, + {0, NULL} + }; + + string_to_enum(interact_modes, imode, mode); + + getClient(L)->interact((InteractAction)imode, pointed); + + lua_pushboolean(L, true); + return 0; +} +//show_huds() +int ModApiClient::l_show_huds(lua_State *L) +{ + g_game->show_huds(); + return 0; +} +//hide_huds() +int ModApiClient::l_hide_huds(lua_State *L) +{ + g_game->hide_huds(); + return 0; +} + +// get_objects_inside_radius(pos, radius) +int ModApiClient::l_get_objects_inside_radius(lua_State *L) +{ + ClientEnvironment &env = getClient(L)->getEnv(); + + v3f pos = checkFloatPos(L, 1); + float radius = readParam(L, 2) * BS; + + std::vector objs; + env.getActiveObjects(pos, radius, objs); + + int i = 0; + lua_createtable(L, objs.size(), 0); + for (const auto obj : objs) { + ClientObjectRef::create(L, obj.obj); // TODO: getObjectRefOrCreate + lua_rawseti(L, -2, ++i); + } + return 1; +} + +void ModApiClient::Initialize(lua_State *L, int top) +{ + API_FCT(get_current_modname); + API_FCT(get_modpath); + API_FCT(print); + API_FCT(display_chat_message); + API_FCT(send_chat_message); + API_FCT(clear_out_chat_queue); + API_FCT(get_player_names); + API_FCT(set_last_run_mod); + API_FCT(get_last_run_mod); + API_FCT(show_formspec); + API_FCT(send_respawn); + API_FCT(gettext); + API_FCT(get_node_or_nil); + API_FCT(disconnect); + API_FCT(get_meta); + API_FCT(sound_play); + API_FCT(sound_stop); + API_FCT(sound_fade); + API_FCT(get_server_info); + API_FCT(get_item_def); + API_FCT(get_node_def); + API_FCT(get_privilege_list); + API_FCT(get_builtin_path); + API_FCT(get_language); + API_FCT(get_csm_restrictions); + API_FCT(send_damage); + API_FCT(place_node); + API_FCT(dig_node); + API_FCT(get_inventory); + API_FCT(set_keypress); + API_FCT(drop_selected_item); + API_FCT(take_screenshot); + API_FCT(send_inventory_fields); + API_FCT(send_nodemeta_fields); + API_FCT(interact); + API_FCT(show_huds); + API_FCT(hide_huds); + API_FCT(get_objects_inside_radius); +} diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h new file mode 100644 index 000000000..6076303df --- /dev/null +++ b/src/script/lua_api/l_client.h @@ -0,0 +1,149 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" +#include "itemdef.h" +#include "tool.h" + +class ModApiClient : public ModApiBase +{ +private: + // get_current_modname() + static int l_get_current_modname(lua_State *L); + + // get_modpath(modname) + static int l_get_modpath(lua_State *L); + + // print(text) + static int l_print(lua_State *L); + + // display_chat_message(message) + static int l_display_chat_message(lua_State *L); + + // send_chat_message(message) + static int l_send_chat_message(lua_State *L); + + // clear_out_chat_queue() + static int l_clear_out_chat_queue(lua_State *L); + + // get_player_names() + static int l_get_player_names(lua_State *L); + + // show_formspec(name, formspec) + static int l_show_formspec(lua_State *L); + + // send_respawn() + static int l_send_respawn(lua_State *L); + + // disconnect() + static int l_disconnect(lua_State *L); + + // gettext(text) + static int l_gettext(lua_State *L); + + // get_last_run_mod(n) + static int l_get_last_run_mod(lua_State *L); + + // set_last_run_mod(modname) + static int l_set_last_run_mod(lua_State *L); + + // get_node(pos) + static int l_get_node_or_nil(lua_State *L); + + // get_language() + static int l_get_language(lua_State *L); + + // get_wielded_item() + static int l_get_wielded_item(lua_State *L); + + // get_meta(pos) + static int l_get_meta(lua_State *L); + + // sound_play(spec, parameters) + static int l_sound_play(lua_State *L); + + // sound_stop(handle) + static int l_sound_stop(lua_State *L); + + // sound_fade(handle, step, gain) + static int l_sound_fade(lua_State *L); + + // get_server_info() + static int l_get_server_info(lua_State *L); + + // get_item_def(itemstring) + static int l_get_item_def(lua_State *L); + + // get_node_def(nodename) + static int l_get_node_def(lua_State *L); + + // get_privilege_list() + static int l_get_privilege_list(lua_State *L); + + // get_builtin_path() + static int l_get_builtin_path(lua_State *L); + + // get_csm_restrictions() + static int l_get_csm_restrictions(lua_State *L); + + // send_damage(damage) + static int l_send_damage(lua_State *L); + + // place_node(pos) + static int l_place_node(lua_State *L); + + // dig_node(pos) + static int l_dig_node(lua_State *L); + + // get_inventory(location) + static int l_get_inventory(lua_State *L); + + // set_keypress(key_setting, pressed) + static int l_set_keypress(lua_State *L); + + // drop_selected_item() + static int l_drop_selected_item(lua_State *L); + + // take_screenshot() + static int l_take_screenshot(lua_State *L); + + // send_inventory_fields(formname, fields) + static int l_send_inventory_fields(lua_State *L); + + // send_nodemeta_fields(position, formname, fields) + static int l_send_nodemeta_fields(lua_State *L); + + // interact(0-5) + static int l_interact(lua_State *L); + + // show_huds + static int l_show_huds(lua_State *L); + + // hide_huds + static int l_hide_huds(lua_State *L); + + // get_objects_inside_radius(pos, radius) + static int l_get_objects_inside_radius(lua_State *L); + +public: + static void Initialize(lua_State *L, int top); +}; diff --git a/src/script/lua_api/l_clientobject.cpp b/src/script/lua_api/l_clientobject.cpp new file mode 100644 index 000000000..98e252da7 --- /dev/null +++ b/src/script/lua_api/l_clientobject.cpp @@ -0,0 +1,287 @@ +// CC0/Unlicense system32 2020 + +#include "lua_api/l_clientobject.h" +#include "l_internal.h" +#include "common/c_converter.h" +#include "client/client.h" +#include "object_properties.h" +#include "util/pointedthing.h" + +// should prob do some more NULL checking + + +ClientObjectRef *ClientObjectRef::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *userdata = luaL_checkudata(L, narg, className); + if (!userdata) + luaL_typerror(L, narg, className); + return *(ClientObjectRef**)userdata; +} + +ClientActiveObject *ClientObjectRef::get_cao(ClientObjectRef *ref) +{ + ClientActiveObject *obj = ref->m_object; + return obj; +} + +GenericCAO *ClientObjectRef::get_generic_cao(ClientObjectRef *ref, lua_State *L) +{ + ClientActiveObject *obj = get_cao(ref); + ClientEnvironment &env = getClient(L)->getEnv(); + GenericCAO *gcao = env.getGenericCAO(obj->getId()); + return gcao; +} +int ClientObjectRef::l_get_id(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + if(!gcao) return 0; + lua_pushvalue(L, gcao->getId()); + return 1; +} + +int ClientObjectRef::l_get_pos(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + ClientActiveObject *gcao = get_cao(ref); + if(!gcao) return 0; + push_v3f(L, gcao->getPosition() / BS); + return 1; +} + +int ClientObjectRef::l_get_velocity(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + if(!gcao) return 0; + push_v3f(L, gcao->getVelocity() / BS); + return 1; +} + +int ClientObjectRef::l_get_acceleration(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + if(!gcao) return 0; + push_v3f(L, gcao->getAcceleration() / BS); + return 1; +} + +int ClientObjectRef::l_get_rotation(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + push_v3f(L, gcao->getRotation()); + return 1; +} + +int ClientObjectRef::l_is_player(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + lua_pushboolean(L, gcao->isPlayer()); + return 1; +} + +int ClientObjectRef::l_is_local_player(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + lua_pushboolean(L, gcao->isLocalPlayer()); + return 1; +} + +int ClientObjectRef::l_get_name(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + lua_pushstring(L, gcao->getName().c_str()); + return 1; +} + +int ClientObjectRef::l_get_parent(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + create(L, gcao->getParent()); + return 1; +} + +int ClientObjectRef::l_get_nametag(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + ObjectProperties *props = gcao->getProperties(); + lua_pushstring(L, props->nametag.c_str()); + return 1; +} + +int ClientObjectRef::l_get_item_textures(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + ObjectProperties *props = gcao->getProperties(); + lua_newtable(L); + + for (std::string &texture : props->textures) { + lua_pushstring(L, texture.c_str()); + } + return 1; +} + +int ClientObjectRef::l_set_visible(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + if(!gcao) return 0; + gcao->setVisible(readParam(L, -1)); + return 0; +} + +int ClientObjectRef::l_remove_from_scene(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + if(!gcao) return 0; + gcao->removeFromScene(readParam(L, -1)); + return 0; +} + +int ClientObjectRef::l_remove(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + ClientActiveObject *cao = get_cao(ref); + if (! cao) + return 0; + getClient(L)->getEnv().removeActiveObject(cao->getId()); + + return 0; +} + +int ClientObjectRef::l_get_hp(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + lua_pushnumber(L, gcao->getHp()); + return 1; +} + +int ClientObjectRef::l_get_max_hp(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + ObjectProperties *props = gcao->getProperties(); + lua_pushnumber(L, props->hp_max); + return 1; +} + +int ClientObjectRef::l_punch(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + if(!gcao) return 0; + PointedThing pointed(gcao->getId(), v3f(0, 0, 0), v3s16(0, 0, 0), 0); + getClient(L)->interact(INTERACT_START_DIGGING, pointed); + return 0; +} + +int ClientObjectRef::l_rightclick(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if(!gcao) return 0; + PointedThing pointed(gcao->getId(), v3f(0, 0, 0), v3s16(0, 0, 0), 0); + getClient(L)->interact(INTERACT_PLACE, pointed); + return 0; +} + +ClientObjectRef::ClientObjectRef(ClientActiveObject *object) : m_object(object) +{ +} + +void ClientObjectRef::create(lua_State *L, ClientActiveObject *object) +{ + if (object) { + ClientObjectRef *o = new ClientObjectRef(object); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + } +} + +void ClientObjectRef::create(lua_State *L, s16 id) +{ + create(L, ((ClientEnvironment *)getEnv(L))->getActiveObject(id)); +} + +int ClientObjectRef::gc_object(lua_State *L) +{ + ClientObjectRef *obj = *(ClientObjectRef **)(lua_touserdata(L, 1)); + delete obj; + return 0; +} + +// taken from LuaLocalPlayer +void ClientObjectRef::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // Drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // Drop methodtable +} + +const char ClientObjectRef::className[] = "ClientObjectRef"; +luaL_Reg ClientObjectRef::methods[] = { + luamethod(ClientObjectRef, get_id), + luamethod(ClientObjectRef, get_pos), + luamethod(ClientObjectRef, get_velocity), + luamethod(ClientObjectRef, get_acceleration), + luamethod(ClientObjectRef, get_rotation), + luamethod(ClientObjectRef, is_player), + luamethod(ClientObjectRef, get_name), + luamethod(ClientObjectRef, get_parent), + luamethod(ClientObjectRef, get_nametag), + luamethod(ClientObjectRef, get_item_textures), + luamethod(ClientObjectRef, get_max_hp), + luamethod(ClientObjectRef, set_visible), + luamethod(ClientObjectRef, remove_from_scene), + luamethod(ClientObjectRef, remove), + luamethod(ClientObjectRef, is_local_player), + luamethod(ClientObjectRef, get_hp), + luamethod(ClientObjectRef, punch), + luamethod(ClientObjectRef, rightclick), + {0, 0} +}; diff --git a/src/script/lua_api/l_clientobject.h b/src/script/lua_api/l_clientobject.h new file mode 100644 index 000000000..1ff745a51 --- /dev/null +++ b/src/script/lua_api/l_clientobject.h @@ -0,0 +1,85 @@ +// CC0/Unlicense system32 2020 + +#pragma once + +#include "lua_api/l_base.h" +#include "client/clientobject.h" +#include "client/content_cao.h" + +class ClientObjectRef : public ModApiBase +{ +public: + ClientObjectRef(ClientActiveObject *object); + + ~ClientObjectRef() = default; + + static void Register(lua_State *L); + + static void create(lua_State *L, ClientActiveObject *object); + + static void create(lua_State *L, s16 id); + + static ClientObjectRef *checkobject(lua_State *L, int narg); + +private: + ClientActiveObject *m_object = nullptr; + static const char className[]; + static luaL_Reg methods[]; + + static ClientActiveObject *get_cao(ClientObjectRef *ref); + static GenericCAO *get_generic_cao(ClientObjectRef *ref, lua_State *L); + + static int gc_object(lua_State *L); + + static int l_get_id(lua_State *L); + + // get_pos(self) + // returns: {x=num, y=num, z=num} + static int l_get_pos(lua_State *L); + + // get_velocity(self) + static int l_get_velocity(lua_State *L); + + // get_acceleration(self) + static int l_get_acceleration(lua_State *L); + + // get_rotation(self) + static int l_get_rotation(lua_State *L); + + // is_player(self) + static int l_is_player(lua_State *L); + + // is_local_player(self) + static int l_is_local_player(lua_State *L); + + // get_name(self) + static int l_get_name(lua_State *L); + + // get_parent(self) + static int l_get_parent(lua_State *L); + + // get_nametag(self) + static int l_get_nametag(lua_State *L); + + // get_textures(self) + static int l_get_item_textures(lua_State *L); + + // get_hp(self) + static int l_get_max_hp(lua_State *L); + + // set_visible(self, visible) + static int l_set_visible(lua_State *L); + + // remove_from_scene(self, permanent) + static int l_remove_from_scene(lua_State *L); + static int l_remove(lua_State *L); + + // get_hp(self) + static int l_get_hp(lua_State *L); + + // punch(self) + static int l_punch(lua_State *L); + + // rightclick(self) + static int l_rightclick(lua_State *L); +}; diff --git a/src/script/lua_api/l_craft.cpp b/src/script/lua_api/l_craft.cpp new file mode 100644 index 000000000..2acae292e --- /dev/null +++ b/src/script/lua_api/l_craft.cpp @@ -0,0 +1,529 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#include "lua_api/l_craft.h" +#include "lua_api/l_internal.h" +#include "lua_api/l_item.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "server.h" +#include "craftdef.h" + +struct EnumString ModApiCraft::es_CraftMethod[] = +{ + {CRAFT_METHOD_NORMAL, "normal"}, + {CRAFT_METHOD_COOKING, "cooking"}, + {CRAFT_METHOD_FUEL, "fuel"}, + {0, NULL}, +}; + +// helper for register_craft +bool ModApiCraft::readCraftRecipeShaped(lua_State *L, int index, + int &width, std::vector &recipe) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + if(!lua_istable(L, index)) + return false; + + lua_pushnil(L); + int rowcount = 0; + while(lua_next(L, index) != 0){ + int colcount = 0; + // key at index -2 and value at index -1 + if(!lua_istable(L, -1)) + return false; + int table2 = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, table2) != 0){ + // key at index -2 and value at index -1 + if(!lua_isstring(L, -1)) + return false; + recipe.emplace_back(readParam(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + colcount++; + } + if(rowcount == 0){ + width = colcount; + } else { + if(colcount != width) + return false; + } + // removes value, keeps key for next iteration + lua_pop(L, 1); + rowcount++; + } + return width != 0; +} + +// helper for register_craft +bool ModApiCraft::readCraftRecipeShapeless(lua_State *L, int index, + std::vector &recipe) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + if(!lua_istable(L, index)) + return false; + + lua_pushnil(L); + while(lua_next(L, index) != 0){ + // key at index -2 and value at index -1 + if(!lua_isstring(L, -1)) + return false; + recipe.emplace_back(readParam(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + return true; +} + +// helper for register_craft +bool ModApiCraft::readCraftReplacements(lua_State *L, int index, + CraftReplacements &replacements) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + if(!lua_istable(L, index)) + return false; + + lua_pushnil(L); + while(lua_next(L, index) != 0){ + // key at index -2 and value at index -1 + if(!lua_istable(L, -1)) + return false; + lua_rawgeti(L, -1, 1); + if(!lua_isstring(L, -1)) + return false; + std::string replace_from = readParam(L, -1); + lua_pop(L, 1); + lua_rawgeti(L, -1, 2); + if(!lua_isstring(L, -1)) + return false; + std::string replace_to = readParam(L, -1); + lua_pop(L, 1); + replacements.pairs.emplace_back(replace_from, replace_to); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + return true; +} +// register_craft({output=item, recipe={{item00,item10},{item01,item11}}) +int ModApiCraft::l_register_craft(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + //infostream<<"register_craft"<getWritableCraftDefManager(); + + std::string type = getstringfield_default(L, table, "type", "shaped"); + + /* + CraftDefinitionShaped + */ + if(type == "shaped"){ + std::string output = getstringfield_default(L, table, "output", ""); + if (output.empty()) + throw LuaError("Crafting definition is missing an output"); + + int width = 0; + std::vector recipe; + lua_getfield(L, table, "recipe"); + if(lua_isnil(L, -1)) + throw LuaError("Crafting definition is missing a recipe" + " (output=\"" + output + "\")"); + if(!readCraftRecipeShaped(L, -1, width, recipe)) + throw LuaError("Invalid crafting recipe" + " (output=\"" + output + "\")"); + + CraftReplacements replacements; + lua_getfield(L, table, "replacements"); + if(!lua_isnil(L, -1)) + { + if(!readCraftReplacements(L, -1, replacements)) + throw LuaError("Invalid replacements" + " (output=\"" + output + "\")"); + } + + CraftDefinition *def = new CraftDefinitionShaped( + output, width, recipe, replacements); + craftdef->registerCraft(def, getServer(L)); + } + /* + CraftDefinitionShapeless + */ + else if(type == "shapeless"){ + std::string output = getstringfield_default(L, table, "output", ""); + if (output.empty()) + throw LuaError("Crafting definition (shapeless)" + " is missing an output"); + + std::vector recipe; + lua_getfield(L, table, "recipe"); + if(lua_isnil(L, -1)) + throw LuaError("Crafting definition (shapeless)" + " is missing a recipe" + " (output=\"" + output + "\")"); + if(!readCraftRecipeShapeless(L, -1, recipe)) + throw LuaError("Invalid crafting recipe" + " (output=\"" + output + "\")"); + + CraftReplacements replacements; + lua_getfield(L, table, "replacements"); + if(!lua_isnil(L, -1)) + { + if(!readCraftReplacements(L, -1, replacements)) + throw LuaError("Invalid replacements" + " (output=\"" + output + "\")"); + } + + CraftDefinition *def = new CraftDefinitionShapeless( + output, recipe, replacements); + craftdef->registerCraft(def, getServer(L)); + } + /* + CraftDefinitionToolRepair + */ + else if(type == "toolrepair"){ + float additional_wear = getfloatfield_default(L, table, + "additional_wear", 0.0); + + CraftDefinition *def = new CraftDefinitionToolRepair( + additional_wear); + craftdef->registerCraft(def, getServer(L)); + } + /* + CraftDefinitionCooking + */ + else if(type == "cooking"){ + std::string output = getstringfield_default(L, table, "output", ""); + if (output.empty()) + throw LuaError("Crafting definition (cooking)" + " is missing an output"); + + std::string recipe = getstringfield_default(L, table, "recipe", ""); + if (recipe.empty()) + throw LuaError("Crafting definition (cooking)" + " is missing a recipe" + " (output=\"" + output + "\")"); + + float cooktime = getfloatfield_default(L, table, "cooktime", 3.0); + + CraftReplacements replacements; + lua_getfield(L, table, "replacements"); + if(!lua_isnil(L, -1)) + { + if(!readCraftReplacements(L, -1, replacements)) + throw LuaError("Invalid replacements" + " (cooking output=\"" + output + "\")"); + } + + CraftDefinition *def = new CraftDefinitionCooking( + output, recipe, cooktime, replacements); + craftdef->registerCraft(def, getServer(L)); + } + /* + CraftDefinitionFuel + */ + else if(type == "fuel"){ + std::string recipe = getstringfield_default(L, table, "recipe", ""); + if (recipe.empty()) + throw LuaError("Crafting definition (fuel)" + " is missing a recipe"); + + float burntime = getfloatfield_default(L, table, "burntime", 1.0); + + CraftReplacements replacements; + lua_getfield(L, table, "replacements"); + if(!lua_isnil(L, -1)) + { + if(!readCraftReplacements(L, -1, replacements)) + throw LuaError("Invalid replacements" + " (fuel recipe=\"" + recipe + "\")"); + } + + CraftDefinition *def = new CraftDefinitionFuel( + recipe, burntime, replacements); + craftdef->registerCraft(def, getServer(L)); + } + else + { + throw LuaError("Unknown crafting definition type: \"" + type + "\""); + } + + lua_pop(L, 1); + return 0; /* number of results */ +} + +// clear_craft({[output=item], [recipe={{item00,item10},{item01,item11}}]) +int ModApiCraft::l_clear_craft(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + luaL_checktype(L, 1, LUA_TTABLE); + int table = 1; + + // Get the writable craft definition manager from the server + IWritableCraftDefManager *craftdef = + getServer(L)->getWritableCraftDefManager(); + + std::string output = getstringfield_default(L, table, "output", ""); + std::string type = getstringfield_default(L, table, "type", "shaped"); + CraftOutput c_output(output, 0); + if (!output.empty()) { + if (craftdef->clearCraftsByOutput(c_output, getServer(L))) { + lua_pushboolean(L, true); + return 1; + } + + warningstream << "No craft recipe known for output" << std::endl; + lua_pushboolean(L, false); + return 1; + } + std::vector recipe; + int width = 0; + CraftMethod method = CRAFT_METHOD_NORMAL; + /* + CraftDefinitionShaped + */ + if (type == "shaped") { + lua_getfield(L, table, "recipe"); + if (lua_isnil(L, -1)) + throw LuaError("Either output or recipe has to be defined"); + if (!readCraftRecipeShaped(L, -1, width, recipe)) + throw LuaError("Invalid crafting recipe"); + } + /* + CraftDefinitionShapeless + */ + else if (type == "shapeless") { + lua_getfield(L, table, "recipe"); + if (lua_isnil(L, -1)) + throw LuaError("Either output or recipe has to be defined"); + if (!readCraftRecipeShapeless(L, -1, recipe)) + throw LuaError("Invalid crafting recipe"); + } + /* + CraftDefinitionCooking + */ + else if (type == "cooking") { + method = CRAFT_METHOD_COOKING; + std::string rec = getstringfield_default(L, table, "recipe", ""); + if (rec.empty()) + throw LuaError("Crafting definition (cooking)" + " is missing a recipe"); + recipe.push_back(rec); + } + /* + CraftDefinitionFuel + */ + else if (type == "fuel") { + method = CRAFT_METHOD_FUEL; + std::string rec = getstringfield_default(L, table, "recipe", ""); + if (rec.empty()) + throw LuaError("Crafting definition (fuel)" + " is missing a recipe"); + recipe.push_back(rec); + } else { + throw LuaError("Unknown crafting definition type: \"" + type + "\""); + } + + std::vector items; + items.reserve(recipe.size()); + for (const auto &item : recipe) + items.emplace_back(item, 1, 0, getServer(L)->idef()); + CraftInput input(method, width, items); + + if (!craftdef->clearCraftsByInput(input, getServer(L))) { + warningstream << "No craft recipe matches input" << std::endl; + lua_pushboolean(L, false); + return 1; + } + + lua_pushboolean(L, true); + return 1; +} + +// get_craft_result(input) +int ModApiCraft::l_get_craft_result(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + int input_i = 1; + std::string method_s = getstringfield_default(L, input_i, "method", "normal"); + enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method", + es_CraftMethod, CRAFT_METHOD_NORMAL); + int width = 1; + lua_getfield(L, input_i, "width"); + if(lua_isnumber(L, -1)) + width = luaL_checkinteger(L, -1); + lua_pop(L, 1); + lua_getfield(L, input_i, "items"); + std::vector items = read_items(L, -1,getServer(L)); + lua_pop(L, 1); // items + + IGameDef *gdef = getServer(L); + ICraftDefManager *cdef = gdef->cdef(); + CraftInput input(method, width, items); + CraftOutput output; + std::vector output_replacements; + bool got = cdef->getCraftResult(input, output, output_replacements, true, gdef); + lua_newtable(L); // output table + if (got) { + ItemStack item; + item.deSerialize(output.item, gdef->idef()); + LuaItemStack::create(L, item); + lua_setfield(L, -2, "item"); + setintfield(L, -1, "time", output.time); + push_items(L, output_replacements); + lua_setfield(L, -2, "replacements"); + } else { + LuaItemStack::create(L, ItemStack()); + lua_setfield(L, -2, "item"); + setintfield(L, -1, "time", 0); + lua_newtable(L); + lua_setfield(L, -2, "replacements"); + } + lua_newtable(L); // decremented input table + lua_pushstring(L, method_s.c_str()); + lua_setfield(L, -2, "method"); + lua_pushinteger(L, width); + lua_setfield(L, -2, "width"); + push_items(L, input.items); + lua_setfield(L, -2, "items"); + return 2; +} + + +static void push_craft_recipe(lua_State *L, IGameDef *gdef, + const CraftDefinition *recipe, + const CraftOutput &tmpout) +{ + CraftInput input = recipe->getInput(tmpout, gdef); + CraftOutput output = recipe->getOutput(input, gdef); + + lua_newtable(L); // items + std::vector::const_iterator iter = input.items.begin(); + for (u16 j = 1; iter != input.items.end(); ++iter, j++) { + if (iter->empty()) + continue; + lua_pushstring(L, iter->name.c_str()); + lua_rawseti(L, -2, j); + } + lua_setfield(L, -2, "items"); + setintfield(L, -1, "width", input.width); + + std::string method_s; + switch (input.method) { + case CRAFT_METHOD_NORMAL: + method_s = "normal"; + break; + case CRAFT_METHOD_COOKING: + method_s = "cooking"; + break; + case CRAFT_METHOD_FUEL: + method_s = "fuel"; + break; + default: + method_s = "unknown"; + } + lua_pushstring(L, method_s.c_str()); + lua_setfield(L, -2, "method"); + + // Deprecated, only for compatibility's sake + lua_pushstring(L, method_s.c_str()); + lua_setfield(L, -2, "type"); + + lua_pushstring(L, output.item.c_str()); + lua_setfield(L, -2, "output"); +} + +static void push_craft_recipes(lua_State *L, IGameDef *gdef, + const std::vector &recipes, + const CraftOutput &output) +{ + lua_createtable(L, recipes.size(), 0); + + if (recipes.empty()) { + lua_pushnil(L); + return; + } + + std::vector::const_iterator it = recipes.begin(); + for (unsigned i = 0; it != recipes.end(); ++it) { + lua_newtable(L); + push_craft_recipe(L, gdef, *it, output); + lua_rawseti(L, -2, ++i); + } +} + + +// get_craft_recipe(result item) +int ModApiCraft::l_get_craft_recipe(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + std::string item = luaL_checkstring(L, 1); + Server *server = getServer(L); + CraftOutput output(item, 0); + std::vector recipes = server->cdef() + ->getCraftRecipes(output, server, 1); + + lua_createtable(L, 1, 0); + + if (recipes.empty()) { + lua_pushnil(L); + lua_setfield(L, -2, "items"); + setintfield(L, -1, "width", 0); + return 1; + } + push_craft_recipe(L, server, recipes[0], output); + return 1; +} + +// get_all_craft_recipes(result item) +int ModApiCraft::l_get_all_craft_recipes(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + std::string item = luaL_checkstring(L, 1); + Server *server = getServer(L); + CraftOutput output(item, 0); + std::vector recipes = server->cdef() + ->getCraftRecipes(output, server); + + push_craft_recipes(L, server, recipes, output); + return 1; +} + +void ModApiCraft::Initialize(lua_State *L, int top) +{ + API_FCT(get_all_craft_recipes); + API_FCT(get_craft_recipe); + API_FCT(get_craft_result); + API_FCT(register_craft); + API_FCT(clear_craft); +} diff --git a/src/script/lua_api/l_craft.h b/src/script/lua_api/l_craft.h new file mode 100644 index 000000000..d6f027f1d --- /dev/null +++ b/src/script/lua_api/l_craft.h @@ -0,0 +1,48 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include + +#include "lua_api/l_base.h" + +struct CraftReplacements; + +class ModApiCraft : public ModApiBase { +private: + static int l_register_craft(lua_State *L); + static int l_get_craft_recipe(lua_State *L); + static int l_get_all_craft_recipes(lua_State *L); + static int l_get_craft_result(lua_State *L); + static int l_clear_craft(lua_State *L); + + static bool readCraftReplacements(lua_State *L, int index, + CraftReplacements &replacements); + static bool readCraftRecipeShapeless(lua_State *L, int index, + std::vector &recipe); + static bool readCraftRecipeShaped(lua_State *L, int index, + int &width, std::vector &recipe); + + static struct EnumString es_CraftMethod[]; + +public: + static void Initialize(lua_State *L, int top); +}; diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp new file mode 100644 index 000000000..660d2e12e --- /dev/null +++ b/src/script/lua_api/l_env.cpp @@ -0,0 +1,1616 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include "lua_api/l_env.h" +#include "lua_api/l_internal.h" +#include "lua_api/l_nodemeta.h" +#include "lua_api/l_nodetimer.h" +#include "lua_api/l_noise.h" +#include "lua_api/l_vmanip.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "scripting_server.h" +#include "environment.h" +#include "mapblock.h" +#include "server.h" +#include "nodedef.h" +#include "daynightratio.h" +#include "util/pointedthing.h" +#include "mapgen/treegen.h" +#include "emerge.h" +#include "pathfinder.h" +#include "face_position_cache.h" +#include "remoteplayer.h" +#include "server/luaentity_sao.h" +#include "server/player_sao.h" +#include "util/string.h" +#include "translation.h" +#ifndef SERVER +#include "client/client.h" +#endif + +struct EnumString ModApiEnvMod::es_ClearObjectsMode[] = +{ + {CLEAR_OBJECTS_MODE_FULL, "full"}, + {CLEAR_OBJECTS_MODE_QUICK, "quick"}, + {0, NULL}, +}; + +/////////////////////////////////////////////////////////////////////////////// + + +void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, + u32 active_object_count, u32 active_object_count_wider) +{ + ServerScripting *scriptIface = env->getScriptIface(); + scriptIface->realityCheck(); + + lua_State *L = scriptIface->getStack(); + sanity_check(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + int error_handler = PUSH_ERROR_HANDLER(L); + + // Get registered_abms + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_abms"); + luaL_checktype(L, -1, LUA_TTABLE); + lua_remove(L, -2); // Remove core + + // Get registered_abms[m_id] + lua_pushinteger(L, m_id); + lua_gettable(L, -2); + if(lua_isnil(L, -1)) + FATAL_ERROR(""); + lua_remove(L, -2); // Remove registered_abms + + scriptIface->setOriginFromTable(-1); + + // Call action + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, "action"); + luaL_checktype(L, -1, LUA_TFUNCTION); + lua_remove(L, -2); // Remove registered_abms[m_id] + push_v3s16(L, p); + pushnode(L, n, env->getGameDef()->ndef()); + lua_pushnumber(L, active_object_count); + lua_pushnumber(L, active_object_count_wider); + + int result = lua_pcall(L, 4, 0, error_handler); + if (result) + scriptIface->scriptError(result, "LuaABM::trigger"); + + lua_pop(L, 1); // Pop error handler +} + +void LuaLBM::trigger(ServerEnvironment *env, v3s16 p, MapNode n) +{ + ServerScripting *scriptIface = env->getScriptIface(); + scriptIface->realityCheck(); + + lua_State *L = scriptIface->getStack(); + sanity_check(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + int error_handler = PUSH_ERROR_HANDLER(L); + + // Get registered_lbms + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_lbms"); + luaL_checktype(L, -1, LUA_TTABLE); + lua_remove(L, -2); // Remove core + + // Get registered_lbms[m_id] + lua_pushinteger(L, m_id); + lua_gettable(L, -2); + FATAL_ERROR_IF(lua_isnil(L, -1), "Entry with given id not found in registered_lbms table"); + lua_remove(L, -2); // Remove registered_lbms + + scriptIface->setOriginFromTable(-1); + + // Call action + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, "action"); + luaL_checktype(L, -1, LUA_TFUNCTION); + lua_remove(L, -2); // Remove registered_lbms[m_id] + push_v3s16(L, p); + pushnode(L, n, env->getGameDef()->ndef()); + + int result = lua_pcall(L, 2, 0, error_handler); + if (result) + scriptIface->scriptError(result, "LuaLBM::trigger"); + + lua_pop(L, 1); // Pop error handler +} + +int LuaRaycast::l_next(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + bool csm = false; +#ifndef SERVER + csm = getClient(L) != nullptr; +#endif + + LuaRaycast *o = checkobject(L, 1); + PointedThing pointed; + env->continueRaycast(&o->state, &pointed); + if (pointed.type == POINTEDTHING_NOTHING) + lua_pushnil(L); + else + push_pointed_thing(L, pointed, csm, true); + + return 1; +} + +int LuaRaycast::create_object(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + bool objects = true; + bool liquids = false; + + v3f pos1 = checkFloatPos(L, 1); + v3f pos2 = checkFloatPos(L, 2); + if (lua_isboolean(L, 3)) { + objects = readParam(L, 3); + } + if (lua_isboolean(L, 4)) { + liquids = readParam(L, 4); + } + + LuaRaycast *o = new LuaRaycast(core::line3d(pos1, pos2), + objects, liquids); + + *(void **) (lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + +LuaRaycast *LuaRaycast::checkobject(lua_State *L, int narg) +{ + NO_MAP_LOCK_REQUIRED; + + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + return *(LuaRaycast **) ud; +} + +int LuaRaycast::gc_object(lua_State *L) +{ + LuaRaycast *o = *(LuaRaycast **) (lua_touserdata(L, 1)); + delete o; + return 0; +} + +void LuaRaycast::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pushliteral(L, "__call"); + lua_pushcfunction(L, l_next); + lua_settable(L, metatable); + + lua_pop(L, 1); + + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); + + lua_register(L, className, create_object); +} + +const char LuaRaycast::className[] = "Raycast"; +const luaL_Reg LuaRaycast::methods[] = +{ + luamethod(LuaRaycast, next), + { 0, 0 } +}; + +void LuaEmergeAreaCallback(v3s16 blockpos, EmergeAction action, void *param) +{ + ScriptCallbackState *state = (ScriptCallbackState *)param; + assert(state != NULL); + assert(state->script != NULL); + assert(state->refcount > 0); + + // state must be protected by envlock + Server *server = state->script->getServer(); + MutexAutoLock envlock(server->m_env_mutex); + + state->refcount--; + + state->script->on_emerge_area_completion(blockpos, action, state); + + if (state->refcount == 0) + delete state; +} + +// Exported functions + +// set_node(pos, node) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_set_node(lua_State *L) +{ + GET_ENV_PTR; + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + // parameters + v3s16 pos = read_v3s16(L, 1); + MapNode n = readnode(L, 2, ndef); + // Do it + bool succeeded = env->setNode(pos, n); + lua_pushboolean(L, succeeded); + return 1; +} + +// bulk_set_node([pos1, pos2, ...], node) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_bulk_set_node(lua_State *L) +{ + GET_ENV_PTR; + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + // parameters + if (!lua_istable(L, 1)) { + return 0; + } + + s32 len = lua_objlen(L, 1); + if (len == 0) { + lua_pushboolean(L, true); + return 1; + } + + MapNode n = readnode(L, 2, ndef); + + // Do it + bool succeeded = true; + for (s32 i = 1; i <= len; i++) { + lua_rawgeti(L, 1, i); + if (!env->setNode(read_v3s16(L, -1), n)) + succeeded = false; + lua_pop(L, 1); + } + + lua_pushboolean(L, succeeded); + return 1; +} + +int ModApiEnvMod::l_add_node(lua_State *L) +{ + return l_set_node(L); +} + +// remove_node(pos) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_remove_node(lua_State *L) +{ + GET_ENV_PTR; + + // parameters + v3s16 pos = read_v3s16(L, 1); + // Do it + bool succeeded = env->removeNode(pos); + lua_pushboolean(L, succeeded); + return 1; +} + +// swap_node(pos, node) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_swap_node(lua_State *L) +{ + GET_ENV_PTR; + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + // parameters + v3s16 pos = read_v3s16(L, 1); + MapNode n = readnode(L, 2, ndef); + // Do it + bool succeeded = env->swapNode(pos, n); + lua_pushboolean(L, succeeded); + return 1; +} + +// get_node(pos) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_get_node(lua_State *L) +{ + GET_ENV_PTR; + + // pos + v3s16 pos = read_v3s16(L, 1); + // Do it + MapNode n = env->getMap().getNode(pos); + // Return node + pushnode(L, n, env->getGameDef()->ndef()); + return 1; +} + +// get_node_or_nil(pos) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_get_node_or_nil(lua_State *L) +{ + GET_ENV_PTR; + + // pos + v3s16 pos = read_v3s16(L, 1); + // Do it + bool pos_ok; + MapNode n = env->getMap().getNode(pos, &pos_ok); + if (pos_ok) { + // Return node + pushnode(L, n, env->getGameDef()->ndef()); + } else { + lua_pushnil(L); + } + return 1; +} + +// get_node_light(pos, timeofday) +// pos = {x=num, y=num, z=num} +// timeofday: nil = current time, 0 = night, 0.5 = day +int ModApiEnvMod::l_get_node_light(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + // Do it + v3s16 pos = read_v3s16(L, 1); + u32 time_of_day = env->getTimeOfDay(); + if(lua_isnumber(L, 2)) + time_of_day = 24000.0 * lua_tonumber(L, 2); + time_of_day %= 24000; + u32 dnr = time_to_daynight_ratio(time_of_day, true); + + bool is_position_ok; + MapNode n = env->getMap().getNode(pos, &is_position_ok); + if (is_position_ok) { + const NodeDefManager *ndef = env->getGameDef()->ndef(); + lua_pushinteger(L, n.getLightBlend(dnr, ndef)); + } else { + lua_pushnil(L); + } + return 1; +} + + +// get_natural_light(pos, timeofday) +// pos = {x=num, y=num, z=num} +// timeofday: nil = current time, 0 = night, 0.5 = day +int ModApiEnvMod::l_get_natural_light(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 pos = read_v3s16(L, 1); + + bool is_position_ok; + MapNode n = env->getMap().getNode(pos, &is_position_ok); + if (!is_position_ok) + return 0; + + // If the daylight is 0, nothing needs to be calculated + u8 daylight = n.param1 & 0x0f; + if (daylight == 0) { + lua_pushinteger(L, 0); + return 1; + } + + u32 time_of_day; + if (lua_isnumber(L, 2)) { + time_of_day = 24000.0 * lua_tonumber(L, 2); + time_of_day %= 24000; + } else { + time_of_day = env->getTimeOfDay(); + } + u32 dnr = time_to_daynight_ratio(time_of_day, true); + + // If it's the same as the artificial light, the sunlight needs to be + // searched for because the value may not emanate from the sun + if (daylight == n.param1 >> 4) + daylight = env->findSunlight(pos); + + lua_pushinteger(L, dnr * daylight / 1000); + return 1; +} + +// place_node(pos, node) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_place_node(lua_State *L) +{ + GET_ENV_PTR; + + ScriptApiItem *scriptIfaceItem = getScriptApi(L); + Server *server = getServer(L); + const NodeDefManager *ndef = server->ndef(); + IItemDefManager *idef = server->idef(); + + v3s16 pos = read_v3s16(L, 1); + MapNode n = readnode(L, 2, ndef); + + // Don't attempt to load non-loaded area as of now + MapNode n_old = env->getMap().getNode(pos); + if(n_old.getContent() == CONTENT_IGNORE){ + lua_pushboolean(L, false); + return 1; + } + // Create item to place + ItemStack item(ndef->get(n).name, 1, 0, idef); + // Make pointed position + PointedThing pointed; + pointed.type = POINTEDTHING_NODE; + pointed.node_abovesurface = pos; + pointed.node_undersurface = pos + v3s16(0,-1,0); + // Place it with a NULL placer (appears in Lua as nil) + bool success = scriptIfaceItem->item_OnPlace(item, nullptr, pointed); + lua_pushboolean(L, success); + return 1; +} + +// dig_node(pos) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_dig_node(lua_State *L) +{ + GET_ENV_PTR; + + ScriptApiNode *scriptIfaceNode = getScriptApi(L); + + v3s16 pos = read_v3s16(L, 1); + + // Don't attempt to load non-loaded area as of now + MapNode n = env->getMap().getNode(pos); + if(n.getContent() == CONTENT_IGNORE){ + lua_pushboolean(L, false); + return 1; + } + // Dig it out with a NULL digger (appears in Lua as a + // non-functional ObjectRef) + bool success = scriptIfaceNode->node_on_dig(pos, n, NULL); + lua_pushboolean(L, success); + return 1; +} + +// punch_node(pos) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_punch_node(lua_State *L) +{ + GET_ENV_PTR; + + ScriptApiNode *scriptIfaceNode = getScriptApi(L); + + v3s16 pos = read_v3s16(L, 1); + + // Don't attempt to load non-loaded area as of now + MapNode n = env->getMap().getNode(pos); + if(n.getContent() == CONTENT_IGNORE){ + lua_pushboolean(L, false); + return 1; + } + // Punch it with a NULL puncher (appears in Lua as a non-functional + // ObjectRef) + bool success = scriptIfaceNode->node_on_punch(pos, n, NULL, PointedThing()); + lua_pushboolean(L, success); + return 1; +} + +// get_node_max_level(pos) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_get_node_max_level(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + v3s16 pos = read_v3s16(L, 1); + MapNode n = env->getMap().getNode(pos); + lua_pushnumber(L, n.getMaxLevel(env->getGameDef()->ndef())); + return 1; +} + +// get_node_level(pos) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_get_node_level(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + v3s16 pos = read_v3s16(L, 1); + MapNode n = env->getMap().getNode(pos); + lua_pushnumber(L, n.getLevel(env->getGameDef()->ndef())); + return 1; +} + +// set_node_level(pos, level) +// pos = {x=num, y=num, z=num} +// level: 0..63 +int ModApiEnvMod::l_set_node_level(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 pos = read_v3s16(L, 1); + u8 level = 1; + if(lua_isnumber(L, 2)) + level = lua_tonumber(L, 2); + MapNode n = env->getMap().getNode(pos); + lua_pushnumber(L, n.setLevel(env->getGameDef()->ndef(), level)); + env->setNode(pos, n); + return 1; +} + +// add_node_level(pos, level) +// pos = {x=num, y=num, z=num} +// level: -127..127 +int ModApiEnvMod::l_add_node_level(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 pos = read_v3s16(L, 1); + s16 level = 1; + if(lua_isnumber(L, 2)) + level = lua_tonumber(L, 2); + MapNode n = env->getMap().getNode(pos); + lua_pushnumber(L, n.addLevel(env->getGameDef()->ndef(), level)); + env->setNode(pos, n); + return 1; +} + +// find_nodes_with_meta(pos1, pos2) +int ModApiEnvMod::l_find_nodes_with_meta(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + std::vector positions = env->getMap().findNodesWithMetadata( + check_v3s16(L, 1), check_v3s16(L, 2)); + + lua_createtable(L, positions.size(), 0); + for (size_t i = 0; i != positions.size(); i++) { + push_v3s16(L, positions[i]); + lua_rawseti(L, -2, i + 1); + } + + return 1; +} + +// get_meta(pos) +int ModApiEnvMod::l_get_meta(lua_State *L) +{ + GET_ENV_PTR; + + // Do it + v3s16 p = read_v3s16(L, 1); + NodeMetaRef::create(L, p, env); + return 1; +} + +// get_node_timer(pos) +int ModApiEnvMod::l_get_node_timer(lua_State *L) +{ + GET_ENV_PTR; + + // Do it + v3s16 p = read_v3s16(L, 1); + NodeTimerRef::create(L, p, &env->getServerMap()); + return 1; +} + +// add_entity(pos, entityname, [staticdata]) -> ObjectRef or nil +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_add_entity(lua_State *L) +{ + GET_ENV_PTR; + + v3f pos = checkFloatPos(L, 1); + const char *name = luaL_checkstring(L, 2); + const char *staticdata = luaL_optstring(L, 3, ""); + + ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, staticdata); + int objectid = env->addActiveObject(obj); + // If failed to add, return nothing (reads as nil) + if(objectid == 0) + return 0; + + // If already deleted (can happen in on_activate), return nil + if (obj->isGone()) + return 0; + getScriptApiBase(L)->objectrefGetOrCreate(L, obj); + return 1; +} + +// add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_add_item(lua_State *L) +{ + GET_ENV_PTR; + + // pos + //v3f pos = checkFloatPos(L, 1); + // item + ItemStack item = read_item(L, 2,getServer(L)->idef()); + if(item.empty() || !item.isKnown(getServer(L)->idef())) + return 0; + + int error_handler = PUSH_ERROR_HANDLER(L); + + // Use spawn_item to spawn a __builtin:item + lua_getglobal(L, "core"); + lua_getfield(L, -1, "spawn_item"); + lua_remove(L, -2); // Remove core + if(lua_isnil(L, -1)) + return 0; + lua_pushvalue(L, 1); + lua_pushstring(L, item.getItemString().c_str()); + + PCALL_RESL(L, lua_pcall(L, 2, 1, error_handler)); + + lua_remove(L, error_handler); + return 1; +} + +// get_connected_players() +int ModApiEnvMod::l_get_connected_players(lua_State *L) +{ + ServerEnvironment *env = (ServerEnvironment *) getEnv(L); + if (!env) { + log_deprecated(L, "Calling get_connected_players() at mod load time" + " is deprecated"); + lua_createtable(L, 0, 0); + return 1; + } + + lua_createtable(L, env->getPlayerCount(), 0); + u32 i = 0; + for (RemotePlayer *player : env->getPlayers()) { + if (player->getPeerId() == PEER_ID_INEXISTENT) + continue; + PlayerSAO *sao = player->getPlayerSAO(); + if (sao && !sao->isGone()) { + getScriptApiBase(L)->objectrefGetOrCreate(L, sao); + lua_rawseti(L, -2, ++i); + } + } + return 1; +} + +// get_player_by_name(name) +int ModApiEnvMod::l_get_player_by_name(lua_State *L) +{ + GET_ENV_PTR; + + // Do it + const char *name = luaL_checkstring(L, 1); + RemotePlayer *player = env->getPlayer(name); + if (!player || player->getPeerId() == PEER_ID_INEXISTENT) + return 0; + PlayerSAO *sao = player->getPlayerSAO(); + if (!sao || sao->isGone()) + return 0; + // Put player on stack + getScriptApiBase(L)->objectrefGetOrCreate(L, sao); + return 1; +} + +// get_objects_inside_radius(pos, radius) +int ModApiEnvMod::l_get_objects_inside_radius(lua_State *L) +{ + GET_ENV_PTR; + ScriptApiBase *script = getScriptApiBase(L); + + // Do it + v3f pos = checkFloatPos(L, 1); + float radius = readParam(L, 2) * BS; + std::vector objs; + + auto include_obj_cb = [](ServerActiveObject *obj){ return !obj->isGone(); }; + env->getObjectsInsideRadius(objs, pos, radius, include_obj_cb); + + int i = 0; + lua_createtable(L, objs.size(), 0); + for (const auto obj : objs) { + // Insert object reference into table + script->objectrefGetOrCreate(L, obj); + lua_rawseti(L, -2, ++i); + } + return 1; +} + +// set_timeofday(val) +// val = 0...1 +int ModApiEnvMod::l_set_timeofday(lua_State *L) +{ + GET_ENV_PTR; + + // Do it + float timeofday_f = readParam(L, 1); + luaL_argcheck(L, timeofday_f >= 0.0f && timeofday_f <= 1.0f, 1, + "value must be between 0 and 1"); + int timeofday_mh = (int)(timeofday_f * 24000.0f); + // This should be set directly in the environment but currently + // such changes aren't immediately sent to the clients, so call + // the server instead. + //env->setTimeOfDay(timeofday_mh); + getServer(L)->setTimeOfDay(timeofday_mh); + return 0; +} + +// get_timeofday() -> 0...1 +int ModApiEnvMod::l_get_timeofday(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + // Do it + int timeofday_mh = env->getTimeOfDay(); + float timeofday_f = (float)timeofday_mh / 24000.0f; + lua_pushnumber(L, timeofday_f); + return 1; +} + +// get_day_count() -> int +int ModApiEnvMod::l_get_day_count(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + lua_pushnumber(L, env->getDayCount()); + return 1; +} + +// get_gametime() +int ModApiEnvMod::l_get_gametime(lua_State *L) +{ + GET_ENV_PTR; + + int game_time = env->getGameTime(); + lua_pushnumber(L, game_time); + return 1; +} + +void ModApiEnvMod::collectNodeIds(lua_State *L, int idx, const NodeDefManager *ndef, + std::vector &filter) +{ + if (lua_istable(L, idx)) { + lua_pushnil(L); + while (lua_next(L, idx) != 0) { + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TSTRING); + ndef->getIds(readParam(L, -1), filter); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } else if (lua_isstring(L, idx)) { + ndef->getIds(readParam(L, 3), filter); + } +} + +// find_node_near(pos, radius, nodenames, [search_center]) -> pos or nil +// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +int ModApiEnvMod::l_find_node_near(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + Map &map = env->getMap(); + + v3s16 pos = read_v3s16(L, 1); + int radius = luaL_checkinteger(L, 2); + std::vector filter; + collectNodeIds(L, 3, ndef, filter); + int start_radius = (lua_isboolean(L, 4) && readParam(L, 4)) ? 0 : 1; + +#ifndef SERVER + // Client API limitations + if (Client *client = getClient(L)) + radius = client->CSMClampRadius(pos, radius); +#endif + + for (int d = start_radius; d <= radius; d++) { + const std::vector &list = FacePositionCache::getFacePositions(d); + for (const v3s16 &i : list) { + v3s16 p = pos + i; + content_t c = map.getNode(p).getContent(); + if (CONTAINS(filter, c)) { + push_v3s16(L, p); + return 1; + } + } + } + return 0; +} + +// find_nodes_near(pos, radius, nodenames, [search_center]) +// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +int ModApiEnvMod::l_find_nodes_near(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + Map &map = env->getMap(); + + v3s16 pos = read_v3s16(L, 1); + int radius = luaL_checkinteger(L, 2); + std::vector filter; + collectNodeIds(L, 3, ndef, filter); + + int start_radius = (lua_isboolean(L, 4) && readParam(L, 4)) ? 0 : 1; + +#ifndef SERVER + // Client API limitations + if (Client *client = getClient(L)) + radius = client->CSMClampRadius(pos, radius); +#endif + + std::vector individual_count; + individual_count.resize(filter.size()); + + lua_newtable(L); + u32 i = 0; + + for (int d = start_radius; d <= radius; d++) { + const std::vector &list = FacePositionCache::getFacePositions(d); + for (const v3s16 &posi : list) { + v3s16 p = pos + posi; + content_t c = map.getNode(p).getContent(); + auto it = std::find(filter.begin(), filter.end(), c); + if (it != filter.end()) { + push_v3s16(L, p); + lua_rawseti(L, -2, ++i); + + u32 filt_index = it - filter.begin(); + individual_count[filt_index]++; + } + } + } + lua_createtable(L, 0, filter.size()); + for (u32 i = 0; i < filter.size(); i++) { + lua_pushinteger(L, individual_count[i]); + lua_setfield(L, -2, ndef->get(filter[i]).name.c_str()); + } + return 2; +} + +// find_nodes_near_under_air(pos, radius, nodenames, [search_center]) +// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +int ModApiEnvMod::l_find_nodes_near_under_air(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + Map &map = env->getMap(); + + v3s16 pos = read_v3s16(L, 1); + int radius = luaL_checkinteger(L, 2); + std::vector filter; + collectNodeIds(L, 3, ndef, filter); + int start_radius = (lua_isboolean(L, 4) && readParam(L, 4)) ? 0 : 1; + +#ifndef SERVER + // Client API limitations + if (Client *client = getClient(L)) + radius = client->CSMClampRadius(pos, radius); +#endif + + std::vector individual_count; + individual_count.resize(filter.size()); + + lua_newtable(L); + u32 i = 0; + + for (int d = start_radius; d <= radius; d++) { + const std::vector &list = FacePositionCache::getFacePositions(d); + for (const v3s16 &posi : list) { + v3s16 p = pos + posi; + content_t c = map.getNode(p).getContent(); + v3s16 psurf(p.X, p.Y + 1, p.Z); + content_t csurf = map.getNode(psurf).getContent(); + if (c == CONTENT_AIR || csurf != CONTENT_AIR) + continue; + auto it = std::find(filter.begin(), filter.end(), c); + if (it != filter.end()) { + push_v3s16(L, p); + lua_rawseti(L, -2, ++i); + + u32 filt_index = it - filter.begin(); + individual_count[filt_index]++; + } + } + } + lua_createtable(L, 0, filter.size()); + for (u32 i = 0; i < filter.size(); i++) { + lua_pushinteger(L, individual_count[i]); + lua_setfield(L, -2, ndef->get(filter[i]).name.c_str()); + } + return 2; +} + +// find_nodes_near_under_air_except(pos, radius, nodenames, [search_center]) +// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +int ModApiEnvMod::l_find_nodes_near_under_air_except(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + Map &map = env->getMap(); + + v3s16 pos = read_v3s16(L, 1); + int radius = luaL_checkinteger(L, 2); + std::vector filter; + collectNodeIds(L, 3, ndef, filter); + int start_radius = (lua_isboolean(L, 4) && readParam(L, 4)) ? 0 : 1; + +#ifndef SERVER + // Client API limitations + if (Client *client = getClient(L)) + radius = client->CSMClampRadius(pos, radius); +#endif + + std::vector individual_count; + individual_count.resize(filter.size()); + + lua_newtable(L); + u32 i = 0; + + for (int d = start_radius; d <= radius; d++) { + const std::vector &list = FacePositionCache::getFacePositions(d); + for (const v3s16 &posi : list) { + v3s16 p = pos + posi; + content_t c = map.getNode(p).getContent(); + v3s16 psurf(p.X, p.Y + 1, p.Z); + content_t csurf = map.getNode(psurf).getContent(); + if (c == CONTENT_AIR || csurf != CONTENT_AIR) + continue; + auto it = std::find(filter.begin(), filter.end(), c); + if (it == filter.end()) { + push_v3s16(L, p); + lua_rawseti(L, -2, ++i); + + u32 filt_index = it - filter.begin(); + individual_count[filt_index]++; + } + } + } + lua_createtable(L, 0, filter.size()); + for (u32 i = 0; i < filter.size(); i++) { + lua_pushinteger(L, individual_count[i]); + lua_setfield(L, -2, ndef->get(filter[i]).name.c_str()); + } + return 2; +} + +// find_nodes_in_area(minp, maxp, nodenames, [grouped]) +int ModApiEnvMod::l_find_nodes_in_area(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + v3s16 minp = read_v3s16(L, 1); + v3s16 maxp = read_v3s16(L, 2); + sortBoxVerticies(minp, maxp); + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + Map &map = env->getMap(); + +#ifndef SERVER + if (Client *client = getClient(L)) { + minp = client->CSMClampPos(minp); + maxp = client->CSMClampPos(maxp); + } +#endif + + v3s16 cube = maxp - minp + 1; + // Volume limit equal to 8 default mapchunks, (80 * 2) ^ 3 = 4,096,000 + if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 4096000) { + luaL_error(L, "find_nodes_in_area(): area volume" + " exceeds allowed value of 4096000"); + return 0; + } + + std::vector filter; + collectNodeIds(L, 3, ndef, filter); + + bool grouped = lua_isboolean(L, 4) && readParam(L, 4); + + if (grouped) { + // create the table we will be returning + lua_createtable(L, 0, filter.size()); + int base = lua_gettop(L); + + // create one table for each filter + std::vector idx; + idx.resize(filter.size()); + for (u32 i = 0; i < filter.size(); i++) + lua_newtable(L); + + v3s16 p; + for (p.X = minp.X; p.X <= maxp.X; p.X++) + for (p.Y = minp.Y; p.Y <= maxp.Y; p.Y++) + for (p.Z = minp.Z; p.Z <= maxp.Z; p.Z++) { + content_t c = map.getNode(p).getContent(); + + auto it = std::find(filter.begin(), filter.end(), c); + if (it != filter.end()) { + // Calculate index of the table and append the position + u32 filt_index = it - filter.begin(); + push_v3s16(L, p); + lua_rawseti(L, base + 1 + filt_index, ++idx[filt_index]); + } + } + + // last filter table is at top of stack + u32 i = filter.size() - 1; + do { + if (idx[i] == 0) { + // No such node found -> drop the empty table + lua_pop(L, 1); + } else { + // This node was found -> put table into the return table + lua_setfield(L, base, ndef->get(filter[i]).name.c_str()); + } + } while (i-- != 0); + + assert(lua_gettop(L) == base); + return 1; + } else { + std::vector individual_count; + individual_count.resize(filter.size()); + + lua_newtable(L); + u32 i = 0; + v3s16 p; + for (p.X = minp.X; p.X <= maxp.X; p.X++) + for (p.Y = minp.Y; p.Y <= maxp.Y; p.Y++) + for (p.Z = minp.Z; p.Z <= maxp.Z; p.Z++) { + content_t c = env->getMap().getNode(p).getContent(); + + auto it = std::find(filter.begin(), filter.end(), c); + if (it != filter.end()) { + push_v3s16(L, p); + lua_rawseti(L, -2, ++i); + + u32 filt_index = it - filter.begin(); + individual_count[filt_index]++; + } + } + + lua_createtable(L, 0, filter.size()); + for (u32 i = 0; i < filter.size(); i++) { + lua_pushinteger(L, individual_count[i]); + lua_setfield(L, -2, ndef->get(filter[i]).name.c_str()); + } + return 2; + } +} + +// find_nodes_in_area_under_air(minp, maxp, nodenames) -> list of positions +// nodenames: e.g. {"ignore", "group:tree"} or "default:dirt" +int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L) +{ + /* Note: A similar but generalized (and therefore slower) version of this + * function could be created -- e.g. find_nodes_in_area_under -- which + * would accept a node name (or ID?) or list of names that the "above node" + * should be. + * TODO + */ + + GET_PLAIN_ENV_PTR; + + v3s16 minp = read_v3s16(L, 1); + v3s16 maxp = read_v3s16(L, 2); + sortBoxVerticies(minp, maxp); + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + Map &map = env->getMap(); + +#ifndef SERVER + if (Client *client = getClient(L)) { + minp = client->CSMClampPos(minp); + maxp = client->CSMClampPos(maxp); + } +#endif + + v3s16 cube = maxp - minp + 1; + // Volume limit equal to 8 default mapchunks, (80 * 2) ^ 3 = 4,096,000 + if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 4096000) { + luaL_error(L, "find_nodes_in_area_under_air(): area volume" + " exceeds allowed value of 4096000"); + return 0; + } + + std::vector filter; + collectNodeIds(L, 3, ndef, filter); + + lua_newtable(L); + u32 i = 0; + v3s16 p; + for (p.X = minp.X; p.X <= maxp.X; p.X++) + for (p.Z = minp.Z; p.Z <= maxp.Z; p.Z++) { + p.Y = minp.Y; + content_t c = map.getNode(p).getContent(); + for (; p.Y <= maxp.Y; p.Y++) { + v3s16 psurf(p.X, p.Y + 1, p.Z); + content_t csurf = map.getNode(psurf).getContent(); + if (c != CONTENT_AIR && csurf == CONTENT_AIR && + CONTAINS(filter, c)) { + push_v3s16(L, p); + lua_rawseti(L, -2, ++i); + } + c = csurf; + } + } + return 1; +} + +// get_perlin(seeddiff, octaves, persistence, scale) +// returns world-specific PerlinNoise +int ModApiEnvMod::l_get_perlin(lua_State *L) +{ + GET_ENV_PTR_NO_MAP_LOCK; + + NoiseParams params; + + if (lua_istable(L, 1)) { + read_noiseparams(L, 1, ¶ms); + } else { + params.seed = luaL_checkint(L, 1); + params.octaves = luaL_checkint(L, 2); + params.persist = readParam(L, 3); + params.spread = v3f(1, 1, 1) * readParam(L, 4); + } + + params.seed += (int)env->getServerMap().getSeed(); + + LuaPerlinNoise *n = new LuaPerlinNoise(¶ms); + *(void **)(lua_newuserdata(L, sizeof(void *))) = n; + luaL_getmetatable(L, "PerlinNoise"); + lua_setmetatable(L, -2); + return 1; +} + +// get_perlin_map(noiseparams, size) +// returns world-specific PerlinNoiseMap +int ModApiEnvMod::l_get_perlin_map(lua_State *L) +{ + GET_ENV_PTR_NO_MAP_LOCK; + + NoiseParams np; + if (!read_noiseparams(L, 1, &np)) + return 0; + v3s16 size = read_v3s16(L, 2); + + s32 seed = (s32)(env->getServerMap().getSeed()); + LuaPerlinNoiseMap *n = new LuaPerlinNoiseMap(&np, seed, size); + *(void **)(lua_newuserdata(L, sizeof(void *))) = n; + luaL_getmetatable(L, "PerlinNoiseMap"); + lua_setmetatable(L, -2); + return 1; +} + +// get_voxel_manip() +// returns voxel manipulator +int ModApiEnvMod::l_get_voxel_manip(lua_State *L) +{ + GET_ENV_PTR; + + Map *map = &(env->getMap()); + LuaVoxelManip *o = (lua_istable(L, 1) && lua_istable(L, 2)) ? + new LuaVoxelManip(map, read_v3s16(L, 1), read_v3s16(L, 2)) : + new LuaVoxelManip(map); + + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, "VoxelManip"); + lua_setmetatable(L, -2); + return 1; +} + +// clear_objects([options]) +// clear all objects in the environment +// where options = {mode = "full" or "quick"} +int ModApiEnvMod::l_clear_objects(lua_State *L) +{ + GET_ENV_PTR; + + ClearObjectsMode mode = CLEAR_OBJECTS_MODE_QUICK; + if (lua_istable(L, 1)) { + mode = (ClearObjectsMode)getenumfield(L, 1, "mode", + ModApiEnvMod::es_ClearObjectsMode, mode); + } + + env->clearObjects(mode); + return 0; +} + +// line_of_sight(pos1, pos2) -> true/false, pos +int ModApiEnvMod::l_line_of_sight(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + // read position 1 from lua + v3f pos1 = checkFloatPos(L, 1); + // read position 2 from lua + v3f pos2 = checkFloatPos(L, 2); + + v3s16 p; + + bool success = env->line_of_sight(pos1, pos2, &p); + lua_pushboolean(L, success); + if (!success) { + push_v3s16(L, p); + return 2; + } + return 1; +} + +// fix_light(p1, p2) +int ModApiEnvMod::l_fix_light(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 blockpos1 = getContainerPos(read_v3s16(L, 1), MAP_BLOCKSIZE); + v3s16 blockpos2 = getContainerPos(read_v3s16(L, 2), MAP_BLOCKSIZE); + ServerMap &map = env->getServerMap(); + std::map modified_blocks; + bool success = true; + v3s16 blockpos; + for (blockpos.X = blockpos1.X; blockpos.X <= blockpos2.X; blockpos.X++) + for (blockpos.Y = blockpos1.Y; blockpos.Y <= blockpos2.Y; blockpos.Y++) + for (blockpos.Z = blockpos1.Z; blockpos.Z <= blockpos2.Z; blockpos.Z++) { + success = success & map.repairBlockLight(blockpos, &modified_blocks); + } + if (!modified_blocks.empty()) { + MapEditEvent event; + event.type = MEET_OTHER; + for (auto &modified_block : modified_blocks) + event.modified_blocks.insert(modified_block.first); + + map.dispatchEvent(event); + } + lua_pushboolean(L, success); + + return 1; +} + +int ModApiEnvMod::l_raycast(lua_State *L) +{ + return LuaRaycast::create_object(L); +} + +// load_area(p1, [p2]) +// load mapblocks in area p1..p2, but do not generate map +int ModApiEnvMod::l_load_area(lua_State *L) +{ + GET_ENV_PTR; + MAP_LOCK_REQUIRED; + + Map *map = &(env->getMap()); + v3s16 bp1 = getNodeBlockPos(check_v3s16(L, 1)); + if (!lua_istable(L, 2)) { + map->emergeBlock(bp1); + } else { + v3s16 bp2 = getNodeBlockPos(check_v3s16(L, 2)); + sortBoxVerticies(bp1, bp2); + for (s16 z = bp1.Z; z <= bp2.Z; z++) + for (s16 y = bp1.Y; y <= bp2.Y; y++) + for (s16 x = bp1.X; x <= bp2.X; x++) { + map->emergeBlock(v3s16(x, y, z)); + } + } + + return 0; +} + +// emerge_area(p1, p2, [callback, context]) +// emerge mapblocks in area p1..p2, calls callback with context upon completion +int ModApiEnvMod::l_emerge_area(lua_State *L) +{ + GET_ENV_PTR; + + EmergeCompletionCallback callback = NULL; + ScriptCallbackState *state = NULL; + + EmergeManager *emerge = getServer(L)->getEmergeManager(); + + v3s16 bpmin = getNodeBlockPos(read_v3s16(L, 1)); + v3s16 bpmax = getNodeBlockPos(read_v3s16(L, 2)); + sortBoxVerticies(bpmin, bpmax); + + size_t num_blocks = VoxelArea(bpmin, bpmax).getVolume(); + assert(num_blocks != 0); + + if (lua_isfunction(L, 3)) { + callback = LuaEmergeAreaCallback; + + lua_pushvalue(L, 3); + int callback_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + lua_pushvalue(L, 4); + int args_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + state = new ScriptCallbackState; + state->script = getServer(L)->getScriptIface(); + state->callback_ref = callback_ref; + state->args_ref = args_ref; + state->refcount = num_blocks; + state->origin = getScriptApiBase(L)->getOrigin(); + } + + for (s16 z = bpmin.Z; z <= bpmax.Z; z++) + for (s16 y = bpmin.Y; y <= bpmax.Y; y++) + for (s16 x = bpmin.X; x <= bpmax.X; x++) { + emerge->enqueueBlockEmergeEx(v3s16(x, y, z), PEER_ID_INEXISTENT, + BLOCK_EMERGE_ALLOW_GEN | BLOCK_EMERGE_FORCE_QUEUE, callback, state); + } + + return 0; +} + +// delete_area(p1, p2) +// delete mapblocks in area p1..p2 +int ModApiEnvMod::l_delete_area(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 bpmin = getNodeBlockPos(read_v3s16(L, 1)); + v3s16 bpmax = getNodeBlockPos(read_v3s16(L, 2)); + sortBoxVerticies(bpmin, bpmax); + + ServerMap &map = env->getServerMap(); + + MapEditEvent event; + event.type = MEET_OTHER; + + bool success = true; + for (s16 z = bpmin.Z; z <= bpmax.Z; z++) + for (s16 y = bpmin.Y; y <= bpmax.Y; y++) + for (s16 x = bpmin.X; x <= bpmax.X; x++) { + v3s16 bp(x, y, z); + if (map.deleteBlock(bp)) { + env->setStaticForActiveObjectsInBlock(bp, false); + event.modified_blocks.insert(bp); + } else { + success = false; + } + } + + map.dispatchEvent(event); + lua_pushboolean(L, success); + return 1; +} + +// find_path(pos1, pos2, searchdistance, +// max_jump, max_drop, algorithm) -> table containing path +int ModApiEnvMod::l_find_path(lua_State *L) +{ + Environment *env = getEnv(L); + + v3s16 pos1 = read_v3s16(L, 1); + v3s16 pos2 = read_v3s16(L, 2); + unsigned int searchdistance = luaL_checkint(L, 3); + unsigned int max_jump = luaL_checkint(L, 4); + unsigned int max_drop = luaL_checkint(L, 5); + PathAlgorithm algo = PA_PLAIN_NP; + if (!lua_isnoneornil(L, 6)) { + std::string algorithm = luaL_checkstring(L,6); + + if (algorithm == "A*") + algo = PA_PLAIN; + + if (algorithm == "Dijkstra") + algo = PA_DIJKSTRA; + } + + std::vector path = get_path(&env->getMap(), env->getGameDef()->ndef(), pos1, pos2, + searchdistance, max_jump, max_drop, algo); + + if (!path.empty()) { + lua_createtable(L, path.size(), 0); + int top = lua_gettop(L); + unsigned int index = 1; + for (const v3s16 &i : path) { + lua_pushnumber(L,index); + push_v3s16(L, i); + lua_settable(L, top); + index++; + } + return 1; + } + + return 0; +} + +// spawn_tree(pos, treedef) +int ModApiEnvMod::l_spawn_tree(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 p0 = read_v3s16(L, 1); + + treegen::TreeDef tree_def; + std::string trunk,leaves,fruit; + const NodeDefManager *ndef = env->getGameDef()->ndef(); + + if(lua_istable(L, 2)) + { + getstringfield(L, 2, "axiom", tree_def.initial_axiom); + getstringfield(L, 2, "rules_a", tree_def.rules_a); + getstringfield(L, 2, "rules_b", tree_def.rules_b); + getstringfield(L, 2, "rules_c", tree_def.rules_c); + getstringfield(L, 2, "rules_d", tree_def.rules_d); + getstringfield(L, 2, "trunk", trunk); + tree_def.trunknode=ndef->getId(trunk); + getstringfield(L, 2, "leaves", leaves); + tree_def.leavesnode=ndef->getId(leaves); + tree_def.leaves2_chance=0; + getstringfield(L, 2, "leaves2", leaves); + if (!leaves.empty()) { + tree_def.leaves2node=ndef->getId(leaves); + getintfield(L, 2, "leaves2_chance", tree_def.leaves2_chance); + } + getintfield(L, 2, "angle", tree_def.angle); + getintfield(L, 2, "iterations", tree_def.iterations); + if (!getintfield(L, 2, "random_level", tree_def.iterations_random_level)) + tree_def.iterations_random_level = 0; + getstringfield(L, 2, "trunk_type", tree_def.trunk_type); + getboolfield(L, 2, "thin_branches", tree_def.thin_branches); + tree_def.fruit_chance=0; + getstringfield(L, 2, "fruit", fruit); + if (!fruit.empty()) { + tree_def.fruitnode=ndef->getId(fruit); + getintfield(L, 2, "fruit_chance",tree_def.fruit_chance); + } + tree_def.explicit_seed = getintfield(L, 2, "seed", tree_def.seed); + } + else + return 0; + + ServerMap *map = &env->getServerMap(); + treegen::error e; + if ((e = treegen::spawn_ltree (map, p0, ndef, tree_def)) != treegen::SUCCESS) { + if (e == treegen::UNBALANCED_BRACKETS) { + luaL_error(L, "spawn_tree(): closing ']' has no matching opening bracket"); + } else { + luaL_error(L, "spawn_tree(): unknown error"); + } + } + + return 1; +} + +// transforming_liquid_add(pos) +int ModApiEnvMod::l_transforming_liquid_add(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 p0 = read_v3s16(L, 1); + env->getMap().transforming_liquid_add(p0); + return 1; +} + +// forceload_block(blockpos) +// blockpos = {x=num, y=num, z=num} +int ModApiEnvMod::l_forceload_block(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 blockpos = read_v3s16(L, 1); + env->getForceloadedBlocks()->insert(blockpos); + return 0; +} + +// forceload_free_block(blockpos) +// blockpos = {x=num, y=num, z=num} +int ModApiEnvMod::l_forceload_free_block(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 blockpos = read_v3s16(L, 1); + env->getForceloadedBlocks()->erase(blockpos); + return 0; +} + +// get_translated_string(lang_code, string) +int ModApiEnvMod::l_get_translated_string(lua_State * L) +{ + GET_ENV_PTR; + std::string lang_code = luaL_checkstring(L, 1); + std::string string = luaL_checkstring(L, 2); + + auto *translations = getServer(L)->getTranslationLanguage(lang_code); + string = wide_to_utf8(translate_string(utf8_to_wide(string), translations)); + lua_pushstring(L, string.c_str()); + return 1; +} + +void ModApiEnvMod::Initialize(lua_State *L, int top) +{ + API_FCT(set_node); + API_FCT(bulk_set_node); + API_FCT(add_node); + API_FCT(swap_node); + API_FCT(add_item); + API_FCT(remove_node); + API_FCT(get_node); + API_FCT(get_node_or_nil); + API_FCT(get_node_light); + API_FCT(get_natural_light); + API_FCT(place_node); + API_FCT(dig_node); + API_FCT(punch_node); + API_FCT(get_node_max_level); + API_FCT(get_node_level); + API_FCT(set_node_level); + API_FCT(add_node_level); + API_FCT(add_entity); + API_FCT(find_nodes_with_meta); + API_FCT(get_meta); + API_FCT(get_node_timer); + API_FCT(get_connected_players); + API_FCT(get_player_by_name); + API_FCT(get_objects_inside_radius); + API_FCT(set_timeofday); + API_FCT(get_timeofday); + API_FCT(get_gametime); + API_FCT(get_day_count); + API_FCT(find_node_near); + API_FCT(find_nodes_in_area); + API_FCT(find_nodes_in_area_under_air); + API_FCT(fix_light); + API_FCT(load_area); + API_FCT(emerge_area); + API_FCT(delete_area); + API_FCT(get_perlin); + API_FCT(get_perlin_map); + API_FCT(get_voxel_manip); + API_FCT(clear_objects); + API_FCT(spawn_tree); + API_FCT(find_path); + API_FCT(line_of_sight); + API_FCT(raycast); + API_FCT(transforming_liquid_add); + API_FCT(forceload_block); + API_FCT(forceload_free_block); + API_FCT(get_translated_string); +} + +void ModApiEnvMod::InitializeClient(lua_State *L, int top) +{ + API_FCT(get_node_light); + API_FCT(get_timeofday); + API_FCT(get_node_max_level); + API_FCT(get_node_level); + API_FCT(find_nodes_with_meta); + API_FCT(find_node_near); + API_FCT(find_nodes_near); + API_FCT(find_nodes_near_under_air); + API_FCT(find_nodes_near_under_air_except); + API_FCT(find_nodes_in_area); + API_FCT(find_nodes_in_area_under_air); + API_FCT(find_path); + API_FCT(line_of_sight); + API_FCT(raycast); +} diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h new file mode 100644 index 000000000..5cd40ba6b --- /dev/null +++ b/src/script/lua_api/l_env.h @@ -0,0 +1,333 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" +#include "serverenvironment.h" +#include "raycast.h" + +class ModApiEnvMod : public ModApiBase { +private: + // set_node(pos, node) + // pos = {x=num, y=num, z=num} + static int l_set_node(lua_State *L); + + // bulk_set_node([pos1, pos2, ...], node) + // pos = {x=num, y=num, z=num} + static int l_bulk_set_node(lua_State *L); + + static int l_add_node(lua_State *L); + + // remove_node(pos) + // pos = {x=num, y=num, z=num} + static int l_remove_node(lua_State *L); + + // swap_node(pos, node) + // pos = {x=num, y=num, z=num} + static int l_swap_node(lua_State *L); + + // get_node(pos) + // pos = {x=num, y=num, z=num} + static int l_get_node(lua_State *L); + + // get_node_or_nil(pos) + // pos = {x=num, y=num, z=num} + static int l_get_node_or_nil(lua_State *L); + + // get_node_light(pos, timeofday) + // pos = {x=num, y=num, z=num} + // timeofday: nil = current time, 0 = night, 0.5 = day + static int l_get_node_light(lua_State *L); + + // get_natural_light(pos, timeofday) + // pos = {x=num, y=num, z=num} + // timeofday: nil = current time, 0 = night, 0.5 = day + static int l_get_natural_light(lua_State *L); + + // place_node(pos, node) + // pos = {x=num, y=num, z=num} + static int l_place_node(lua_State *L); + + // dig_node(pos) + // pos = {x=num, y=num, z=num} + static int l_dig_node(lua_State *L); + + // punch_node(pos) + // pos = {x=num, y=num, z=num} + static int l_punch_node(lua_State *L); + + // get_node_max_level(pos) + // pos = {x=num, y=num, z=num} + static int l_get_node_max_level(lua_State *L); + + // get_node_level(pos) + // pos = {x=num, y=num, z=num} + static int l_get_node_level(lua_State *L); + + // set_node_level(pos) + // pos = {x=num, y=num, z=num} + static int l_set_node_level(lua_State *L); + + // add_node_level(pos) + // pos = {x=num, y=num, z=num} + static int l_add_node_level(lua_State *L); + + // find_nodes_with_meta(pos1, pos2) + static int l_find_nodes_with_meta(lua_State *L); + + // get_meta(pos) + static int l_get_meta(lua_State *L); + + // get_node_timer(pos) + static int l_get_node_timer(lua_State *L); + + // add_entity(pos, entityname) -> ObjectRef or nil + // pos = {x=num, y=num, z=num} + static int l_add_entity(lua_State *L); + + // add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil + // pos = {x=num, y=num, z=num} + static int l_add_item(lua_State *L); + + // get_connected_players() + static int l_get_connected_players(lua_State *L); + + // get_player_by_name(name) + static int l_get_player_by_name(lua_State *L); + + // get_objects_inside_radius(pos, radius) + static int l_get_objects_inside_radius(lua_State *L); + + // set_timeofday(val) + // val = 0...1 + static int l_set_timeofday(lua_State *L); + + // get_timeofday() -> 0...1 + static int l_get_timeofday(lua_State *L); + + // get_gametime() + static int l_get_gametime(lua_State *L); + + // get_day_count() -> int + static int l_get_day_count(lua_State *L); + + // find_node_near(pos, radius, nodenames, search_center) -> pos or nil + // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" + static int l_find_node_near(lua_State *L); + + // find_nodes_near(pos, radius, nodenames, search_center) -> list of positions + // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" + static int l_find_nodes_near(lua_State *L); + + // find_nodes_near_under_air(pos, radius, nodenames, search_center) -> list of positions + // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" + static int l_find_nodes_near_under_air(lua_State *L); + + // find_nodes_near_under_air(pos, radius, nodenames, search_center) -> list of positions + // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" + static int l_find_nodes_near_under_air_except(lua_State *L); + + // find_nodes_in_area(minp, maxp, nodenames) -> list of positions + // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" + static int l_find_nodes_in_area(lua_State *L); + + // find_surface_nodes_in_area(minp, maxp, nodenames) -> list of positions + // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" + static int l_find_nodes_in_area_under_air(lua_State *L); + + // fix_light(p1, p2) -> true/false + static int l_fix_light(lua_State *L); + + // load_area(p1) + static int l_load_area(lua_State *L); + + // emerge_area(p1, p2) + static int l_emerge_area(lua_State *L); + + // delete_area(p1, p2) -> true/false + static int l_delete_area(lua_State *L); + + // get_perlin(seeddiff, octaves, persistence, scale) + // returns world-specific PerlinNoise + static int l_get_perlin(lua_State *L); + + // get_perlin_map(noiseparams, size) + // returns world-specific PerlinNoiseMap + static int l_get_perlin_map(lua_State *L); + + // get_voxel_manip() + // returns world-specific voxel manipulator + static int l_get_voxel_manip(lua_State *L); + + // clear_objects() + // clear all objects in the environment + static int l_clear_objects(lua_State *L); + + // spawn_tree(pos, treedef) + static int l_spawn_tree(lua_State *L); + + // line_of_sight(pos1, pos2) -> true/false + static int l_line_of_sight(lua_State *L); + + // raycast(pos1, pos2, objects, liquids) -> Raycast + static int l_raycast(lua_State *L); + + // find_path(pos1, pos2, searchdistance, + // max_jump, max_drop, algorithm) -> table containing path + static int l_find_path(lua_State *L); + + // transforming_liquid_add(pos) + static int l_transforming_liquid_add(lua_State *L); + + // forceload_block(blockpos) + // forceloads a block + static int l_forceload_block(lua_State *L); + + // forceload_free_block(blockpos) + // stops forceloading a position + static int l_forceload_free_block(lua_State *L); + + // Get a string translated server side + static int l_get_translated_string(lua_State * L); + + /* Helpers */ + + static void collectNodeIds(lua_State *L, int idx, + const NodeDefManager *ndef, std::vector &filter); + +public: + static void Initialize(lua_State *L, int top); + static void InitializeClient(lua_State *L, int top); + + static struct EnumString es_ClearObjectsMode[]; +}; + +class LuaABM : public ActiveBlockModifier { +private: + int m_id; + + std::vector m_trigger_contents; + std::vector m_required_neighbors; + float m_trigger_interval; + u32 m_trigger_chance; + bool m_simple_catch_up; +public: + LuaABM(lua_State *L, int id, + const std::vector &trigger_contents, + const std::vector &required_neighbors, + float trigger_interval, u32 trigger_chance, bool simple_catch_up): + m_id(id), + m_trigger_contents(trigger_contents), + m_required_neighbors(required_neighbors), + m_trigger_interval(trigger_interval), + m_trigger_chance(trigger_chance), + m_simple_catch_up(simple_catch_up) + { + } + virtual const std::vector &getTriggerContents() const + { + return m_trigger_contents; + } + virtual const std::vector &getRequiredNeighbors() const + { + return m_required_neighbors; + } + virtual float getTriggerInterval() + { + return m_trigger_interval; + } + virtual u32 getTriggerChance() + { + return m_trigger_chance; + } + virtual bool getSimpleCatchUp() + { + return m_simple_catch_up; + } + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n, + u32 active_object_count, u32 active_object_count_wider); +}; + +class LuaLBM : public LoadingBlockModifierDef +{ +private: + int m_id; +public: + LuaLBM(lua_State *L, int id, + const std::set &trigger_contents, + const std::string &name, + bool run_at_every_load): + m_id(id) + { + this->run_at_every_load = run_at_every_load; + this->trigger_contents = trigger_contents; + this->name = name; + } + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n); +}; + +//! Lua wrapper for RaycastState objects +class LuaRaycast : public ModApiBase +{ +private: + static const char className[]; + static const luaL_Reg methods[]; + //! Inner state + RaycastState state; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + /*! + * Raycast:next() -> pointed_thing + * Returns the next pointed thing on the ray. + */ + static int l_next(lua_State *L); +public: + //! Constructor with the same arguments as RaycastState. + LuaRaycast( + const core::line3d &shootline, + bool objects_pointable, + bool liquids_pointable) : + state(shootline, objects_pointable, liquids_pointable) + {} + + //! Creates a LuaRaycast and leaves it on top of the stack. + static int create_object(lua_State *L); + + /*! + * Returns the Raycast from the stack or throws an error. + * @param narg location of the RaycastState in the stack + */ + static LuaRaycast *checkobject(lua_State *L, int narg); + + //! Registers Raycast as a Lua userdata type. + static void Register(lua_State *L); +}; + +struct ScriptCallbackState { + ServerScripting *script; + int callback_ref; + int args_ref; + unsigned int refcount; + std::string origin; +}; diff --git a/src/script/lua_api/l_http.cpp b/src/script/lua_api/l_http.cpp new file mode 100644 index 000000000..d09867d8a --- /dev/null +++ b/src/script/lua_api/l_http.cpp @@ -0,0 +1,251 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_internal.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "lua_api/l_http.h" +#include "httpfetch.h" +#include "settings.h" +#include "debug.h" +#include "log.h" + +#include +#include +#include + +#define HTTP_API(name) \ + lua_pushstring(L, #name); \ + lua_pushcfunction(L, l_http_##name); \ + lua_settable(L, -3); + +#if USE_CURL +void ModApiHttp::read_http_fetch_request(lua_State *L, HTTPFetchRequest &req) +{ + luaL_checktype(L, 1, LUA_TTABLE); + + req.caller = httpfetch_caller_alloc_secure(); + getstringfield(L, 1, "url", req.url); + lua_getfield(L, 1, "user_agent"); + if (lua_isstring(L, -1)) + req.useragent = getstringfield_default(L, 1, "user_agent", ""); + lua_pop(L, 1); + req.multipart = getboolfield_default(L, 1, "multipart", false); + req.timeout = getintfield_default(L, 1, "timeout", 3) * 1000; + + lua_getfield(L, 1, "method"); + if (lua_isstring(L, -1)) { + std::string mth = getstringfield_default(L, 1, "method", ""); + if (mth == "GET") + req.method = HTTP_GET; + else if (mth == "POST") + req.method = HTTP_POST; + else if (mth == "PUT") + req.method = HTTP_PUT; + else if (mth == "DELETE") + req.method = HTTP_DELETE; + } + lua_pop(L, 1); + + // post_data: if table, post form data, otherwise raw data DEPRECATED use data and method instead + lua_getfield(L, 1, "post_data"); + if (lua_isnil(L, 2)) { + lua_pop(L, 1); + lua_getfield(L, 1, "data"); + } + else { + req.method = HTTP_POST; + } + + if (lua_istable(L, 2)) { + lua_pushnil(L); + while (lua_next(L, 2) != 0) { + req.fields[readParam(L, -2)] = readParam(L, -1); + lua_pop(L, 1); + } + } else if (lua_isstring(L, 2)) { + req.raw_data = readParam(L, 2); + } + + lua_pop(L, 1); + + lua_getfield(L, 1, "extra_headers"); + if (lua_istable(L, 2)) { + lua_pushnil(L); + while (lua_next(L, 2) != 0) { + req.extra_headers.emplace_back(readParam(L, -1)); + lua_pop(L, 1); + } + } + lua_pop(L, 1); +} + +void ModApiHttp::push_http_fetch_result(lua_State *L, HTTPFetchResult &res, bool completed) +{ + lua_newtable(L); + setboolfield(L, -1, "succeeded", res.succeeded); + setboolfield(L, -1, "timeout", res.timeout); + setboolfield(L, -1, "completed", completed); + setintfield(L, -1, "code", res.response_code); + setstringfield(L, -1, "data", res.data); +} + +// http_api.fetch_sync(HTTPRequest definition) +int ModApiHttp::l_http_fetch_sync(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + HTTPFetchRequest req; + read_http_fetch_request(L, req); + + infostream << "Mod performs HTTP request with URL " << req.url << std::endl; + + HTTPFetchResult res; + httpfetch_sync(req, res); + + push_http_fetch_result(L, res, true); + + return 1; +} + +// http_api.fetch_async(HTTPRequest definition) +int ModApiHttp::l_http_fetch_async(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + HTTPFetchRequest req; + read_http_fetch_request(L, req); + + infostream << "Mod performs HTTP request with URL " << req.url << std::endl; + httpfetch_async(req); + + // Convert handle to hex string since lua can't handle 64-bit integers + std::stringstream handle_conversion_stream; + handle_conversion_stream << std::hex << req.caller; + std::string caller_handle(handle_conversion_stream.str()); + + lua_pushstring(L, caller_handle.c_str()); + return 1; +} + +// http_api.fetch_async_get(handle) +int ModApiHttp::l_http_fetch_async_get(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + std::string handle_str = luaL_checkstring(L, 1); + + // Convert hex string back to 64-bit handle + u64 handle; + std::stringstream handle_conversion_stream; + handle_conversion_stream << std::hex << handle_str; + handle_conversion_stream >> handle; + + HTTPFetchResult res; + bool completed = httpfetch_async_get(handle, res); + + push_http_fetch_result(L, res, completed); + + return 1; +} + +int ModApiHttp::l_request_http_api(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + // We have to make sure that this function is being called directly by + // a mod, otherwise a malicious mod could override this function and + // steal its return value. + lua_Debug info; + + // Make sure there's only one item below this function on the stack... + if (lua_getstack(L, 2, &info)) { + return 0; + } + FATAL_ERROR_IF(!lua_getstack(L, 1, &info), "lua_getstack() failed"); + FATAL_ERROR_IF(!lua_getinfo(L, "S", &info), "lua_getinfo() failed"); + + // ...and that that item is the main file scope. + if (strcmp(info.what, "main") != 0) { + return 0; + } + + // Mod must be listed in secure.http_mods or secure.trusted_mods + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); + if (!lua_isstring(L, -1)) { + return 0; + } + + std::string mod_name = readParam(L, -1); + std::string http_mods = g_settings->get("secure.http_mods"); + http_mods.erase(std::remove(http_mods.begin(), http_mods.end(), ' '), http_mods.end()); + std::vector mod_list_http = str_split(http_mods, ','); + + std::string trusted_mods = g_settings->get("secure.trusted_mods"); + trusted_mods.erase(std::remove(trusted_mods.begin(), trusted_mods.end(), ' '), trusted_mods.end()); + std::vector mod_list_trusted = str_split(trusted_mods, ','); + + mod_list_http.insert(mod_list_http.end(), mod_list_trusted.begin(), mod_list_trusted.end()); + if (std::find(mod_list_http.begin(), mod_list_http.end(), mod_name) == mod_list_http.end()) { + lua_pushnil(L); + return 1; + } + + lua_getglobal(L, "core"); + lua_getfield(L, -1, "http_add_fetch"); + + lua_newtable(L); + HTTP_API(fetch_async); + HTTP_API(fetch_async_get); + + // Stack now looks like this: + // + // Now call core.http_add_fetch to append .fetch(request, callback) to table + lua_call(L, 1, 1); + + return 1; +} + +int ModApiHttp::l_get_http_api(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + lua_newtable(L); + HTTP_API(fetch_async); + HTTP_API(fetch_async_get); + HTTP_API(fetch_sync); + + return 1; +} + +#endif + +void ModApiHttp::Initialize(lua_State *L, int top) +{ +#if USE_CURL + API_FCT(get_http_api); +#endif +} + +void ModApiHttp::InitializeAsync(lua_State *L, int top) +{ +#if USE_CURL + API_FCT(get_http_api); +#endif +} diff --git a/src/script/lua_api/l_http.h b/src/script/lua_api/l_http.h new file mode 100644 index 000000000..d0266058c --- /dev/null +++ b/src/script/lua_api/l_http.h @@ -0,0 +1,54 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" +#include "config.h" + +struct HTTPFetchRequest; +struct HTTPFetchResult; + +class ModApiHttp : public ModApiBase { +private: +#if USE_CURL + // Helpers for HTTP fetch functions + static void read_http_fetch_request(lua_State *L, HTTPFetchRequest &req); + static void push_http_fetch_result(lua_State *L, HTTPFetchResult &res, bool completed = true); + + // http_fetch_sync({url=, timeout=, data=}) + static int l_http_fetch_sync(lua_State *L); + + // http_fetch_async({url=, timeout=, data=}) + static int l_http_fetch_async(lua_State *L); + + // http_fetch_async_get(handle) + static int l_http_fetch_async_get(lua_State *L); + + // request_http_api() + static int l_request_http_api(lua_State *L); + + // get_http_api() + static int l_get_http_api(lua_State *L); +#endif + +public: + static void Initialize(lua_State *L, int top); + static void InitializeAsync(lua_State *L, int top); +}; diff --git a/src/script/lua_api/l_internal.h b/src/script/lua_api/l_internal.h new file mode 100644 index 000000000..34b8cf8e3 --- /dev/null +++ b/src/script/lua_api/l_internal.h @@ -0,0 +1,79 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +/******************************************************************************/ +/******************************************************************************/ +/* WARNING!!!! do NOT add this header in any include file or any code file */ +/* not being a modapi file!!!!!!!! */ +/******************************************************************************/ +/******************************************************************************/ + +#pragma once + +#include "common/c_internal.h" + +#define luamethod(class, name) {#name, class::l_##name} + +#define luamethod_dep(class, good, bad) \ + {#bad, [](lua_State *L) -> int { \ + return l_deprecated_function(L, #good, #bad, &class::l_##good); \ + }} + +#define luamethod_aliased(class, good, bad) \ + luamethod(class, good), \ + luamethod_dep(class, good, bad) + +#define API_FCT(name) registerFunction(L, #name, l_##name, top) + +// For future use +#define MAP_LOCK_REQUIRED ((void)0) +#define NO_MAP_LOCK_REQUIRED ((void)0) + +/* In debug mode ensure no code tries to retrieve the server env when it isn't + * actually available (in CSM) */ +#if !defined(SERVER) && !defined(NDEBUG) +#define DEBUG_ASSERT_NO_CLIENTAPI \ + FATAL_ERROR_IF(getClient(L) != nullptr, "Tried " \ + "to retrieve ServerEnvironment on client") +#else +#define DEBUG_ASSERT_NO_CLIENTAPI ((void)0) +#endif + +// Retrieve ServerEnvironment pointer as `env` (no map lock) +#define GET_ENV_PTR_NO_MAP_LOCK \ + DEBUG_ASSERT_NO_CLIENTAPI; \ + ServerEnvironment *env = (ServerEnvironment *)getEnv(L); \ + if (env == NULL) \ + return 0 + +// Retrieve ServerEnvironment pointer as `env` +#define GET_ENV_PTR \ + MAP_LOCK_REQUIRED; \ + GET_ENV_PTR_NO_MAP_LOCK + +// Retrieve Environment pointer as `env` (no map lock) +#define GET_PLAIN_ENV_PTR_NO_MAP_LOCK \ + Environment *env = (Environment *)getEnv(L); \ + if (env == NULL) \ + return 0 + +// Retrieve Environment pointer as `env` +#define GET_PLAIN_ENV_PTR \ + MAP_LOCK_REQUIRED; \ + GET_PLAIN_ENV_PTR_NO_MAP_LOCK diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp new file mode 100644 index 000000000..c46231acf --- /dev/null +++ b/src/script/lua_api/l_inventory.cpp @@ -0,0 +1,559 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_inventory.h" +#include "lua_api/l_internal.h" +#include "lua_api/l_item.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "server.h" +#include "server/serverinventorymgr.h" +#include "remoteplayer.h" + +/* + InvRef +*/ +InvRef* InvRef::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if(!ud) luaL_typerror(L, narg, className); + return *(InvRef**)ud; // unbox pointer +} + +Inventory* InvRef::getinv(lua_State *L, InvRef *ref) +{ + return getServerInventoryMgr(L)->getInventory(ref->m_loc); +} + +InventoryList* InvRef::getlist(lua_State *L, InvRef *ref, + const char *listname) +{ + NO_MAP_LOCK_REQUIRED; + Inventory *inv = getinv(L, ref); + if(!inv) + return NULL; + return inv->getList(listname); +} + +void InvRef::reportInventoryChange(lua_State *L, InvRef *ref) +{ + // Inform other things that the inventory has changed + getServerInventoryMgr(L)->setInventoryModified(ref->m_loc); +} + +// Exported functions + +// garbage collector +int InvRef::gc_object(lua_State *L) { + InvRef *o = *(InvRef **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +// is_empty(self, listname) -> true/false +int InvRef::l_is_empty(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + InventoryList *list = getlist(L, ref, listname); + if(list && list->getUsedSlots() > 0){ + lua_pushboolean(L, false); + } else { + lua_pushboolean(L, true); + } + return 1; +} + +// get_size(self, listname) +int InvRef::l_get_size(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + InventoryList *list = getlist(L, ref, listname); + if(list){ + lua_pushinteger(L, list->getSize()); + } else { + lua_pushinteger(L, 0); + } + return 1; +} + +// get_width(self, listname) +int InvRef::l_get_width(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + InventoryList *list = getlist(L, ref, listname); + if(list){ + lua_pushinteger(L, list->getWidth()); + } else { + lua_pushinteger(L, 0); + } + return 1; +} + +// set_size(self, listname, size) +int InvRef::l_set_size(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + + int newsize = luaL_checknumber(L, 3); + if (newsize < 0) { + lua_pushboolean(L, false); + return 1; + } + + Inventory *inv = getinv(L, ref); + if(inv == NULL){ + lua_pushboolean(L, false); + return 1; + } + if(newsize == 0){ + inv->deleteList(listname); + reportInventoryChange(L, ref); + lua_pushboolean(L, true); + return 1; + } + InventoryList *list = inv->getList(listname); + if(list){ + list->setSize(newsize); + } else { + list = inv->addList(listname, newsize); + if (!list) + { + lua_pushboolean(L, false); + return 1; + } + } + reportInventoryChange(L, ref); + lua_pushboolean(L, true); + return 1; +} + +// set_width(self, listname, size) +int InvRef::l_set_width(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + int newwidth = luaL_checknumber(L, 3); + Inventory *inv = getinv(L, ref); + if(inv == NULL){ + return 0; + } + InventoryList *list = inv->getList(listname); + if(list){ + list->setWidth(newwidth); + } else { + return 0; + } + reportInventoryChange(L, ref); + return 0; +} + +// get_stack(self, listname, i) -> itemstack +int InvRef::l_get_stack(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + int i = luaL_checknumber(L, 3) - 1; + InventoryList *list = getlist(L, ref, listname); + ItemStack item; + if(list != NULL && i >= 0 && i < (int) list->getSize()) + item = list->getItem(i); + LuaItemStack::create(L, item); + return 1; +} + +// set_stack(self, listname, i, stack) -> true/false +int InvRef::l_set_stack(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + int i = luaL_checknumber(L, 3) - 1; + ItemStack newitem = read_item(L, 4, getServer(L)->idef()); + InventoryList *list = getlist(L, ref, listname); + if(list != NULL && i >= 0 && i < (int) list->getSize()){ + list->changeItem(i, newitem); + reportInventoryChange(L, ref); + lua_pushboolean(L, true); + } else { + lua_pushboolean(L, false); + } + return 1; +} + +// get_list(self, listname) -> list or nil +int InvRef::l_get_list(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + Inventory *inv = getinv(L, ref); + if(inv){ + push_inventory_list(L, inv, listname); + } else { + lua_pushnil(L); + } + return 1; +} + +// set_list(self, listname, list) +int InvRef::l_set_list(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + Inventory *inv = getinv(L, ref); + if(inv == NULL){ + return 0; + } + InventoryList *list = inv->getList(listname); + if(list) + read_inventory_list(L, 3, inv, listname, + getServer(L), list->getSize()); + else + read_inventory_list(L, 3, inv, listname, getServer(L)); + reportInventoryChange(L, ref); + return 0; +} + +// get_lists(self) -> list of InventoryLists +int InvRef::l_get_lists(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + Inventory *inv = getinv(L, ref); + if (!inv) { + return 0; + } + std::vector lists = inv->getLists(); + std::vector::iterator iter = lists.begin(); + lua_createtable(L, 0, lists.size()); + for (; iter != lists.end(); iter++) { + const char* name = (*iter)->getName().c_str(); + lua_pushstring(L, name); + push_inventory_list(L, inv, name); + lua_rawset(L, -3); + } + return 1; +} + +// set_lists(self, lists) +int InvRef::l_set_lists(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + Inventory *inv = getinv(L, ref); + if (!inv) { + return 0; + } + + // Make a temporary inventory in case reading fails + Inventory *tempInv(inv); + tempInv->clear(); + + Server *server = getServer(L); + + lua_pushnil(L); + luaL_checktype(L, 2, LUA_TTABLE); + while (lua_next(L, 2)) { + const char *listname = lua_tostring(L, -2); + read_inventory_list(L, -1, tempInv, listname, server); + lua_pop(L, 1); + } + inv = tempInv; + return 0; +} + +// add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack +// Returns the leftover stack +int InvRef::l_add_item(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + ItemStack item = read_item(L, 3, getServer(L)->idef()); + InventoryList *list = getlist(L, ref, listname); + if(list){ + ItemStack leftover = list->addItem(item); + if(leftover.count != item.count) + reportInventoryChange(L, ref); + LuaItemStack::create(L, leftover); + } else { + LuaItemStack::create(L, item); + } + return 1; +} + +// room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false +// Returns true if the item completely fits into the list +int InvRef::l_room_for_item(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + ItemStack item = read_item(L, 3, getServer(L)->idef()); + InventoryList *list = getlist(L, ref, listname); + if(list){ + lua_pushboolean(L, list->roomForItem(item)); + } else { + lua_pushboolean(L, false); + } + return 1; +} + +// contains_item(self, listname, itemstack or itemstring or table or nil, [match_meta]) -> true/false +// Returns true if the list contains the given count of the given item +int InvRef::l_contains_item(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + ItemStack item = read_item(L, 3, getServer(L)->idef()); + InventoryList *list = getlist(L, ref, listname); + bool match_meta = false; + if (lua_isboolean(L, 4)) + match_meta = readParam(L, 4); + if (list) { + lua_pushboolean(L, list->containsItem(item, match_meta)); + } else { + lua_pushboolean(L, false); + } + return 1; +} + +// remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack +// Returns the items that were actually removed +int InvRef::l_remove_item(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + ItemStack item = read_item(L, 3, getServer(L)->idef()); + InventoryList *list = getlist(L, ref, listname); + if(list){ + ItemStack removed = list->removeItem(item); + if(!removed.empty()) + reportInventoryChange(L, ref); + LuaItemStack::create(L, removed); + } else { + LuaItemStack::create(L, ItemStack()); + } + return 1; +} + +// get_location() -> location (like get_inventory(location)) +int InvRef::l_get_location(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *ref = checkobject(L, 1); + const InventoryLocation &loc = ref->m_loc; + switch(loc.type){ + case InventoryLocation::PLAYER: + lua_newtable(L); + lua_pushstring(L, "player"); + lua_setfield(L, -2, "type"); + lua_pushstring(L, loc.name.c_str()); + lua_setfield(L, -2, "name"); + return 1; + case InventoryLocation::NODEMETA: + lua_newtable(L); + lua_pushstring(L, "node"); + lua_setfield(L, -2, "type"); + push_v3s16(L, loc.p); + lua_setfield(L, -2, "pos"); + return 1; + case InventoryLocation::DETACHED: + lua_newtable(L); + lua_pushstring(L, "detached"); + lua_setfield(L, -2, "type"); + lua_pushstring(L, loc.name.c_str()); + lua_setfield(L, -2, "name"); + return 1; + case InventoryLocation::UNDEFINED: + case InventoryLocation::CURRENT_PLAYER: + break; + } + lua_newtable(L); + lua_pushstring(L, "undefined"); + lua_setfield(L, -2, "type"); + return 1; +} + + +InvRef::InvRef(const InventoryLocation &loc): + m_loc(loc) +{ +} + +// Creates an InvRef and leaves it on top of stack +// Not callable from Lua; all references are created on the C side. +void InvRef::create(lua_State *L, const InventoryLocation &loc) +{ + NO_MAP_LOCK_REQUIRED; + InvRef *o = new InvRef(loc); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); +} +void InvRef::createPlayer(lua_State *L, RemotePlayer *player) +{ + NO_MAP_LOCK_REQUIRED; + InventoryLocation loc; + loc.setPlayer(player->getName()); + create(L, loc); +} +void InvRef::createNodeMeta(lua_State *L, v3s16 p) +{ + InventoryLocation loc; + loc.setNodeMeta(p); + create(L, loc); +} + +void InvRef::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Cannot be created from Lua + //lua_register(L, className, create_object); +} + +const char InvRef::className[] = "InvRef"; +const luaL_Reg InvRef::methods[] = { + luamethod(InvRef, is_empty), + luamethod(InvRef, get_size), + luamethod(InvRef, set_size), + luamethod(InvRef, get_width), + luamethod(InvRef, set_width), + luamethod(InvRef, get_stack), + luamethod(InvRef, set_stack), + luamethod(InvRef, get_list), + luamethod(InvRef, set_list), + luamethod(InvRef, get_lists), + luamethod(InvRef, set_lists), + luamethod(InvRef, add_item), + luamethod(InvRef, room_for_item), + luamethod(InvRef, contains_item), + luamethod(InvRef, remove_item), + luamethod(InvRef, get_location), + {0,0} +}; + +// get_inventory(location) +int ModApiInventory::l_get_inventory(lua_State *L) +{ + InventoryLocation loc; + + lua_getfield(L, 1, "type"); + std::string type = luaL_checkstring(L, -1); + lua_pop(L, 1); + + if(type == "node"){ + MAP_LOCK_REQUIRED; + lua_getfield(L, 1, "pos"); + v3s16 pos = check_v3s16(L, -1); + loc.setNodeMeta(pos); + + if (getServerInventoryMgr(L)->getInventory(loc) != NULL) + InvRef::create(L, loc); + else + lua_pushnil(L); + return 1; + } + + NO_MAP_LOCK_REQUIRED; + if (type == "player") { + lua_getfield(L, 1, "name"); + loc.setPlayer(luaL_checkstring(L, -1)); + lua_pop(L, 1); + } else if (type == "detached") { + lua_getfield(L, 1, "name"); + loc.setDetached(luaL_checkstring(L, -1)); + lua_pop(L, 1); + } + + if (getServerInventoryMgr(L)->getInventory(loc) != NULL) + InvRef::create(L, loc); + else + lua_pushnil(L); + return 1; + // END NO_MAP_LOCK_REQUIRED; + +} + +// create_detached_inventory_raw(name, [player_name]) +int ModApiInventory::l_create_detached_inventory_raw(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *name = luaL_checkstring(L, 1); + std::string player = readParam(L, 2, ""); + if (getServerInventoryMgr(L)->createDetachedInventory(name, getServer(L)->idef(), player) != NULL) { + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + } else { + lua_pushnil(L); + } + return 1; +} + +// remove_detached_inventory_raw(name) +int ModApiInventory::l_remove_detached_inventory_raw(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const std::string &name = luaL_checkstring(L, 1); + lua_pushboolean(L, getServerInventoryMgr(L)->removeDetachedInventory(name)); + return 1; +} + +void ModApiInventory::Initialize(lua_State *L, int top) +{ + API_FCT(create_detached_inventory_raw); + API_FCT(remove_detached_inventory_raw); + API_FCT(get_inventory); +} diff --git a/src/script/lua_api/l_inventory.h b/src/script/lua_api/l_inventory.h new file mode 100644 index 000000000..6b0693681 --- /dev/null +++ b/src/script/lua_api/l_inventory.h @@ -0,0 +1,129 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" + +#include "inventory.h" +#include "inventorymanager.h" + +class RemotePlayer; + +/* + InvRef +*/ + +class InvRef : public ModApiBase { +private: + InventoryLocation m_loc; + + static const char className[]; + static const luaL_Reg methods[]; + + static InvRef *checkobject(lua_State *L, int narg); + + static Inventory* getinv(lua_State *L, InvRef *ref); + + static InventoryList* getlist(lua_State *L, InvRef *ref, + const char *listname); + + static void reportInventoryChange(lua_State *L, InvRef *ref); + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // is_empty(self, listname) -> true/false + static int l_is_empty(lua_State *L); + + // get_size(self, listname) + static int l_get_size(lua_State *L); + + // get_width(self, listname) + static int l_get_width(lua_State *L); + + // set_size(self, listname, size) + static int l_set_size(lua_State *L); + + // set_width(self, listname, size) + static int l_set_width(lua_State *L); + + // get_stack(self, listname, i) -> itemstack + static int l_get_stack(lua_State *L); + + // set_stack(self, listname, i, stack) -> true/false + static int l_set_stack(lua_State *L); + + // get_list(self, listname) -> list or nil + static int l_get_list(lua_State *L); + + // set_list(self, listname, list) + static int l_set_list(lua_State *L); + + // get_lists(self) -> list of InventoryLists + static int l_get_lists(lua_State *L); + + // set_lists(self, lists) + static int l_set_lists(lua_State *L); + + // add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack + // Returns the leftover stack + static int l_add_item(lua_State *L); + + // room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false + // Returns true if the item completely fits into the list + static int l_room_for_item(lua_State *L); + + // contains_item(self, listname, itemstack or itemstring or table or nil, [match_meta]) -> true/false + // Returns true if the list contains the given count of the given item name + static int l_contains_item(lua_State *L); + + // remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack + // Returns the items that were actually removed + static int l_remove_item(lua_State *L); + + // get_location() -> location (like get_inventory(location)) + static int l_get_location(lua_State *L); + +public: + InvRef(const InventoryLocation &loc); + + ~InvRef() = default; + + // Creates an InvRef and leaves it on top of stack + // Not callable from Lua; all references are created on the C side. + static void create(lua_State *L, const InventoryLocation &loc); + static void createPlayer(lua_State *L, RemotePlayer *player); + static void createNodeMeta(lua_State *L, v3s16 p); + static void Register(lua_State *L); +}; + +class ModApiInventory : public ModApiBase { +private: + static int l_create_detached_inventory_raw(lua_State *L); + + static int l_remove_detached_inventory_raw(lua_State *L); + + static int l_get_inventory(lua_State *L); + +public: + static void Initialize(lua_State *L, int top); +}; diff --git a/src/script/lua_api/l_inventoryaction.cpp b/src/script/lua_api/l_inventoryaction.cpp new file mode 100644 index 000000000..d763644e4 --- /dev/null +++ b/src/script/lua_api/l_inventoryaction.cpp @@ -0,0 +1,273 @@ +/* +Dragonfire +Copyright (C) 2020 Elias Fleckenstein + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "l_inventoryaction.h" +#include "l_internal.h" +#include "client/client.h" + +int LuaInventoryAction::gc_object(lua_State *L) +{ + LuaInventoryAction *o = *(LuaInventoryAction **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +int LuaInventoryAction::mt_tostring(lua_State *L) +{ + LuaInventoryAction *o = checkobject(L, 1); + std::ostringstream os(std::ios::binary); + o->m_action->serialize(os); + lua_pushfstring(L, "InventoryAction(\"%s\")", os.str().c_str()); + return 1; +} + +int LuaInventoryAction::l_apply(lua_State *L) +{ + LuaInventoryAction *o = checkobject(L, 1); + + std::ostringstream os(std::ios::binary); + o->m_action->serialize(os); + + std::istringstream is(os.str(), std::ios_base::binary); + + InventoryAction *a = InventoryAction::deSerialize(is); + + getClient(L)->inventoryAction(a); + return 0; +} + +int LuaInventoryAction::l_from(lua_State *L) +{ + GET_MOVE_ACTION + readFullInventoryLocationInto(L, &act->from_inv, &act->from_list, &act->from_i); + return 0; +} + +int LuaInventoryAction::l_to(lua_State *L) +{ + GET_MOVE_ACTION + readFullInventoryLocationInto(L, &act->to_inv, &act->to_list, &act->to_i); + return 0; +} + +int LuaInventoryAction::l_craft(lua_State *L) +{ + LuaInventoryAction *o = checkobject(L, 1); + + if (o->m_action->getType() != IAction::Craft) + return 0; + + std::string locStr; + InventoryLocation loc; + + locStr = readParam(L, 2); + + try { + loc.deSerialize(locStr); + dynamic_cast(o->m_action)->craft_inv = loc; + } catch (SerializationError &) { + } + + return 0; +} + +int LuaInventoryAction::l_set_count(lua_State *L) +{ + LuaInventoryAction *o = checkobject(L, 1); + + s16 count = luaL_checkinteger(L, 2); + + switch (o->m_action->getType()) { + case IAction::Move: + ((IMoveAction *)o->m_action)->count = count; + break; + case IAction::Drop: + ((IDropAction *)o->m_action)->count = count; + break; + case IAction::Craft: + ((ICraftAction *)o->m_action)->count = count; + break; + } + + return 0; +} + +int LuaInventoryAction::l_to_table(lua_State *L) +{ + LuaInventoryAction *o = checkobject(L, 1); + MoveAction *act = dynamic_cast(o->m_action); + + std::string type = ""; + u16 count = 0; + switch (o->m_action->getType()) { + case IAction::Move: + count = ((IMoveAction *)o->m_action)->count; + type = "move"; + break; + case IAction::Drop: + count = ((IDropAction *)o->m_action)->count; + type = "drop"; + break; + case IAction::Craft: + count = ((ICraftAction *)o->m_action)->count; + type = "craft"; + break; + } + + lua_newtable(L); + lua_pushinteger(L, count); + lua_setfield(L, -2, "count"); + lua_pushstring(L, type.c_str()); + lua_setfield(L, -2, "type"); + + lua_newtable(L); + std::ostringstream from_loc; + act->from_inv.serialize(from_loc); + lua_pushstring(L, from_loc.str().c_str()); + lua_setfield(L, -2, "location"); + lua_pushstring(L, act->from_list.c_str()); + lua_setfield(L, -2, "inventory"); + lua_pushinteger(L, act->from_i + 1); + lua_setfield(L, -2, "slot"); + lua_setfield(L, -2, "from"); + + lua_newtable(L); + std::ostringstream to_loc; + act->to_inv.serialize(to_loc); + lua_pushstring(L, to_loc.str().c_str()); + lua_setfield(L, -2, "location"); + lua_pushstring(L, act->to_list.c_str()); + lua_setfield(L, -2, "inventory"); + lua_pushinteger(L, act->to_i + 1); + lua_setfield(L, -2, "slot"); + lua_setfield(L, -2, "to"); + + return 1; +} + +LuaInventoryAction::LuaInventoryAction(const IAction &type) : m_action(nullptr) +{ + switch (type) { + case IAction::Move: + m_action = new IMoveAction(); + break; + case IAction::Drop: + m_action = new IDropAction(); + break; + case IAction::Craft: + m_action = new ICraftAction(); + break; + } +} + +LuaInventoryAction::~LuaInventoryAction() +{ + delete m_action; +} + +void LuaInventoryAction::readFullInventoryLocationInto( + lua_State *L, InventoryLocation *loc, std::string *list, s16 *index) +{ + try { + loc->deSerialize(readParam(L, 2)); + std::string l = readParam(L, 3); + *list = l; + *index = luaL_checkinteger(L, 4) - 1; + } catch (SerializationError &) { + } +} + +int LuaInventoryAction::create_object(lua_State *L) +{ + IAction type; + std::string typeStr; + + typeStr = readParam(L, 1); + + if (typeStr == "move") + type = IAction::Move; + else if (typeStr == "drop") + type = IAction::Drop; + else if (typeStr == "craft") + type = IAction::Craft; + else + return 0; + + LuaInventoryAction *o = new LuaInventoryAction(type); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + +int LuaInventoryAction::create(lua_State *L, const IAction &type) +{ + LuaInventoryAction *o = new LuaInventoryAction(type); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + +LuaInventoryAction *LuaInventoryAction::checkobject(lua_State *L, int narg) +{ + return *(LuaInventoryAction **)luaL_checkudata(L, narg, className); +} + +void LuaInventoryAction::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pushliteral(L, "__tostring"); + lua_pushcfunction(L, mt_tostring); + lua_settable(L, metatable); + + lua_pop(L, 1); + + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); + + lua_register(L, className, create_object); +} + +const char LuaInventoryAction::className[] = "InventoryAction"; +const luaL_Reg LuaInventoryAction::methods[] = { + luamethod(LuaInventoryAction, apply), + luamethod(LuaInventoryAction, from), + luamethod(LuaInventoryAction, to), + luamethod(LuaInventoryAction, craft), + luamethod(LuaInventoryAction, set_count), + luamethod(LuaInventoryAction, to_table), + {0,0} +}; diff --git a/src/script/lua_api/l_inventoryaction.h b/src/script/lua_api/l_inventoryaction.h new file mode 100644 index 000000000..971a497b2 --- /dev/null +++ b/src/script/lua_api/l_inventoryaction.h @@ -0,0 +1,76 @@ +/* +Dragonfire +Copyright (C) 2020 Elias Fleckenstein + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "inventorymanager.h" +#include "lua_api/l_base.h" + +#define GET_MOVE_ACTION \ + LuaInventoryAction *o = checkobject(L, 1); \ + if (o->m_action->getType() == IAction::Craft) \ + return 0; \ + MoveAction *act = dynamic_cast(o->m_action); + +class LuaInventoryAction : public ModApiBase { +private: + InventoryAction *m_action; + + static void readFullInventoryLocationInto(lua_State *L, InventoryLocation *loc, std::string *list, s16 *index); + + static const char className[]; + static const luaL_Reg methods[]; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // __tostring metamethod + static int mt_tostring(lua_State *L); + + // apply(self) + static int l_apply(lua_State *L); + + // from(self, location, list, index) + static int l_from(lua_State *L); + + // to(self, location, list, index) + static int l_to(lua_State *L); + + // craft(self, location) + static int l_craft(lua_State *L); + + // set_count(self, count) + static int l_set_count(lua_State *L); + + // to_table(self) + static int l_to_table(lua_State *L); +public: + LuaInventoryAction(const IAction &type); + ~LuaInventoryAction(); + + // LuaInventoryAction(inventory action type) + // Creates an LuaInventoryAction and leaves it on top of stack + static int create_object(lua_State *L); + // Not callable from Lua + static int create(lua_State *L, const IAction &type); + static LuaInventoryAction *checkobject(lua_State *L, int narg); + static void Register(lua_State *L); +}; diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp new file mode 100644 index 000000000..d13ca4ad4 --- /dev/null +++ b/src/script/lua_api/l_item.cpp @@ -0,0 +1,681 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_item.h" +#include "lua_api/l_itemstackmeta.h" +#include "lua_api/l_internal.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "itemdef.h" +#include "nodedef.h" +#include "server.h" +#include "inventory.h" +#include "log.h" +#include "script/cpp_api/s_base.h" +#ifndef SERVER +#include "client/client.h" +#include "client/renderingengine.h" +#include "client/shader.h" +#endif + +// garbage collector +int LuaItemStack::gc_object(lua_State *L) +{ + LuaItemStack *o = *(LuaItemStack **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +// __tostring metamethod +int LuaItemStack::mt_tostring(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + std::string itemstring = o->m_stack.getItemString(false); + lua_pushfstring(L, "ItemStack(\"%s\")", itemstring.c_str()); + return 1; +} + +// is_empty(self) -> true/false +int LuaItemStack::l_is_empty(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushboolean(L, item.empty()); + return 1; +} + +// get_name(self) -> string +int LuaItemStack::l_get_name(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushstring(L, item.name.c_str()); + return 1; +} + +// set_name(self, name) +int LuaItemStack::l_set_name(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + + bool status = true; + item.name = luaL_checkstring(L, 2); + if (item.name.empty() || item.empty()) { + item.clear(); + status = false; + } + + lua_pushboolean(L, status); + return 1; +} + +// get_count(self) -> number +int LuaItemStack::l_get_count(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushinteger(L, item.count); + return 1; +} + +// set_count(self, number) +int LuaItemStack::l_set_count(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + + bool status; + lua_Integer count = luaL_checkinteger(L, 2); + if (count > 0 && count <= 65535) { + item.count = count; + status = true; + } else { + item.clear(); + status = false; + } + + lua_pushboolean(L, status); + return 1; +} + +// get_wear(self) -> number +int LuaItemStack::l_get_wear(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushinteger(L, item.wear); + return 1; +} + +// set_wear(self, number) +int LuaItemStack::l_set_wear(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + + bool status; + lua_Integer wear = luaL_checkinteger(L, 2); + if (wear <= 65535) { + item.wear = wear; + status = true; + } else { + item.clear(); + status = false; + } + + lua_pushboolean(L, status); + return 1; +} + +// get_meta(self) -> string +int LuaItemStack::l_get_meta(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStackMetaRef::create(L, &o->m_stack); + return 1; +} + +// DEPRECATED +// get_metadata(self) -> string +int LuaItemStack::l_get_metadata(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + const std::string &value = item.metadata.getString(""); + lua_pushlstring(L, value.c_str(), value.size()); + return 1; +} + +// DEPRECATED +// set_metadata(self, string) +int LuaItemStack::l_set_metadata(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + + size_t len = 0; + const char *ptr = luaL_checklstring(L, 2, &len); + item.metadata.setString("", std::string(ptr, len)); + + lua_pushboolean(L, true); + return 1; +} + +// get_description(self) +int LuaItemStack::l_get_description(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + std::string desc = o->m_stack.getDescription(getGameDef(L)->idef()); + lua_pushstring(L, desc.c_str()); + return 1; +} + +// get_short_description(self) +int LuaItemStack::l_get_short_description(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + std::string desc = o->m_stack.getShortDescription(getGameDef(L)->idef()); + lua_pushstring(L, desc.c_str()); + return 1; +} + +// clear(self) -> true +int LuaItemStack::l_clear(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + o->m_stack.clear(); + lua_pushboolean(L, true); + return 1; +} + +// replace(self, itemstack or itemstring or table or nil) -> true +int LuaItemStack::l_replace(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + o->m_stack = read_item(L, 2, getGameDef(L)->idef()); + lua_pushboolean(L, true); + return 1; +} + +// to_string(self) -> string +int LuaItemStack::l_to_string(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + std::string itemstring = o->m_stack.getItemString(); + lua_pushstring(L, itemstring.c_str()); + return 1; +} + +// to_table(self) -> table or nil +int LuaItemStack::l_to_table(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + const ItemStack &item = o->m_stack; + if(item.empty()) + { + lua_pushnil(L); + } + else + { + lua_newtable(L); + lua_pushstring(L, item.name.c_str()); + lua_setfield(L, -2, "name"); + lua_pushinteger(L, item.count); + lua_setfield(L, -2, "count"); + lua_pushinteger(L, item.wear); + lua_setfield(L, -2, "wear"); + + const std::string &metadata_str = item.metadata.getString(""); + lua_pushlstring(L, metadata_str.c_str(), metadata_str.size()); + lua_setfield(L, -2, "metadata"); + + lua_newtable(L); + const StringMap &fields = item.metadata.getStrings(); + for (const auto &field : fields) { + const std::string &name = field.first; + if (name.empty()) + continue; + const std::string &value = field.second; + lua_pushlstring(L, name.c_str(), name.size()); + lua_pushlstring(L, value.c_str(), value.size()); + lua_settable(L, -3); + } + lua_setfield(L, -2, "meta"); + } + return 1; +} + +// get_stack_max(self) -> number +int LuaItemStack::l_get_stack_max(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushinteger(L, item.getStackMax(getGameDef(L)->idef())); + return 1; +} + +// get_free_space(self) -> number +int LuaItemStack::l_get_free_space(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushinteger(L, item.freeSpace(getGameDef(L)->idef())); + return 1; +} + +// is_known(self) -> true/false +// Checks if the item is defined. +int LuaItemStack::l_is_known(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + bool is_known = item.isKnown(getGameDef(L)->idef()); + lua_pushboolean(L, is_known); + return 1; +} + +// get_definition(self) -> table +// Returns the item definition table from registered_items, +// or a fallback one (name="unknown") +int LuaItemStack::l_get_definition(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + + // Get registered_items[name] + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_items"); + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, item.name.c_str()); + if(lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_getfield(L, -1, "unknown"); + } + return 1; +} + +// get_tool_capabilities(self) -> table +// Returns the effective tool digging properties. +// Returns those of the hand ("") if this item has none associated. +int LuaItemStack::l_get_tool_capabilities(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + const ToolCapabilities &prop = + item.getToolCapabilities(getGameDef(L)->idef()); + push_tool_capabilities(L, prop); + return 1; +} + +// add_wear(self, amount) -> true/false +// The range for "amount" is [0,65535]. Wear is only added if the item +// is a tool. Adding wear might destroy the item. +// Returns true if the item is (or was) a tool. +int LuaItemStack::l_add_wear(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + int amount = lua_tointeger(L, 2); + bool result = item.addWear(amount, getGameDef(L)->idef()); + lua_pushboolean(L, result); + return 1; +} + +// add_item(self, itemstack or itemstring or table or nil) -> itemstack +// Returns leftover item stack +int LuaItemStack::l_add_item(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + ItemStack newitem = read_item(L, -1, getGameDef(L)->idef()); + ItemStack leftover = item.addItem(newitem, getGameDef(L)->idef()); + create(L, leftover); + return 1; +} + +// item_fits(self, itemstack or itemstring or table or nil) -> true/false, itemstack +// First return value is true iff the new item fits fully into the stack +// Second return value is the would-be-left-over item stack +int LuaItemStack::l_item_fits(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + ItemStack newitem = read_item(L, 2, getGameDef(L)->idef()); + ItemStack restitem; + bool fits = item.itemFits(newitem, &restitem, getGameDef(L)->idef()); + lua_pushboolean(L, fits); // first return value + create(L, restitem); // second return value + return 2; +} + +// take_item(self, takecount=1) -> itemstack +int LuaItemStack::l_take_item(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + u32 takecount = 1; + if(!lua_isnone(L, 2)) + takecount = luaL_checkinteger(L, 2); + ItemStack taken = item.takeItem(takecount); + create(L, taken); + return 1; +} + +// peek_item(self, peekcount=1) -> itemstack +int LuaItemStack::l_peek_item(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + u32 peekcount = 1; + if(!lua_isnone(L, 2)) + peekcount = lua_tointeger(L, 2); + ItemStack peekaboo = item.peekItem(peekcount); + create(L, peekaboo); + return 1; +} + +LuaItemStack::LuaItemStack(const ItemStack &item): + m_stack(item) +{ +} + +const ItemStack& LuaItemStack::getItem() const +{ + return m_stack; +} +ItemStack& LuaItemStack::getItem() +{ + return m_stack; +} + +// LuaItemStack(itemstack or itemstring or table or nil) +// Creates an LuaItemStack and leaves it on top of stack +int LuaItemStack::create_object(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ItemStack item; + if (!lua_isnone(L, 1)) + item = read_item(L, 1, getGameDef(L)->idef()); + LuaItemStack *o = new LuaItemStack(item); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} +// Not callable from Lua +int LuaItemStack::create(lua_State *L, const ItemStack &item) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = new LuaItemStack(item); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + +LuaItemStack *LuaItemStack::checkobject(lua_State *L, int narg) +{ + return *(LuaItemStack **)luaL_checkudata(L, narg, className); +} + +void LuaItemStack::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + // hide metatable from Lua getmetatable() + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pushliteral(L, "__tostring"); + lua_pushcfunction(L, mt_tostring); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Can be created from Lua (ItemStack(itemstack or itemstring or table or nil)) + lua_register(L, className, create_object); +} + +const char LuaItemStack::className[] = "ItemStack"; +const luaL_Reg LuaItemStack::methods[] = { + luamethod(LuaItemStack, is_empty), + luamethod(LuaItemStack, get_name), + luamethod(LuaItemStack, set_name), + luamethod(LuaItemStack, get_count), + luamethod(LuaItemStack, set_count), + luamethod(LuaItemStack, get_wear), + luamethod(LuaItemStack, set_wear), + luamethod(LuaItemStack, get_meta), + luamethod(LuaItemStack, get_metadata), + luamethod(LuaItemStack, set_metadata), + luamethod(LuaItemStack, get_description), + luamethod(LuaItemStack, get_short_description), + luamethod(LuaItemStack, clear), + luamethod(LuaItemStack, replace), + luamethod(LuaItemStack, to_string), + luamethod(LuaItemStack, to_table), + luamethod(LuaItemStack, get_stack_max), + luamethod(LuaItemStack, get_free_space), + luamethod(LuaItemStack, is_known), + luamethod(LuaItemStack, get_definition), + luamethod(LuaItemStack, get_tool_capabilities), + luamethod(LuaItemStack, add_wear), + luamethod(LuaItemStack, add_item), + luamethod(LuaItemStack, item_fits), + luamethod(LuaItemStack, take_item), + luamethod(LuaItemStack, peek_item), + {0,0} +}; + +/* + ItemDefinition +*/ + +// register_item_raw({lots of stuff}) +int ModApiItemMod::l_register_item_raw(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + luaL_checktype(L, 1, LUA_TTABLE); + int table = 1; + + // Get the writable item and node definition managers from the server + IWritableItemDefManager *idef = + getGameDef(L)->getWritableItemDefManager(); + NodeDefManager *ndef = + getGameDef(L)->getWritableNodeDefManager(); + + // Check if name is defined + std::string name; + lua_getfield(L, table, "name"); + if(lua_isstring(L, -1)){ + name = readParam(L, -1); + } else { + throw LuaError("register_item_raw: name is not defined or not a string"); + } + + // Check if on_use is defined + + ItemDefinition def; + // Set a distinctive default value to check if this is set + def.node_placement_prediction = "__default"; + + // Read the item definition + read_item_definition(L, table, def, def); + + // Default to having client-side placement prediction for nodes + // ("" in item definition sets it off) + if(def.node_placement_prediction == "__default"){ + if(def.type == ITEM_NODE) + def.node_placement_prediction = name; + else + def.node_placement_prediction = ""; + } + + // Register item definition + idef->registerItem(def); + + // Read the node definition (content features) and register it + if (def.type == ITEM_NODE) { + ContentFeatures f; + read_content_features(L, f, table); + // when a mod reregisters ignore, only texture changes and such should + // be done + if (f.name == "ignore") + return 0; + + content_t id = ndef->set(f.name, f); + + if (id > MAX_REGISTERED_CONTENT) { + throw LuaError("Number of registerable nodes (" + + itos(MAX_REGISTERED_CONTENT+1) + + ") exceeded (" + name + ")"); + } + + } + + return 0; /* number of results */ +} + +// unregister_item(name) +int ModApiItemMod::l_unregister_item_raw(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string name = luaL_checkstring(L, 1); + + IWritableItemDefManager *idef = + getGameDef(L)->getWritableItemDefManager(); + + // Unregister the node + if (idef->get(name).type == ITEM_NODE) { + NodeDefManager *ndef = + getGameDef(L)->getWritableNodeDefManager(); + ndef->removeNode(name); + } + + idef->unregisterItem(name); + + return 0; /* number of results */ +} + +// register_alias_raw(name, convert_to_name) +int ModApiItemMod::l_register_alias_raw(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string name = luaL_checkstring(L, 1); + std::string convert_to = luaL_checkstring(L, 2); + + // Get the writable item definition manager from the server + IWritableItemDefManager *idef = + getGameDef(L)->getWritableItemDefManager(); + + idef->registerAlias(name, convert_to); + + return 0; /* number of results */ +} + +// get_content_id(name) +int ModApiItemMod::l_get_content_id(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string name = luaL_checkstring(L, 1); + + const IItemDefManager *idef = getGameDef(L)->getItemDefManager(); + const NodeDefManager *ndef = getGameDef(L)->getNodeDefManager(); + + // If this is called at mod load time, NodeDefManager isn't aware of + // aliases yet, so we need to handle them manually + std::string alias_name = idef->getAlias(name); + + content_t content_id; + if (alias_name != name) { + if (!ndef->getId(alias_name, content_id)) + throw LuaError("Unknown node: " + alias_name + + " (from alias " + name + ")"); + } else if (!ndef->getId(name, content_id)) { + throw LuaError("Unknown node: " + name); + } + + lua_pushinteger(L, content_id); + return 1; /* number of results */ +} + +// get_name_from_content_id(name) +int ModApiItemMod::l_get_name_from_content_id(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + content_t c = luaL_checkint(L, 1); + + const NodeDefManager *ndef = getGameDef(L)->getNodeDefManager(); + const char *name = ndef->get(c).name.c_str(); + + lua_pushstring(L, name); + return 1; /* number of results */ +} + +void ModApiItemMod::Initialize(lua_State *L, int top) +{ + API_FCT(register_item_raw); + API_FCT(unregister_item_raw); + API_FCT(register_alias_raw); + API_FCT(get_content_id); + API_FCT(get_name_from_content_id); +} diff --git a/src/script/lua_api/l_item.h b/src/script/lua_api/l_item.h new file mode 100644 index 000000000..eb0f67503 --- /dev/null +++ b/src/script/lua_api/l_item.h @@ -0,0 +1,157 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" +#include "inventory.h" // ItemStack + +class LuaItemStack : public ModApiBase { +private: + ItemStack m_stack; + + static const char className[]; + static const luaL_Reg methods[]; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // __tostring metamethod + static int mt_tostring(lua_State *L); + + // is_empty(self) -> true/false + static int l_is_empty(lua_State *L); + + // get_name(self) -> string + static int l_get_name(lua_State *L); + + // set_name(self, name) + static int l_set_name(lua_State *L); + + // get_count(self) -> number + static int l_get_count(lua_State *L); + + // set_count(self, number) + static int l_set_count(lua_State *L); + + // get_wear(self) -> number + static int l_get_wear(lua_State *L); + + // set_wear(self, number) + static int l_set_wear(lua_State *L); + + // get_meta(self) -> string + static int l_get_meta(lua_State *L); + + // DEPRECATED + // get_metadata(self) -> string + static int l_get_metadata(lua_State *L); + + // DEPRECATED + // set_metadata(self, string) + static int l_set_metadata(lua_State *L); + + // get_description(self) + static int l_get_description(lua_State *L); + + // get_short_description(self) + static int l_get_short_description(lua_State *L); + + // clear(self) -> true + static int l_clear(lua_State *L); + + // replace(self, itemstack or itemstring or table or nil) -> true + static int l_replace(lua_State *L); + + // to_string(self) -> string + static int l_to_string(lua_State *L); + + // to_table(self) -> table or nil + static int l_to_table(lua_State *L); + + // get_stack_max(self) -> number + static int l_get_stack_max(lua_State *L); + + // get_free_space(self) -> number + static int l_get_free_space(lua_State *L); + + // is_known(self) -> true/false + // Checks if the item is defined. + static int l_is_known(lua_State *L); + + // get_definition(self) -> table + // Returns the item definition table from core.registered_items, + // or a fallback one (name="unknown") + static int l_get_definition(lua_State *L); + + // get_tool_capabilities(self) -> table + // Returns the effective tool digging properties. + // Returns those of the hand ("") if this item has none associated. + static int l_get_tool_capabilities(lua_State *L); + + // add_wear(self, amount) -> true/false + // The range for "amount" is [0,65535]. Wear is only added if the item + // is a tool. Adding wear might destroy the item. + // Returns true if the item is (or was) a tool. + static int l_add_wear(lua_State *L); + + // add_item(self, itemstack or itemstring or table or nil) -> itemstack + // Returns leftover item stack + static int l_add_item(lua_State *L); + + // item_fits(self, itemstack or itemstring or table or nil) -> true/false, itemstack + // First return value is true iff the new item fits fully into the stack + // Second return value is the would-be-left-over item stack + static int l_item_fits(lua_State *L); + + // take_item(self, takecount=1) -> itemstack + static int l_take_item(lua_State *L); + + // peek_item(self, peekcount=1) -> itemstack + static int l_peek_item(lua_State *L); + +public: + LuaItemStack(const ItemStack &item); + ~LuaItemStack() = default; + + const ItemStack& getItem() const; + ItemStack& getItem(); + + // LuaItemStack(itemstack or itemstring or table or nil) + // Creates an LuaItemStack and leaves it on top of stack + static int create_object(lua_State *L); + // Not callable from Lua + static int create(lua_State *L, const ItemStack &item); + static LuaItemStack* checkobject(lua_State *L, int narg); + static void Register(lua_State *L); + +}; + +class ModApiItemMod : public ModApiBase { +private: + static int l_register_item_raw(lua_State *L); + static int l_unregister_item_raw(lua_State *L); + static int l_register_alias_raw(lua_State *L); + static int l_get_content_id(lua_State *L); + static int l_get_name_from_content_id(lua_State *L); +public: + static void Initialize(lua_State *L, int top); +}; diff --git a/src/script/lua_api/l_itemstackmeta.cpp b/src/script/lua_api/l_itemstackmeta.cpp new file mode 100644 index 000000000..70f524087 --- /dev/null +++ b/src/script/lua_api/l_itemstackmeta.cpp @@ -0,0 +1,139 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017-8 rubenwardy +Copyright (C) 2017 raymoo + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_itemstackmeta.h" +#include "lua_api/l_internal.h" +#include "common/c_content.h" + +/* + NodeMetaRef +*/ +ItemStackMetaRef* ItemStackMetaRef::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + + return *(ItemStackMetaRef**)ud; // unbox pointer +} + +Metadata* ItemStackMetaRef::getmeta(bool auto_create) +{ + return &istack->metadata; +} + +void ItemStackMetaRef::clearMeta() +{ + istack->metadata.clear(); +} + +void ItemStackMetaRef::reportMetadataChange(const std::string *name) +{ + // TODO +} + +// Exported functions +int ItemStackMetaRef::l_set_tool_capabilities(lua_State *L) +{ + ItemStackMetaRef *metaref = checkobject(L, 1); + if (lua_isnoneornil(L, 2)) { + metaref->clearToolCapabilities(); + } else if (lua_istable(L, 2)) { + ToolCapabilities caps = read_tool_capabilities(L, 2); + metaref->setToolCapabilities(caps); + } else { + luaL_typerror(L, 2, "table or nil"); + } + + return 0; +} + +// garbage collector +int ItemStackMetaRef::gc_object(lua_State *L) { + ItemStackMetaRef *o = *(ItemStackMetaRef **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +// Creates an NodeMetaRef and leaves it on top of stack +// Not callable from Lua; all references are created on the C side. +void ItemStackMetaRef::create(lua_State *L, ItemStack *istack) +{ + ItemStackMetaRef *o = new ItemStackMetaRef(istack); + //infostream<<"NodeMetaRef::create: o="< +Copyright (C) 2017-8 rubenwardy +Copyright (C) 2017 raymoo + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" +#include "lua_api/l_metadata.h" +#include "irrlichttypes_bloated.h" +#include "inventory.h" + +class ItemStackMetaRef : public MetaDataRef +{ +private: + ItemStack *istack = nullptr; + + static const char className[]; + static const luaL_Reg methods[]; + + static ItemStackMetaRef *checkobject(lua_State *L, int narg); + + virtual Metadata* getmeta(bool auto_create); + + virtual void clearMeta(); + + virtual void reportMetadataChange(const std::string *name = nullptr); + + void setToolCapabilities(const ToolCapabilities &caps) + { + istack->metadata.setToolCapabilities(caps); + } + + void clearToolCapabilities() + { + istack->metadata.clearToolCapabilities(); + } + + // Exported functions + static int l_set_tool_capabilities(lua_State *L); + + // garbage collector + static int gc_object(lua_State *L); +public: + ItemStackMetaRef(ItemStack *istack): istack(istack) {} + ~ItemStackMetaRef() = default; + + // Creates an ItemStackMetaRef and leaves it on top of stack + // Not callable from Lua; all references are created on the C side. + static void create(lua_State *L, ItemStack *istack); + + static void Register(lua_State *L); +}; diff --git a/src/script/lua_api/l_localplayer.cpp b/src/script/lua_api/l_localplayer.cpp new file mode 100644 index 000000000..b1e50df1f --- /dev/null +++ b/src/script/lua_api/l_localplayer.cpp @@ -0,0 +1,638 @@ +/* +Minetest +Copyright (C) 2017 Dumbeldor, Vincent Glize + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "l_clientobject.h" +#include "l_localplayer.h" +#include "l_internal.h" +#include "lua_api/l_item.h" +#include "script/common/c_converter.h" +#include "client/localplayer.h" +#include "hud.h" +#include "common/c_content.h" +#include "client/client.h" +#include "client/content_cao.h" +#include "client/game.h" +#include "l_clientobject.h" +#include + +LuaLocalPlayer::LuaLocalPlayer(LocalPlayer *m) : m_localplayer(m) +{ +} + +void LuaLocalPlayer::create(lua_State *L, LocalPlayer *m) +{ + lua_getglobal(L, "core"); + luaL_checktype(L, -1, LUA_TTABLE); + int objectstable = lua_gettop(L); + lua_getfield(L, -1, "localplayer"); + + // Duplication check + if (lua_type(L, -1) == LUA_TUSERDATA) { + lua_pop(L, 1); + return; + } + + LuaLocalPlayer *o = new LuaLocalPlayer(m); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + + lua_pushvalue(L, lua_gettop(L)); + lua_setfield(L, objectstable, "localplayer"); +} + +int LuaLocalPlayer::l_get_velocity(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + push_v3f(L, player->getSpeed() / BS); + return 1; +} + +int LuaLocalPlayer::l_set_velocity(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + v3f pos = checkFloatPos(L, 2); + player->setSpeed(pos); + + return 0; +} + +int LuaLocalPlayer::l_get_yaw(lua_State *L) +{ + lua_pushnumber(L, wrapDegrees_0_360(g_game->cam_view.camera_yaw)); + return 1; +} + +int LuaLocalPlayer::l_get_pitch(lua_State *L) +{ + lua_pushnumber(L, -wrapDegrees_180(g_game->cam_view.camera_pitch) ); + return 1; +} + +int LuaLocalPlayer::l_get_hp(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushinteger(L, player->hp); + return 1; +} + +int LuaLocalPlayer::l_get_name(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushstring(L, player->getName()); + return 1; +} + +// get_wield_index(self) +int LuaLocalPlayer::l_get_wield_index(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushinteger(L, player->getWieldIndex() + 1); + return 1; +} + +// set_wield_index(self) +int LuaLocalPlayer::l_set_wield_index(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + u32 index = luaL_checkinteger(L, 2) - 1; + + player->setWieldIndex(index); + g_game->processItemSelection(&g_game->runData.new_playeritem); + ItemStack selected_item, hand_item; + ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item); + g_game->camera->wield(tool_item); + return 0; +} + +// get_wielded_item(self) +int LuaLocalPlayer::l_get_wielded_item(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + ItemStack selected_item; + player->getWieldedItem(&selected_item, nullptr); + LuaItemStack::create(L, selected_item); + return 1; +} + +int LuaLocalPlayer::l_is_attached(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushboolean(L, player->getParent() != nullptr); + return 1; +} + +int LuaLocalPlayer::l_is_touching_ground(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushboolean(L, player->touching_ground); + return 1; +} + +int LuaLocalPlayer::l_is_in_liquid(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushboolean(L, player->in_liquid); + return 1; +} + +int LuaLocalPlayer::l_is_in_liquid_stable(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushboolean(L, player->in_liquid_stable); + return 1; +} + +int LuaLocalPlayer::l_get_liquid_viscosity(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushinteger(L, player->liquid_viscosity); + return 1; +} + +int LuaLocalPlayer::l_is_climbing(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushboolean(L, player->is_climbing); + return 1; +} + +int LuaLocalPlayer::l_swimming_vertical(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushboolean(L, player->swimming_vertical); + return 1; +} + +// get_physics_override(self) +int LuaLocalPlayer::l_get_physics_override(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + push_physics_override(L, player->physics_override_speed, player->physics_override_jump, player->physics_override_gravity, player->physics_override_sneak, player->physics_override_sneak_glitch, player->physics_override_new_move); + + return 1; +} + +// set_physics_override(self, override) +int LuaLocalPlayer::l_set_physics_override(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + player->physics_override_speed = getfloatfield_default( + L, 2, "speed", player->physics_override_speed); + player->physics_override_jump = getfloatfield_default( + L, 2, "jump", player->physics_override_jump); + player->physics_override_gravity = getfloatfield_default( + L, 2, "gravity", player->physics_override_gravity); + player->physics_override_sneak = getboolfield_default( + L, 2, "sneak", player->physics_override_sneak); + player->physics_override_sneak_glitch = getboolfield_default( + L, 2, "sneak_glitch", player->physics_override_sneak_glitch); + player->physics_override_new_move = getboolfield_default( + L, 2, "new_move", player->physics_override_new_move); + + return 0; +} + +int LuaLocalPlayer::l_get_last_pos(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + push_v3f(L, player->last_position / BS); + return 1; +} + +int LuaLocalPlayer::l_get_last_velocity(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + push_v3f(L, player->last_speed); + return 1; +} + +int LuaLocalPlayer::l_get_last_look_vertical(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushnumber(L, -1.0 * player->last_pitch * core::DEGTORAD); + return 1; +} + +int LuaLocalPlayer::l_get_last_look_horizontal(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushnumber(L, (player->last_yaw + 90.) * core::DEGTORAD); + return 1; +} + +// get_control(self) +int LuaLocalPlayer::l_get_control(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + const PlayerControl &c = player->getPlayerControl(); + + auto set = [L] (const char *name, bool value) { + lua_pushboolean(L, value); + lua_setfield(L, -2, name); + }; + + lua_createtable(L, 0, 12); + set("up", c.up); + set("down", c.down); + set("left", c.left); + set("right", c.right); + set("jump", c.jump); + set("aux1", c.aux1); + set("sneak", c.sneak); + set("zoom", c.zoom); + set("dig", c.dig); + set("place", c.place); + + return 1; +} + +// get_breath(self) +int LuaLocalPlayer::l_get_breath(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushinteger(L, player->getBreath()); + return 1; +} + +// get_pos(self) +int LuaLocalPlayer::l_get_pos(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + push_v3f(L, player->getPosition() / BS); + return 1; +} + +// set_pos(self, pos) +int LuaLocalPlayer::l_set_pos(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + v3f pos = checkFloatPos(L, 2); + player->setPosition(pos); + getClient(L)->sendPlayerPos(true); + return 0; +} + +int LuaLocalPlayer::l_set_yaw(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + f32 p = (float) luaL_checknumber(L, 2); + //* 0.01745329252f; + g_game->cam_view.camera_yaw = p; + g_game->cam_view_target.camera_yaw = p; + player->setYaw(p); + return 0; +} + +int LuaLocalPlayer::l_set_pitch(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + f32 p = (float) luaL_checknumber(L, 2); + //* 0.01745329252f ; + g_game->cam_view.camera_pitch = p; + g_game->cam_view_target.camera_pitch = p; + player->setPitch(p); + return 0; +} + +// get_movement_acceleration(self) +int LuaLocalPlayer::l_get_movement_acceleration(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_newtable(L); + lua_pushnumber(L, player->movement_acceleration_default); + lua_setfield(L, -2, "default"); + + lua_pushnumber(L, player->movement_acceleration_air); + lua_setfield(L, -2, "air"); + + lua_pushnumber(L, player->movement_acceleration_fast); + lua_setfield(L, -2, "fast"); + + return 1; +} + +// get_movement_speed(self) +int LuaLocalPlayer::l_get_movement_speed(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_newtable(L); + lua_pushnumber(L, player->movement_speed_walk); + lua_setfield(L, -2, "walk"); + + lua_pushnumber(L, player->movement_speed_crouch); + lua_setfield(L, -2, "crouch"); + + lua_pushnumber(L, player->movement_speed_fast); + lua_setfield(L, -2, "fast"); + + lua_pushnumber(L, player->movement_speed_climb); + lua_setfield(L, -2, "climb"); + + lua_pushnumber(L, player->movement_speed_jump); + lua_setfield(L, -2, "jump"); + + return 1; +} + +// get_movement(self) +int LuaLocalPlayer::l_get_movement(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_newtable(L); + + lua_pushnumber(L, player->movement_liquid_fluidity); + lua_setfield(L, -2, "liquid_fluidity"); + + lua_pushnumber(L, player->movement_liquid_fluidity_smooth); + lua_setfield(L, -2, "liquid_fluidity_smooth"); + + lua_pushnumber(L, player->movement_liquid_sink); + lua_setfield(L, -2, "liquid_sink"); + + lua_pushnumber(L, player->movement_gravity); + lua_setfield(L, -2, "gravity"); + + return 1; +} + +// get_armor_groups(self) +int LuaLocalPlayer::l_get_armor_groups(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + push_groups(L, player->getCAO()->getGroups()); + return 1; +} + +// hud_add(self, form) +int LuaLocalPlayer::l_hud_add(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + HudElement *elem = new HudElement; + read_hud_element(L, elem); + + u32 id = player->addHud(elem); + if (id == U32_MAX) { + delete elem; + return 0; + } + lua_pushnumber(L, id); + return 1; +} + +// hud_remove(self, id) +int LuaLocalPlayer::l_hud_remove(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + u32 id = luaL_checkinteger(L, 2); + HudElement *element = player->removeHud(id); + if (!element) + lua_pushboolean(L, false); + else + lua_pushboolean(L, true); + delete element; + return 1; +} + +// hud_change(self, id, stat, data) +int LuaLocalPlayer::l_hud_change(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + u32 id = luaL_checkinteger(L, 2); + + HudElement *element = player->getHud(id); + if (!element) + return 0; + + void *unused; + read_hud_change(L, element, &unused); + + lua_pushboolean(L, true); + return 1; +} + +// hud_get(self, id) +int LuaLocalPlayer::l_hud_get(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + u32 id = luaL_checkinteger(L, -1); + + HudElement *e = player->getHud(id); + if (!e) { + lua_pushnil(L); + return 1; + } + + push_hud_element(L, e); + return 1; +} + +// get_nearby_objects(self, radius) +int LuaLocalPlayer::l_get_nearby_objects(lua_State *L) +{ + // should this be a double? + float radius = readParam(L, 1) * BS; + std::vector objs; + + ClientEnvironment &env = getClient(L)->getEnv(); + v3f pos = env.getLocalPlayer()->getPosition(); + env.getActiveObjects(pos, radius, objs); + + lua_newtable(L); + + int i = 0; + lua_createtable(L, objs.size(), 0); + for (const auto obj : objs) { + ClientObjectRef::create(L, obj.obj); + lua_rawseti(L, -2, ++i); + } + + return 1; +} + +int LuaLocalPlayer::l_get_object(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + ClientEnvironment &env = getClient(L)->getEnv(); + ClientActiveObject *obj = env.getGenericCAO(player->getCAO()->getId()); + + ClientObjectRef::create(L, obj); + + return 1; +} + +LuaLocalPlayer *LuaLocalPlayer::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + + return *(LuaLocalPlayer **)ud; +} + +LocalPlayer *LuaLocalPlayer::getobject(LuaLocalPlayer *ref) +{ + return ref->m_localplayer; +} + +LocalPlayer *LuaLocalPlayer::getobject(lua_State *L, int narg) +{ + LuaLocalPlayer *ref = checkobject(L, narg); + assert(ref); + LocalPlayer *player = getobject(ref); + assert(player); + return player; +} + +int LuaLocalPlayer::l_set_override_speed(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + f32 s = (float) luaL_checknumber(L, 2); + g_settings->setBool("movement_ignore_server_speed",true); + g_settings->setFloat("movement_speed_walk",s); + player->movement_speed_walk = g_settings->getFloat("movement_speed_walk") * BS; + return 0; +} + +int LuaLocalPlayer::l_set_speeds_from_local_settings(lua_State *L) +{ + g_settings->setBool("movement_ignore_server_speed",true); + getClient(L)->set_speeds_from_local_settings(); + return 0; +} +int LuaLocalPlayer::l_set_speeds_from_server_settings(lua_State *L) +{ + g_settings->setBool("movement_ignore_server_speed",false); + getClient(L)->set_speeds_from_server_settings(); + return 0; +} + +int LuaLocalPlayer::gc_object(lua_State *L) +{ + LuaLocalPlayer *o = *(LuaLocalPlayer **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +void LuaLocalPlayer::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // Drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // Drop methodtable +} + +const char LuaLocalPlayer::className[] = "LocalPlayer"; +const luaL_Reg LuaLocalPlayer::methods[] = { + luamethod(LuaLocalPlayer, get_velocity), + luamethod(LuaLocalPlayer, set_velocity), + luamethod(LuaLocalPlayer, get_hp), + luamethod(LuaLocalPlayer, get_name), + luamethod(LuaLocalPlayer, get_wield_index), + luamethod(LuaLocalPlayer, set_wield_index), + luamethod(LuaLocalPlayer, get_wielded_item), + luamethod(LuaLocalPlayer, is_attached), + luamethod(LuaLocalPlayer, is_touching_ground), + luamethod(LuaLocalPlayer, is_in_liquid), + luamethod(LuaLocalPlayer, is_in_liquid_stable), + luamethod(LuaLocalPlayer, get_liquid_viscosity), + luamethod(LuaLocalPlayer, is_climbing), + luamethod(LuaLocalPlayer, swimming_vertical), + luamethod(LuaLocalPlayer, get_physics_override), + luamethod(LuaLocalPlayer, set_physics_override), + // TODO: figure our if these are useful in any way + luamethod(LuaLocalPlayer, get_last_pos), + luamethod(LuaLocalPlayer, get_last_velocity), + luamethod(LuaLocalPlayer, get_last_look_horizontal), + luamethod(LuaLocalPlayer, get_last_look_vertical), + // + luamethod(LuaLocalPlayer, get_control), + luamethod(LuaLocalPlayer, get_breath), + luamethod(LuaLocalPlayer, get_pos), + luamethod(LuaLocalPlayer, set_pos), + luamethod(LuaLocalPlayer, get_yaw), + luamethod(LuaLocalPlayer, set_yaw), + luamethod(LuaLocalPlayer, get_pitch), + luamethod(LuaLocalPlayer, set_pitch), + luamethod(LuaLocalPlayer, get_movement_acceleration), + luamethod(LuaLocalPlayer, get_movement_speed), + luamethod(LuaLocalPlayer, get_movement), + luamethod(LuaLocalPlayer, get_armor_groups), + luamethod(LuaLocalPlayer, hud_add), + luamethod(LuaLocalPlayer, hud_remove), + luamethod(LuaLocalPlayer, hud_change), + luamethod(LuaLocalPlayer, hud_get), + luamethod(LuaLocalPlayer, get_object), + + luamethod(LuaLocalPlayer, get_nearby_objects), + luamethod(LuaLocalPlayer, get_object), + luamethod(LuaLocalPlayer, set_override_speed), + luamethod(LuaLocalPlayer, set_speeds_from_server_settings), + luamethod(LuaLocalPlayer, set_speeds_from_local_settings), + + {0, 0} +}; diff --git a/src/script/lua_api/l_localplayer.h b/src/script/lua_api/l_localplayer.h new file mode 100644 index 000000000..67bba5f01 --- /dev/null +++ b/src/script/lua_api/l_localplayer.h @@ -0,0 +1,147 @@ +/* +Minetest +Copyright (C) 2017 Dumbeldor, Vincent Glize + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "l_base.h" + +class LocalPlayer; + +class LuaLocalPlayer : public ModApiBase +{ +private: + static const char className[]; + static const luaL_Reg methods[]; + + // garbage collector + static int gc_object(lua_State *L); + + // get_velocity(self) + static int l_get_velocity(lua_State *L); + + // set_velocity(self, vel) + static int l_set_velocity(lua_State *L); + + // get_hp(self) + static int l_get_hp(lua_State *L); + + // get_name(self) + static int l_get_name(lua_State *L); + + // get_wield_index(self) + static int l_get_wield_index(lua_State *L); + + // set_wield_index(self) + static int l_set_wield_index(lua_State *L); + + // get_wielded_item(self) + static int l_get_wielded_item(lua_State *L); + + static int l_is_attached(lua_State *L); + static int l_is_touching_ground(lua_State *L); + static int l_is_in_liquid(lua_State *L); + static int l_is_in_liquid_stable(lua_State *L); + static int l_get_liquid_viscosity(lua_State *L); + static int l_is_climbing(lua_State *L); + static int l_swimming_vertical(lua_State *L); + + static int l_get_physics_override(lua_State *L); + static int l_set_physics_override(lua_State *L); + + static int l_get_override_pos(lua_State *L); + + static int l_get_last_pos(lua_State *L); + static int l_get_last_velocity(lua_State *L); + static int l_get_last_look_vertical(lua_State *L); + static int l_get_last_look_horizontal(lua_State *L); + + // get_control(self) + static int l_get_control(lua_State *L); + + // get_breath(self) + static int l_get_breath(lua_State *L); + + // get_pos(self) + static int l_get_pos(lua_State *L); + + // set_pos(self, pos) + static int l_set_pos(lua_State *L); + + // get_yaw(self) + static int l_get_yaw(lua_State *L); + + // set_yaw(self, yaw) + static int l_set_yaw(lua_State *L); + + // get_pitch(self) + static int l_get_pitch(lua_State *L); + + // set_pitch(self, pitch) + static int l_set_pitch(lua_State *L); + + // get_movement_acceleration(self) + static int l_get_movement_acceleration(lua_State *L); + + // get_movement_speed(self) + static int l_get_movement_speed(lua_State *L); + + // get_movement(self) + static int l_get_movement(lua_State *L); + + // get_armor_groups(self) + static int l_get_armor_groups(lua_State *L); + + // hud_add(self, id, form) + static int l_hud_add(lua_State *L); + + // hud_rm(self, id) + static int l_hud_remove(lua_State *L); + + // hud_change(self, id, stat, data) + static int l_hud_change(lua_State *L); + // hud_get(self, id) + static int l_hud_get(lua_State *L); + + // get_nearby_objects(self, radius) + static int l_get_nearby_objects(lua_State *L); + + // get_object(self) + static int l_get_object(lua_State *L); + + // set_override_speed + static int l_set_override_speed(lua_State *L); + + static int l_set_speeds_from_local_settings(lua_State *L); + + static int l_set_speeds_from_server_settings(lua_State *L); + + LocalPlayer *m_localplayer = nullptr; + +public: + LuaLocalPlayer(LocalPlayer *m); + ~LuaLocalPlayer() = default; + + static void create(lua_State *L, LocalPlayer *m); + + static LuaLocalPlayer *checkobject(lua_State *L, int narg); + static LocalPlayer *getobject(LuaLocalPlayer *ref); + static LocalPlayer *getobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp new file mode 100644 index 000000000..f486544d8 --- /dev/null +++ b/src/script/lua_api/l_mainmenu.cpp @@ -0,0 +1,1163 @@ +/* +Minetest +Copyright (C) 2013 sapier + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_mainmenu.h" +#include "lua_api/l_internal.h" +#include "common/c_content.h" +#include "cpp_api/s_async.h" +#include "gui/guiEngine.h" +#include "gui/guiMainMenu.h" +#include "gui/guiKeyChangeMenu.h" +#include "gui/guiPathSelectMenu.h" +#include "version.h" +#include "porting.h" +#include "filesys.h" +#include "convert_json.h" +#include "content/content.h" +#include "content/subgames.h" +#include "serverlist.h" +#include "mapgen/mapgen.h" +#include "settings.h" + +#include +#include +#include "client/renderingengine.h" +#include "network/networkprotocol.h" + + +/******************************************************************************/ +std::string ModApiMainMenu::getTextData(lua_State *L, std::string name) +{ + lua_getglobal(L, "gamedata"); + + lua_getfield(L, -1, name.c_str()); + + if(lua_isnil(L, -1)) + return ""; + + return luaL_checkstring(L, -1); +} + +/******************************************************************************/ +int ModApiMainMenu::getIntegerData(lua_State *L, std::string name,bool& valid) +{ + lua_getglobal(L, "gamedata"); + + lua_getfield(L, -1, name.c_str()); + + if(lua_isnil(L, -1)) { + valid = false; + return -1; + } + + valid = true; + return luaL_checkinteger(L, -1); +} + +/******************************************************************************/ +int ModApiMainMenu::getBoolData(lua_State *L, std::string name,bool& valid) +{ + lua_getglobal(L, "gamedata"); + + lua_getfield(L, -1, name.c_str()); + + if(lua_isnil(L, -1)) { + valid = false; + return false; + } + + valid = true; + return readParam(L, -1); +} + +/******************************************************************************/ +int ModApiMainMenu::l_update_formspec(lua_State *L) +{ + GUIEngine* engine = getGuiEngine(L); + sanity_check(engine != NULL); + + if (engine->m_startgame) + return 0; + + //read formspec + std::string formspec(luaL_checkstring(L, 1)); + + if (engine->m_formspecgui != 0) { + engine->m_formspecgui->setForm(formspec); + } + + return 0; +} + +/******************************************************************************/ +int ModApiMainMenu::l_set_formspec_prepend(lua_State *L) +{ + GUIEngine *engine = getGuiEngine(L); + sanity_check(engine != NULL); + + if (engine->m_startgame) + return 0; + + std::string formspec(luaL_checkstring(L, 1)); + engine->setFormspecPrepend(formspec); + + return 0; +} + +/******************************************************************************/ +int ModApiMainMenu::l_start(lua_State *L) +{ + GUIEngine* engine = getGuiEngine(L); + sanity_check(engine != NULL); + + //update c++ gamedata from lua table + + bool valid = false; + + MainMenuData *data = engine->m_data; + + data->selected_world = getIntegerData(L, "selected_world",valid) -1; + data->simple_singleplayer_mode = getBoolData(L,"singleplayer",valid); + data->do_reconnect = getBoolData(L, "do_reconnect", valid); + if (!data->do_reconnect) { + data->name = getTextData(L,"playername"); + data->password = getTextData(L,"password"); + data->address = getTextData(L,"address"); + data->port = getTextData(L,"port"); + } + data->serverdescription = getTextData(L,"serverdescription"); + data->servername = getTextData(L,"servername"); + + //close menu next time + engine->m_startgame = true; + return 0; +} + +/******************************************************************************/ +int ModApiMainMenu::l_close(lua_State *L) +{ + GUIEngine* engine = getGuiEngine(L); + sanity_check(engine != NULL); + + engine->m_kill = true; + return 0; +} + +/******************************************************************************/ +int ModApiMainMenu::l_set_background(lua_State *L) +{ + GUIEngine* engine = getGuiEngine(L); + sanity_check(engine != NULL); + + std::string backgroundlevel(luaL_checkstring(L, 1)); + std::string texturename(luaL_checkstring(L, 2)); + + bool tile_image = false; + bool retval = false; + unsigned int minsize = 16; + + if (!lua_isnone(L, 3)) { + tile_image = readParam(L, 3); + } + + if (!lua_isnone(L, 4)) { + minsize = lua_tonumber(L, 4); + } + + if (backgroundlevel == "background") { + retval |= engine->setTexture(TEX_LAYER_BACKGROUND, texturename, + tile_image, minsize); + } + + if (backgroundlevel == "overlay") { + retval |= engine->setTexture(TEX_LAYER_OVERLAY, texturename, + tile_image, minsize); + } + + if (backgroundlevel == "header") { + retval |= engine->setTexture(TEX_LAYER_HEADER, texturename, + tile_image, minsize); + } + + if (backgroundlevel == "footer") { + retval |= engine->setTexture(TEX_LAYER_FOOTER, texturename, + tile_image, minsize); + } + + lua_pushboolean(L,retval); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_set_clouds(lua_State *L) +{ + GUIEngine* engine = getGuiEngine(L); + sanity_check(engine != NULL); + + bool value = readParam(L,1); + + engine->m_clouds_enabled = value; + + return 0; +} + +/******************************************************************************/ +int ModApiMainMenu::l_get_textlist_index(lua_State *L) +{ + // get_table_index accepts both tables and textlists + return l_get_table_index(L); +} + +/******************************************************************************/ +int ModApiMainMenu::l_get_table_index(lua_State *L) +{ + GUIEngine* engine = getGuiEngine(L); + sanity_check(engine != NULL); + + std::string tablename(luaL_checkstring(L, 1)); + GUITable *table = engine->m_menu->getTable(tablename); + s32 selection = table ? table->getSelected() : 0; + + if (selection >= 1) + lua_pushinteger(L, selection); + else + lua_pushnil(L); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_get_worlds(lua_State *L) +{ + std::vector worlds = getAvailableWorlds(); + + lua_newtable(L); + int top = lua_gettop(L); + unsigned int index = 1; + + for (const WorldSpec &world : worlds) { + lua_pushnumber(L,index); + + lua_newtable(L); + int top_lvl2 = lua_gettop(L); + + lua_pushstring(L,"path"); + lua_pushstring(L, world.path.c_str()); + lua_settable(L, top_lvl2); + + lua_pushstring(L,"name"); + lua_pushstring(L, world.name.c_str()); + lua_settable(L, top_lvl2); + + lua_pushstring(L,"gameid"); + lua_pushstring(L, world.gameid.c_str()); + lua_settable(L, top_lvl2); + + lua_settable(L, top); + index++; + } + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_get_favorites(lua_State *L) +{ + std::string listtype = "local"; + + if (!lua_isnone(L, 1)) { + listtype = luaL_checkstring(L, 1); + } + + std::vector servers; + + if(listtype == "online") { + servers = ServerList::getOnline(); + } else { + servers = ServerList::getLocal(); + } + + lua_newtable(L); + int top = lua_gettop(L); + unsigned int index = 1; + + for (const Json::Value &server : servers) { + + lua_pushnumber(L, index); + + lua_newtable(L); + int top_lvl2 = lua_gettop(L); + + if (!server["clients"].asString().empty()) { + std::string clients_raw = server["clients"].asString(); + char* endptr = 0; + int numbervalue = strtol(clients_raw.c_str(), &endptr,10); + + if ((!clients_raw.empty()) && (*endptr == 0)) { + lua_pushstring(L, "clients"); + lua_pushnumber(L, numbervalue); + lua_settable(L, top_lvl2); + } + } + + if (!server["clients_max"].asString().empty()) { + + std::string clients_max_raw = server["clients_max"].asString(); + char* endptr = 0; + int numbervalue = strtol(clients_max_raw.c_str(), &endptr,10); + + if ((!clients_max_raw.empty()) && (*endptr == 0)) { + lua_pushstring(L, "clients_max"); + lua_pushnumber(L, numbervalue); + lua_settable(L, top_lvl2); + } + } + + if (!server["version"].asString().empty()) { + lua_pushstring(L, "version"); + std::string topush = server["version"].asString(); + lua_pushstring(L, topush.c_str()); + lua_settable(L, top_lvl2); + } + + if (!server["proto_min"].asString().empty()) { + lua_pushstring(L, "proto_min"); + lua_pushinteger(L, server["proto_min"].asInt()); + lua_settable(L, top_lvl2); + } + + if (!server["proto_max"].asString().empty()) { + lua_pushstring(L, "proto_max"); + lua_pushinteger(L, server["proto_max"].asInt()); + lua_settable(L, top_lvl2); + } + + if (!server["password"].asString().empty()) { + lua_pushstring(L, "password"); + lua_pushboolean(L, server["password"].asBool()); + lua_settable(L, top_lvl2); + } + + if (!server["creative"].asString().empty()) { + lua_pushstring(L, "creative"); + lua_pushboolean(L, server["creative"].asBool()); + lua_settable(L, top_lvl2); + } + + if (!server["damage"].asString().empty()) { + lua_pushstring(L, "damage"); + lua_pushboolean(L, server["damage"].asBool()); + lua_settable(L, top_lvl2); + } + + if (!server["pvp"].asString().empty()) { + lua_pushstring(L, "pvp"); + lua_pushboolean(L, server["pvp"].asBool()); + lua_settable(L, top_lvl2); + } + + if (!server["description"].asString().empty()) { + lua_pushstring(L, "description"); + std::string topush = server["description"].asString(); + lua_pushstring(L, topush.c_str()); + lua_settable(L, top_lvl2); + } + + if (!server["name"].asString().empty()) { + lua_pushstring(L, "name"); + std::string topush = server["name"].asString(); + lua_pushstring(L, topush.c_str()); + lua_settable(L, top_lvl2); + } + + if (!server["address"].asString().empty()) { + lua_pushstring(L, "address"); + std::string topush = server["address"].asString(); + lua_pushstring(L, topush.c_str()); + lua_settable(L, top_lvl2); + } + + if (!server["port"].asString().empty()) { + lua_pushstring(L, "port"); + std::string topush = server["port"].asString(); + lua_pushstring(L, topush.c_str()); + lua_settable(L, top_lvl2); + } + + if (server.isMember("ping")) { + float ping = server["ping"].asFloat(); + lua_pushstring(L, "ping"); + lua_pushnumber(L, ping); + lua_settable(L, top_lvl2); + } + + if (server["clients_list"].isArray()) { + unsigned int index_lvl2 = 1; + lua_pushstring(L, "clients_list"); + lua_newtable(L); + int top_lvl3 = lua_gettop(L); + for (const Json::Value &client : server["clients_list"]) { + lua_pushnumber(L, index_lvl2); + std::string topush = client.asString(); + lua_pushstring(L, topush.c_str()); + lua_settable(L, top_lvl3); + index_lvl2++; + } + lua_settable(L, top_lvl2); + } + + if (server["mods"].isArray()) { + unsigned int index_lvl2 = 1; + lua_pushstring(L, "mods"); + lua_newtable(L); + int top_lvl3 = lua_gettop(L); + for (const Json::Value &mod : server["mods"]) { + + lua_pushnumber(L, index_lvl2); + std::string topush = mod.asString(); + lua_pushstring(L, topush.c_str()); + lua_settable(L, top_lvl3); + index_lvl2++; + } + lua_settable(L, top_lvl2); + } + + lua_settable(L, top); + index++; + } + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_delete_favorite(lua_State *L) +{ + std::vector servers; + + std::string listtype = "local"; + + if (!lua_isnone(L,2)) { + listtype = luaL_checkstring(L,2); + } + + if ((listtype != "local") && + (listtype != "online")) + return 0; + + + if(listtype == "online") { + servers = ServerList::getOnline(); + } else { + servers = ServerList::getLocal(); + } + + int fav_idx = luaL_checkinteger(L,1) -1; + + if ((fav_idx >= 0) && + (fav_idx < (int) servers.size())) { + + ServerList::deleteEntry(servers[fav_idx]); + } + + return 0; +} + +/******************************************************************************/ +int ModApiMainMenu::l_get_games(lua_State *L) +{ + std::vector games = getAvailableGames(); + + lua_newtable(L); + int top = lua_gettop(L); + unsigned int index = 1; + + for (const SubgameSpec &game : games) { + lua_pushnumber(L, index); + lua_newtable(L); + int top_lvl2 = lua_gettop(L); + + lua_pushstring(L, "id"); + lua_pushstring(L, game.id.c_str()); + lua_settable(L, top_lvl2); + + lua_pushstring(L, "path"); + lua_pushstring(L, game.path.c_str()); + lua_settable(L, top_lvl2); + + lua_pushstring(L, "type"); + lua_pushstring(L, "game"); + lua_settable(L, top_lvl2); + + lua_pushstring(L, "gamemods_path"); + lua_pushstring(L, game.gamemods_path.c_str()); + lua_settable(L, top_lvl2); + + lua_pushstring(L, "name"); + lua_pushstring(L, game.name.c_str()); + lua_settable(L, top_lvl2); + + lua_pushstring(L, "author"); + lua_pushstring(L, game.author.c_str()); + lua_settable(L, top_lvl2); + + lua_pushstring(L, "release"); + lua_pushinteger(L, game.release); + lua_settable(L, top_lvl2); + + lua_pushstring(L, "menuicon_path"); + lua_pushstring(L, game.menuicon_path.c_str()); + lua_settable(L, top_lvl2); + + lua_pushstring(L, "addon_mods_paths"); + lua_newtable(L); + int table2 = lua_gettop(L); + int internal_index = 1; + for (const std::string &addon_mods_path : game.addon_mods_paths) { + lua_pushnumber(L, internal_index); + lua_pushstring(L, addon_mods_path.c_str()); + lua_settable(L, table2); + internal_index++; + } + lua_settable(L, top_lvl2); + lua_settable(L, top); + index++; + } + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_get_content_info(lua_State *L) +{ + std::string path = luaL_checkstring(L, 1); + + ContentSpec spec; + spec.path = path; + parseContentInfo(spec); + + lua_newtable(L); + + lua_pushstring(L, spec.name.c_str()); + lua_setfield(L, -2, "name"); + + lua_pushstring(L, spec.type.c_str()); + lua_setfield(L, -2, "type"); + + lua_pushstring(L, spec.author.c_str()); + lua_setfield(L, -2, "author"); + + lua_pushinteger(L, spec.release); + lua_setfield(L, -2, "release"); + + lua_pushstring(L, spec.desc.c_str()); + lua_setfield(L, -2, "description"); + + lua_pushstring(L, spec.path.c_str()); + lua_setfield(L, -2, "path"); + + if (spec.type == "mod") { + ModSpec spec; + spec.path = path; + parseModContents(spec); + + // Dependencies + lua_newtable(L); + int i = 1; + for (const auto &dep : spec.depends) { + lua_pushstring(L, dep.c_str()); + lua_rawseti(L, -2, i++); + } + lua_setfield(L, -2, "depends"); + + // Optional Dependencies + lua_newtable(L); + i = 1; + for (const auto &dep : spec.optdepends) { + lua_pushstring(L, dep.c_str()); + lua_rawseti(L, -2, i++); + } + lua_setfield(L, -2, "optional_depends"); + } + + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_show_keys_menu(lua_State *L) +{ + GUIEngine* engine = getGuiEngine(L); + sanity_check(engine != NULL); + + GUIKeyChangeMenu *kmenu = new GUIKeyChangeMenu(RenderingEngine::get_gui_env(), + engine->m_parent, + -1, + engine->m_menumanager, + engine->m_texture_source); + kmenu->drop(); + return 0; +} + +/******************************************************************************/ +int ModApiMainMenu::l_create_world(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + int gameidx = luaL_checkinteger(L,2) -1; + + std::string path = porting::path_user + DIR_DELIM + "worlds" + DIR_DELIM + + sanitizeDirName(name, "world_"); + + std::vector games = getAvailableGames(); + + if ((gameidx >= 0) && + (gameidx < (int) games.size())) { + + // Create world if it doesn't exist + try { + loadGameConfAndInitWorld(path, name, games[gameidx], true); + lua_pushnil(L); + } catch (const BaseException &e) { + lua_pushstring(L, (std::string("Failed to initialize world: ") + e.what()).c_str()); + } + } else { + lua_pushstring(L, "Invalid game index"); + } + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_delete_world(lua_State *L) +{ + int world_id = luaL_checkinteger(L, 1) - 1; + std::vector worlds = getAvailableWorlds(); + if (world_id < 0 || world_id >= (int) worlds.size()) { + lua_pushstring(L, "Invalid world index"); + return 1; + } + const WorldSpec &spec = worlds[world_id]; + if (!fs::RecursiveDelete(spec.path)) { + lua_pushstring(L, "Failed to delete world"); + return 1; + } + return 0; +} + +/******************************************************************************/ +int ModApiMainMenu::l_set_topleft_text(lua_State *L) +{ + GUIEngine* engine = getGuiEngine(L); + sanity_check(engine != NULL); + + std::string text; + + if (!lua_isnone(L,1) && !lua_isnil(L,1)) + text = luaL_checkstring(L, 1); + + engine->setTopleftText(text); + return 0; +} + +/******************************************************************************/ +int ModApiMainMenu::l_get_mapgen_names(lua_State *L) +{ + std::vector names; + bool include_hidden = lua_isboolean(L, 1) && readParam(L, 1); + Mapgen::getMapgenNames(&names, include_hidden); + + lua_newtable(L); + for (size_t i = 0; i != names.size(); i++) { + lua_pushstring(L, names[i]); + lua_rawseti(L, -2, i + 1); + } + + return 1; +} + + +/******************************************************************************/ +int ModApiMainMenu::l_get_modpath(lua_State *L) +{ + std::string modpath = fs::RemoveRelativePathComponents( + porting::path_user + DIR_DELIM + "mods" + DIR_DELIM); + lua_pushstring(L, modpath.c_str()); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_get_clientmodpath(lua_State *L) +{ + std::string modpath = fs::RemoveRelativePathComponents( + porting::path_user + DIR_DELIM + "clientmods" + DIR_DELIM); + lua_pushstring(L, modpath.c_str()); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_get_gamepath(lua_State *L) +{ + std::string gamepath = fs::RemoveRelativePathComponents( + porting::path_user + DIR_DELIM + "games" + DIR_DELIM); + lua_pushstring(L, gamepath.c_str()); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_get_texturepath(lua_State *L) +{ + std::string gamepath = fs::RemoveRelativePathComponents( + porting::path_user + DIR_DELIM + "textures"); + lua_pushstring(L, gamepath.c_str()); + return 1; +} + +int ModApiMainMenu::l_get_texturepath_share(lua_State *L) +{ + std::string gamepath = fs::RemoveRelativePathComponents( + porting::path_share + DIR_DELIM + "textures"); + lua_pushstring(L, gamepath.c_str()); + return 1; +} + +int ModApiMainMenu::l_get_cache_path(lua_State *L) +{ + lua_pushstring(L, fs::RemoveRelativePathComponents(porting::path_cache).c_str()); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_create_dir(lua_State *L) { + const char *path = luaL_checkstring(L, 1); + + if (ModApiMainMenu::mayModifyPath(path)) { + lua_pushboolean(L, fs::CreateAllDirs(path)); + return 1; + } + + lua_pushboolean(L, false); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_delete_dir(lua_State *L) +{ + const char *path = luaL_checkstring(L, 1); + + std::string absolute_path = fs::RemoveRelativePathComponents(path); + + if (ModApiMainMenu::mayModifyPath(absolute_path)) { + lua_pushboolean(L, fs::RecursiveDelete(absolute_path)); + return 1; + } + + lua_pushboolean(L, false); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_copy_dir(lua_State *L) +{ + const char *source = luaL_checkstring(L, 1); + const char *destination = luaL_checkstring(L, 2); + + bool keep_source = true; + + if ((!lua_isnone(L,3)) && + (!lua_isnil(L,3))) { + keep_source = readParam(L,3); + } + + std::string absolute_destination = fs::RemoveRelativePathComponents(destination); + std::string absolute_source = fs::RemoveRelativePathComponents(source); + + if ((ModApiMainMenu::mayModifyPath(absolute_destination))) { + bool retval = fs::CopyDir(absolute_source,absolute_destination); + + if (retval && (!keep_source)) { + + retval &= fs::RecursiveDelete(absolute_source); + } + lua_pushboolean(L,retval); + return 1; + } + lua_pushboolean(L,false); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_extract_zip(lua_State *L) +{ + const char *zipfile = luaL_checkstring(L, 1); + const char *destination = luaL_checkstring(L, 2); + + std::string absolute_destination = fs::RemoveRelativePathComponents(destination); + + if (ModApiMainMenu::mayModifyPath(absolute_destination)) { + fs::CreateAllDirs(absolute_destination); + + io::IFileSystem *fs = RenderingEngine::get_filesystem(); + + if (!fs->addFileArchive(zipfile, false, false, io::EFAT_ZIP)) { + lua_pushboolean(L,false); + return 1; + } + + sanity_check(fs->getFileArchiveCount() > 0); + + /**********************************************************************/ + /* WARNING this is not threadsafe!! */ + /**********************************************************************/ + io::IFileArchive* opened_zip = + fs->getFileArchive(fs->getFileArchiveCount()-1); + + const io::IFileList* files_in_zip = opened_zip->getFileList(); + + unsigned int number_of_files = files_in_zip->getFileCount(); + + for (unsigned int i=0; i < number_of_files; i++) { + std::string fullpath = destination; + fullpath += DIR_DELIM; + fullpath += files_in_zip->getFullFileName(i).c_str(); + std::string fullpath_dir = fs::RemoveLastPathComponent(fullpath); + + if (!files_in_zip->isDirectory(i)) { + if (!fs::PathExists(fullpath_dir) && !fs::CreateAllDirs(fullpath_dir)) { + fs->removeFileArchive(fs->getFileArchiveCount()-1); + lua_pushboolean(L,false); + return 1; + } + + io::IReadFile* toread = opened_zip->createAndOpenFile(i); + + FILE *targetfile = fopen(fullpath.c_str(),"wb"); + + if (targetfile == NULL) { + fs->removeFileArchive(fs->getFileArchiveCount()-1); + lua_pushboolean(L,false); + return 1; + } + + char read_buffer[1024]; + long total_read = 0; + + while (total_read < toread->getSize()) { + + unsigned int bytes_read = + toread->read(read_buffer,sizeof(read_buffer)); + if ((bytes_read == 0 ) || + (fwrite(read_buffer, 1, bytes_read, targetfile) != bytes_read)) + { + fclose(targetfile); + fs->removeFileArchive(fs->getFileArchiveCount()-1); + lua_pushboolean(L,false); + return 1; + } + total_read += bytes_read; + } + + fclose(targetfile); + } + + } + + fs->removeFileArchive(fs->getFileArchiveCount()-1); + lua_pushboolean(L,true); + return 1; + } + + lua_pushboolean(L,false); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_get_mainmenu_path(lua_State *L) +{ + GUIEngine* engine = getGuiEngine(L); + sanity_check(engine != NULL); + + lua_pushstring(L,engine->getScriptDir().c_str()); + return 1; +} + +/******************************************************************************/ +bool ModApiMainMenu::mayModifyPath(const std::string &path) +{ + if (fs::PathStartsWith(path, fs::TempPath())) + return true; + + if (fs::PathStartsWith(path, fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM "games"))) + return true; + + if (fs::PathStartsWith(path, fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM "mods"))) + return true; + + if (fs::PathStartsWith(path, fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM "clientmods"))) + return true; + + if (fs::PathStartsWith(path, fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM "textures"))) + return true; + + if (fs::PathStartsWith(path, fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM "worlds"))) + return true; + + if (fs::PathStartsWith(path, fs::RemoveRelativePathComponents(porting::path_cache))) + return true; + + return false; +} + + +/******************************************************************************/ +int ModApiMainMenu::l_may_modify_path(lua_State *L) +{ + const char *target = luaL_checkstring(L, 1); + std::string absolute_destination = fs::RemoveRelativePathComponents(target); + lua_pushboolean(L, ModApiMainMenu::mayModifyPath(absolute_destination)); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_show_path_select_dialog(lua_State *L) +{ + GUIEngine* engine = getGuiEngine(L); + sanity_check(engine != NULL); + + const char *formname= luaL_checkstring(L, 1); + const char *title = luaL_checkstring(L, 2); + bool is_file_select = readParam(L, 3); + + GUIFileSelectMenu* fileOpenMenu = + new GUIFileSelectMenu(RenderingEngine::get_gui_env(), + engine->m_parent, + -1, + engine->m_menumanager, + title, + formname, + is_file_select); + fileOpenMenu->setTextDest(engine->m_buttonhandler); + fileOpenMenu->drop(); + return 0; +} + +/******************************************************************************/ +int ModApiMainMenu::l_download_file(lua_State *L) +{ + const char *url = luaL_checkstring(L, 1); + const char *target = luaL_checkstring(L, 2); + + //check path + std::string absolute_destination = fs::RemoveRelativePathComponents(target); + + if (ModApiMainMenu::mayModifyPath(absolute_destination)) { + if (GUIEngine::downloadFile(url,absolute_destination)) { + lua_pushboolean(L,true); + return 1; + } + } else { + errorstream << "DOWNLOAD denied: " << absolute_destination + << " isn't a allowed path" << std::endl; + } + lua_pushboolean(L,false); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_get_video_drivers(lua_State *L) +{ + std::vector drivers = RenderingEngine::getSupportedVideoDrivers(); + + lua_newtable(L); + for (u32 i = 0; i != drivers.size(); i++) { + const char *name = RenderingEngine::getVideoDriverName(drivers[i]); + const char *fname = RenderingEngine::getVideoDriverFriendlyName(drivers[i]); + + lua_newtable(L); + lua_pushstring(L, name); + lua_setfield(L, -2, "name"); + lua_pushstring(L, fname); + lua_setfield(L, -2, "friendly_name"); + + lua_rawseti(L, -2, i + 1); + } + + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_get_video_modes(lua_State *L) +{ + std::vector > videomodes + = RenderingEngine::getSupportedVideoModes(); + + lua_newtable(L); + for (u32 i = 0; i != videomodes.size(); i++) { + lua_newtable(L); + lua_pushnumber(L, videomodes[i].X); + lua_setfield(L, -2, "w"); + lua_pushnumber(L, videomodes[i].Y); + lua_setfield(L, -2, "h"); + lua_pushnumber(L, videomodes[i].Z); + lua_setfield(L, -2, "depth"); + + lua_rawseti(L, -2, i + 1); + } + + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_gettext(lua_State *L) +{ + std::string text = strgettext(std::string(luaL_checkstring(L, 1))); + lua_pushstring(L, text.c_str()); + + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_get_screen_info(lua_State *L) +{ + lua_newtable(L); + int top = lua_gettop(L); + lua_pushstring(L,"density"); + lua_pushnumber(L,RenderingEngine::getDisplayDensity()); + lua_settable(L, top); + + lua_pushstring(L,"display_width"); + lua_pushnumber(L,RenderingEngine::getDisplaySize().X); + lua_settable(L, top); + + lua_pushstring(L,"display_height"); + lua_pushnumber(L,RenderingEngine::getDisplaySize().Y); + lua_settable(L, top); + + const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize(); + lua_pushstring(L,"window_width"); + lua_pushnumber(L, window_size.X); + lua_settable(L, top); + + lua_pushstring(L,"window_height"); + lua_pushnumber(L, window_size.Y); + lua_settable(L, top); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_get_min_supp_proto(lua_State *L) +{ + lua_pushinteger(L, CLIENT_PROTOCOL_VERSION_MIN); + return 1; +} + +int ModApiMainMenu::l_get_max_supp_proto(lua_State *L) +{ + lua_pushinteger(L, CLIENT_PROTOCOL_VERSION_MAX); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_open_url(lua_State *L) +{ + std::string url = luaL_checkstring(L, 1); + lua_pushboolean(L, porting::openURL(url)); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_do_async_callback(lua_State *L) +{ + GUIEngine* engine = getGuiEngine(L); + + size_t func_length, param_length; + const char* serialized_func_raw = luaL_checklstring(L, 1, &func_length); + + const char* serialized_param_raw = luaL_checklstring(L, 2, ¶m_length); + + sanity_check(serialized_func_raw != NULL); + sanity_check(serialized_param_raw != NULL); + + std::string serialized_func = std::string(serialized_func_raw, func_length); + std::string serialized_param = std::string(serialized_param_raw, param_length); + + lua_pushinteger(L, engine->queueAsync(serialized_func, serialized_param)); + + return 1; +} + +/******************************************************************************/ +void ModApiMainMenu::Initialize(lua_State *L, int top) +{ + API_FCT(update_formspec); + API_FCT(set_formspec_prepend); + API_FCT(set_clouds); + API_FCT(get_textlist_index); + API_FCT(get_table_index); + API_FCT(get_worlds); + API_FCT(get_games); + API_FCT(get_content_info); + API_FCT(start); + API_FCT(close); + API_FCT(get_favorites); + API_FCT(show_keys_menu); + API_FCT(create_world); + API_FCT(delete_world); + API_FCT(delete_favorite); + API_FCT(set_background); + API_FCT(set_topleft_text); + API_FCT(get_mapgen_names); + API_FCT(get_modpath); + API_FCT(get_clientmodpath); + API_FCT(get_gamepath); + API_FCT(get_texturepath); + API_FCT(get_texturepath_share); + API_FCT(get_cache_path); + API_FCT(create_dir); + API_FCT(delete_dir); + API_FCT(copy_dir); + API_FCT(extract_zip); + API_FCT(may_modify_path); + API_FCT(get_mainmenu_path); + API_FCT(show_path_select_dialog); + API_FCT(download_file); + API_FCT(gettext); + API_FCT(get_video_drivers); + API_FCT(get_video_modes); + API_FCT(get_screen_info); + API_FCT(get_min_supp_proto); + API_FCT(get_max_supp_proto); + API_FCT(open_url); + API_FCT(do_async_callback); +} + +/******************************************************************************/ +void ModApiMainMenu::InitializeAsync(lua_State *L, int top) +{ + API_FCT(get_worlds); + API_FCT(get_games); + API_FCT(get_favorites); + API_FCT(get_mapgen_names); + API_FCT(get_modpath); + API_FCT(get_clientmodpath); + API_FCT(get_gamepath); + API_FCT(get_texturepath); + API_FCT(get_texturepath_share); + API_FCT(get_cache_path); + API_FCT(create_dir); + API_FCT(delete_dir); + API_FCT(copy_dir); + //API_FCT(extract_zip); //TODO remove dependency to GuiEngine + API_FCT(may_modify_path); + API_FCT(download_file); + //API_FCT(gettext); (gettext lib isn't threadsafe) +} diff --git a/src/script/lua_api/l_mainmenu.h b/src/script/lua_api/l_mainmenu.h new file mode 100644 index 000000000..80f17a660 --- /dev/null +++ b/src/script/lua_api/l_mainmenu.h @@ -0,0 +1,166 @@ +/* +Minetest +Copyright (C) 2013 sapier + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" + +class AsyncEngine; + +/** Implementation of lua api support for mainmenu */ +class ModApiMainMenu: public ModApiBase +{ + +private: + /** + * read a text variable from gamedata table within lua stack + * @param L stack to read variable from + * @param name name of variable to read + * @return string value of requested variable + */ + static std::string getTextData(lua_State *L, std::string name); + + /** + * read a integer variable from gamedata table within lua stack + * @param L stack to read variable from + * @param name name of variable to read + * @return integer value of requested variable + */ + static int getIntegerData(lua_State *L, std::string name,bool& valid); + + /** + * read a bool variable from gamedata table within lua stack + * @param L stack to read variable from + * @param name name of variable to read + * @return bool value of requested variable + */ + static int getBoolData(lua_State *L, std::string name,bool& valid); + + /** + * Checks if a path may be modified. Paths in the temp directory or the user + * games, mods, textures, or worlds directories may be modified. + * @param path path to check + * @return true if the path may be modified + */ + static bool mayModifyPath(const std::string &path); + + //api calls + + static int l_start(lua_State *L); + + static int l_close(lua_State *L); + + static int l_create_world(lua_State *L); + + static int l_delete_world(lua_State *L); + + static int l_get_worlds(lua_State *L); + + static int l_get_mapgen_names(lua_State *L); + + static int l_get_favorites(lua_State *L); + + static int l_delete_favorite(lua_State *L); + + static int l_gettext(lua_State *L); + + //packages + + static int l_get_games(lua_State *L); + + static int l_get_content_info(lua_State *L); + + //gui + + static int l_show_keys_menu(lua_State *L); + + static int l_show_path_select_dialog(lua_State *L); + + static int l_set_topleft_text(lua_State *L); + + static int l_set_clouds(lua_State *L); + + static int l_get_textlist_index(lua_State *L); + + static int l_get_table_index(lua_State *L); + + static int l_set_background(lua_State *L); + + static int l_update_formspec(lua_State *L); + + static int l_set_formspec_prepend(lua_State *L); + + static int l_get_screen_info(lua_State *L); + + //filesystem + + static int l_get_mainmenu_path(lua_State *L); + + static int l_get_modpath(lua_State *L); + + static int l_get_clientmodpath(lua_State *L); + + static int l_get_gamepath(lua_State *L); + + static int l_get_texturepath(lua_State *L); + + static int l_get_texturepath_share(lua_State *L); + + static int l_get_cache_path(lua_State *L); + + static int l_create_dir(lua_State *L); + + static int l_delete_dir(lua_State *L); + + static int l_copy_dir(lua_State *L); + + static int l_extract_zip(lua_State *L); + + static int l_may_modify_path(lua_State *L); + + static int l_download_file(lua_State *L); + + static int l_get_video_drivers(lua_State *L); + + static int l_get_video_modes(lua_State *L); + + //version compatibility + static int l_get_min_supp_proto(lua_State *L); + + static int l_get_max_supp_proto(lua_State *L); + + // other + static int l_open_url(lua_State *L); + + + // async + static int l_do_async_callback(lua_State *L); + +public: + + /** + * initialize this API module + * @param L lua stack to initialize + * @param top index (in lua stack) of global API table + */ + static void Initialize(lua_State *L, int top); + + static void InitializeAsync(lua_State *L, int top); + +}; diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp new file mode 100644 index 000000000..3041cd42f --- /dev/null +++ b/src/script/lua_api/l_mapgen.cpp @@ -0,0 +1,1878 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_mapgen.h" +#include "lua_api/l_internal.h" +#include "lua_api/l_vmanip.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "cpp_api/s_security.h" +#include "util/serialize.h" +#include "server.h" +#include "environment.h" +#include "emerge.h" +#include "mapgen/mg_biome.h" +#include "mapgen/mg_ore.h" +#include "mapgen/mg_decoration.h" +#include "mapgen/mg_schematic.h" +#include "mapgen/mapgen_v5.h" +#include "mapgen/mapgen_v7.h" +#include "filesys.h" +#include "settings.h" +#include "log.h" + +struct EnumString ModApiMapgen::es_BiomeTerrainType[] = +{ + {BIOMETYPE_NORMAL, "normal"}, + {0, NULL}, +}; + +struct EnumString ModApiMapgen::es_DecorationType[] = +{ + {DECO_SIMPLE, "simple"}, + {DECO_SCHEMATIC, "schematic"}, + {DECO_LSYSTEM, "lsystem"}, + {0, NULL}, +}; + +struct EnumString ModApiMapgen::es_MapgenObject[] = +{ + {MGOBJ_VMANIP, "voxelmanip"}, + {MGOBJ_HEIGHTMAP, "heightmap"}, + {MGOBJ_BIOMEMAP, "biomemap"}, + {MGOBJ_HEATMAP, "heatmap"}, + {MGOBJ_HUMIDMAP, "humiditymap"}, + {MGOBJ_GENNOTIFY, "gennotify"}, + {0, NULL}, +}; + +struct EnumString ModApiMapgen::es_OreType[] = +{ + {ORE_SCATTER, "scatter"}, + {ORE_SHEET, "sheet"}, + {ORE_PUFF, "puff"}, + {ORE_BLOB, "blob"}, + {ORE_VEIN, "vein"}, + {ORE_STRATUM, "stratum"}, + {0, NULL}, +}; + +struct EnumString ModApiMapgen::es_Rotation[] = +{ + {ROTATE_0, "0"}, + {ROTATE_90, "90"}, + {ROTATE_180, "180"}, + {ROTATE_270, "270"}, + {ROTATE_RAND, "random"}, + {0, NULL}, +}; + +struct EnumString ModApiMapgen::es_SchematicFormatType[] = +{ + {SCHEM_FMT_HANDLE, "handle"}, + {SCHEM_FMT_MTS, "mts"}, + {SCHEM_FMT_LUA, "lua"}, + {0, NULL}, +}; + +ObjDef *get_objdef(lua_State *L, int index, const ObjDefManager *objmgr); + +Biome *get_or_load_biome(lua_State *L, int index, + BiomeManager *biomemgr); +Biome *read_biome_def(lua_State *L, int index, const NodeDefManager *ndef); +size_t get_biome_list(lua_State *L, int index, + BiomeManager *biomemgr, std::unordered_set *biome_id_list); + +Schematic *get_or_load_schematic(lua_State *L, int index, + SchematicManager *schemmgr, StringMap *replace_names); +Schematic *load_schematic(lua_State *L, int index, const NodeDefManager *ndef, + StringMap *replace_names); +Schematic *load_schematic_from_def(lua_State *L, int index, + const NodeDefManager *ndef, StringMap *replace_names); +bool read_schematic_def(lua_State *L, int index, + Schematic *schem, std::vector *names); + +bool read_deco_simple(lua_State *L, DecoSimple *deco); +bool read_deco_schematic(lua_State *L, SchematicManager *schemmgr, DecoSchematic *deco); + + +/////////////////////////////////////////////////////////////////////////////// + +ObjDef *get_objdef(lua_State *L, int index, const ObjDefManager *objmgr) +{ + if (index < 0) + index = lua_gettop(L) + 1 + index; + + // If a number, assume this is a handle to an object def + if (lua_isnumber(L, index)) + return objmgr->get(lua_tointeger(L, index)); + + // If a string, assume a name is given instead + if (lua_isstring(L, index)) + return objmgr->getByName(lua_tostring(L, index)); + + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +Schematic *get_or_load_schematic(lua_State *L, int index, + SchematicManager *schemmgr, StringMap *replace_names) +{ + if (index < 0) + index = lua_gettop(L) + 1 + index; + + Schematic *schem = (Schematic *)get_objdef(L, index, schemmgr); + if (schem) + return schem; + + schem = load_schematic(L, index, schemmgr->getNodeDef(), + replace_names); + if (!schem) + return NULL; + + if (schemmgr->add(schem) == OBJDEF_INVALID_HANDLE) { + delete schem; + return NULL; + } + + return schem; +} + + +Schematic *load_schematic(lua_State *L, int index, const NodeDefManager *ndef, + StringMap *replace_names) +{ + if (index < 0) + index = lua_gettop(L) + 1 + index; + + Schematic *schem = NULL; + + if (lua_istable(L, index)) { + schem = load_schematic_from_def(L, index, ndef, + replace_names); + if (!schem) { + delete schem; + return NULL; + } + } else if (lua_isnumber(L, index)) { + return NULL; + } else if (lua_isstring(L, index)) { + schem = SchematicManager::create(SCHEMATIC_NORMAL); + + std::string filepath = lua_tostring(L, index); + if (!fs::IsPathAbsolute(filepath)) + filepath = ModApiBase::getCurrentModPath(L) + DIR_DELIM + filepath; + + if (!schem->loadSchematicFromFile(filepath, ndef, + replace_names)) { + delete schem; + return NULL; + } + } + + return schem; +} + + +Schematic *load_schematic_from_def(lua_State *L, int index, + const NodeDefManager *ndef, StringMap *replace_names) +{ + Schematic *schem = SchematicManager::create(SCHEMATIC_NORMAL); + + if (!read_schematic_def(L, index, schem, &schem->m_nodenames)) { + delete schem; + return NULL; + } + + size_t num_nodes = schem->m_nodenames.size(); + + schem->m_nnlistsizes.push_back(num_nodes); + + if (replace_names) { + for (size_t i = 0; i != num_nodes; i++) { + StringMap::iterator it = replace_names->find(schem->m_nodenames[i]); + if (it != replace_names->end()) + schem->m_nodenames[i] = it->second; + } + } + + if (ndef) + ndef->pendNodeResolve(schem); + + return schem; +} + + +bool read_schematic_def(lua_State *L, int index, + Schematic *schem, std::vector *names) +{ + if (!lua_istable(L, index)) + return false; + + //// Get schematic size + lua_getfield(L, index, "size"); + v3s16 size = check_v3s16(L, -1); + lua_pop(L, 1); + + schem->size = size; + + //// Get schematic data + lua_getfield(L, index, "data"); + luaL_checktype(L, -1, LUA_TTABLE); + + u32 numnodes = size.X * size.Y * size.Z; + schem->schemdata = new MapNode[numnodes]; + + size_t names_base = names->size(); + std::unordered_map name_id_map; + + u32 i = 0; + for (lua_pushnil(L); lua_next(L, -2); i++, lua_pop(L, 1)) { + if (i >= numnodes) + continue; + + //// Read name + std::string name; + if (!getstringfield(L, -1, "name", name)) + throw LuaError("Schematic data definition with missing name field"); + + //// Read param1/prob + u8 param1; + if (!getintfield(L, -1, "param1", param1) && + !getintfield(L, -1, "prob", param1)) + param1 = MTSCHEM_PROB_ALWAYS_OLD; + + //// Read param2 + u8 param2 = getintfield_default(L, -1, "param2", 0); + + //// Find or add new nodename-to-ID mapping + std::unordered_map::iterator it = name_id_map.find(name); + content_t name_index; + if (it != name_id_map.end()) { + name_index = it->second; + } else { + name_index = names->size() - names_base; + name_id_map[name] = name_index; + names->push_back(name); + } + + //// Perform probability/force_place fixup on param1 + param1 >>= 1; + if (getboolfield_default(L, -1, "force_place", false)) + param1 |= MTSCHEM_FORCE_PLACE; + + //// Actually set the node in the schematic + schem->schemdata[i] = MapNode(name_index, param1, param2); + } + + if (i != numnodes) { + errorstream << "read_schematic_def: incorrect number of " + "nodes provided in raw schematic data (got " << i << + ", expected " << numnodes << ")." << std::endl; + return false; + } + + //// Get Y-slice probability values (if present) + schem->slice_probs = new u8[size.Y]; + for (i = 0; i != (u32) size.Y; i++) + schem->slice_probs[i] = MTSCHEM_PROB_ALWAYS; + + lua_getfield(L, index, "yslice_prob"); + if (lua_istable(L, -1)) { + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + u16 ypos; + if (!getintfield(L, -1, "ypos", ypos) || (ypos >= size.Y) || + !getintfield(L, -1, "prob", schem->slice_probs[ypos])) + continue; + + schem->slice_probs[ypos] >>= 1; + } + } + + return true; +} + + +void read_schematic_replacements(lua_State *L, int index, StringMap *replace_names) +{ + if (index < 0) + index = lua_gettop(L) + 1 + index; + + lua_pushnil(L); + while (lua_next(L, index)) { + std::string replace_from; + std::string replace_to; + + if (lua_istable(L, -1)) { // Old {{"x", "y"}, ...} format + lua_rawgeti(L, -1, 1); + if (!lua_isstring(L, -1)) + throw LuaError("schematics: replace_from field is not a string"); + replace_from = lua_tostring(L, -1); + lua_pop(L, 1); + + lua_rawgeti(L, -1, 2); + if (!lua_isstring(L, -1)) + throw LuaError("schematics: replace_to field is not a string"); + replace_to = lua_tostring(L, -1); + lua_pop(L, 1); + } else { // New {x = "y", ...} format + if (!lua_isstring(L, -2)) + throw LuaError("schematics: replace_from field is not a string"); + replace_from = lua_tostring(L, -2); + if (!lua_isstring(L, -1)) + throw LuaError("schematics: replace_to field is not a string"); + replace_to = lua_tostring(L, -1); + } + + replace_names->insert(std::make_pair(replace_from, replace_to)); + lua_pop(L, 1); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +Biome *get_or_load_biome(lua_State *L, int index, BiomeManager *biomemgr) +{ + if (index < 0) + index = lua_gettop(L) + 1 + index; + + Biome *biome = (Biome *)get_objdef(L, index, biomemgr); + if (biome) + return biome; + + biome = read_biome_def(L, index, biomemgr->getNodeDef()); + if (!biome) + return NULL; + + if (biomemgr->add(biome) == OBJDEF_INVALID_HANDLE) { + delete biome; + return NULL; + } + + return biome; +} + + +Biome *read_biome_def(lua_State *L, int index, const NodeDefManager *ndef) +{ + if (!lua_istable(L, index)) + return NULL; + + BiomeType biometype = (BiomeType)getenumfield(L, index, "type", + ModApiMapgen::es_BiomeTerrainType, BIOMETYPE_NORMAL); + Biome *b = BiomeManager::create(biometype); + + b->name = getstringfield_default(L, index, "name", ""); + b->depth_top = getintfield_default(L, index, "depth_top", 0); + b->depth_filler = getintfield_default(L, index, "depth_filler", -31000); + b->depth_water_top = getintfield_default(L, index, "depth_water_top", 0); + b->depth_riverbed = getintfield_default(L, index, "depth_riverbed", 0); + b->heat_point = getfloatfield_default(L, index, "heat_point", 0.f); + b->humidity_point = getfloatfield_default(L, index, "humidity_point", 0.f); + b->vertical_blend = getintfield_default(L, index, "vertical_blend", 0); + b->flags = 0; // reserved + + b->min_pos = getv3s16field_default( + L, index, "min_pos", v3s16(-31000, -31000, -31000)); + getintfield(L, index, "y_min", b->min_pos.Y); + b->max_pos = getv3s16field_default( + L, index, "max_pos", v3s16(31000, 31000, 31000)); + getintfield(L, index, "y_max", b->max_pos.Y); + + std::vector &nn = b->m_nodenames; + nn.push_back(getstringfield_default(L, index, "node_top", "")); + nn.push_back(getstringfield_default(L, index, "node_filler", "")); + nn.push_back(getstringfield_default(L, index, "node_stone", "")); + nn.push_back(getstringfield_default(L, index, "node_water_top", "")); + nn.push_back(getstringfield_default(L, index, "node_water", "")); + nn.push_back(getstringfield_default(L, index, "node_river_water", "")); + nn.push_back(getstringfield_default(L, index, "node_riverbed", "")); + nn.push_back(getstringfield_default(L, index, "node_dust", "")); + + size_t nnames = getstringlistfield(L, index, "node_cave_liquid", &nn); + // If no cave liquids defined, set list to "ignore" to trigger old hardcoded + // cave liquid behaviour. + if (nnames == 0) { + nn.emplace_back("ignore"); + nnames = 1; + } + b->m_nnlistsizes.push_back(nnames); + + nn.push_back(getstringfield_default(L, index, "node_dungeon", "")); + nn.push_back(getstringfield_default(L, index, "node_dungeon_alt", "")); + nn.push_back(getstringfield_default(L, index, "node_dungeon_stair", "")); + ndef->pendNodeResolve(b); + + return b; +} + + +size_t get_biome_list(lua_State *L, int index, + BiomeManager *biomemgr, std::unordered_set *biome_id_list) +{ + if (index < 0) + index = lua_gettop(L) + 1 + index; + + if (lua_isnil(L, index)) + return 0; + + bool is_single = true; + if (lua_istable(L, index)) { + lua_getfield(L, index, "name"); + is_single = !lua_isnil(L, -1); + lua_pop(L, 1); + } + + if (is_single) { + Biome *biome = get_or_load_biome(L, index, biomemgr); + if (!biome) { + infostream << "get_biome_list: failed to get biome '" + << (lua_isstring(L, index) ? lua_tostring(L, index) : "") + << "'." << std::endl; + return 1; + } + + biome_id_list->insert(biome->index); + return 0; + } + + // returns number of failed resolutions + size_t fail_count = 0; + size_t count = 0; + + for (lua_pushnil(L); lua_next(L, index); lua_pop(L, 1)) { + count++; + Biome *biome = get_or_load_biome(L, -1, biomemgr); + if (!biome) { + fail_count++; + infostream << "get_biome_list: failed to get biome '" + << (lua_isstring(L, -1) ? lua_tostring(L, -1) : "") + << "'" << std::endl; + continue; + } + + biome_id_list->insert(biome->index); + } + + return fail_count; +} + +/////////////////////////////////////////////////////////////////////////////// + +// get_biome_id(biomename) +// returns the biome id as used in biomemap and returned by 'get_biome_data()' +int ModApiMapgen::l_get_biome_id(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + const char *biome_str = lua_tostring(L, 1); + if (!biome_str) + return 0; + + const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager(); + if (!bmgr) + return 0; + + const Biome *biome = (Biome *)bmgr->getByName(biome_str); + if (!biome || biome->index == OBJDEF_INVALID_INDEX) + return 0; + + lua_pushinteger(L, biome->index); + + return 1; +} + + +// get_biome_name(biome_id) +// returns the biome name string +int ModApiMapgen::l_get_biome_name(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + int biome_id = luaL_checkinteger(L, 1); + + const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager(); + if (!bmgr) + return 0; + + const Biome *b = (Biome *)bmgr->getRaw(biome_id); + lua_pushstring(L, b->name.c_str()); + + return 1; +} + + +// get_heat(pos) +// returns the heat at the position +int ModApiMapgen::l_get_heat(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + v3s16 pos = read_v3s16(L, 1); + + NoiseParams np_heat; + NoiseParams np_heat_blend; + + MapSettingsManager *settingsmgr = + getServer(L)->getEmergeManager()->map_settings_mgr; + + if (!settingsmgr->getMapSettingNoiseParams("mg_biome_np_heat", + &np_heat) || + !settingsmgr->getMapSettingNoiseParams("mg_biome_np_heat_blend", + &np_heat_blend)) + return 0; + + std::string value; + if (!settingsmgr->getMapSetting("seed", &value)) + return 0; + std::istringstream ss(value); + u64 seed; + ss >> seed; + + const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager(); + if (!bmgr) + return 0; + + float heat = bmgr->getHeatAtPosOriginal(pos, np_heat, np_heat_blend, seed); + + lua_pushnumber(L, heat); + + return 1; +} + + +// get_humidity(pos) +// returns the humidity at the position +int ModApiMapgen::l_get_humidity(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + v3s16 pos = read_v3s16(L, 1); + + NoiseParams np_humidity; + NoiseParams np_humidity_blend; + + MapSettingsManager *settingsmgr = + getServer(L)->getEmergeManager()->map_settings_mgr; + + if (!settingsmgr->getMapSettingNoiseParams("mg_biome_np_humidity", + &np_humidity) || + !settingsmgr->getMapSettingNoiseParams("mg_biome_np_humidity_blend", + &np_humidity_blend)) + return 0; + + std::string value; + if (!settingsmgr->getMapSetting("seed", &value)) + return 0; + std::istringstream ss(value); + u64 seed; + ss >> seed; + + const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager(); + if (!bmgr) + return 0; + + float humidity = bmgr->getHumidityAtPosOriginal(pos, np_humidity, + np_humidity_blend, seed); + + lua_pushnumber(L, humidity); + + return 1; +} + + +// get_biome_data(pos) +// returns a table containing the biome id, heat and humidity at the position +int ModApiMapgen::l_get_biome_data(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + v3s16 pos = read_v3s16(L, 1); + + NoiseParams np_heat; + NoiseParams np_heat_blend; + NoiseParams np_humidity; + NoiseParams np_humidity_blend; + + MapSettingsManager *settingsmgr = + getServer(L)->getEmergeManager()->map_settings_mgr; + + if (!settingsmgr->getMapSettingNoiseParams("mg_biome_np_heat", + &np_heat) || + !settingsmgr->getMapSettingNoiseParams("mg_biome_np_heat_blend", + &np_heat_blend) || + !settingsmgr->getMapSettingNoiseParams("mg_biome_np_humidity", + &np_humidity) || + !settingsmgr->getMapSettingNoiseParams("mg_biome_np_humidity_blend", + &np_humidity_blend)) + return 0; + + std::string value; + if (!settingsmgr->getMapSetting("seed", &value)) + return 0; + std::istringstream ss(value); + u64 seed; + ss >> seed; + + const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager(); + if (!bmgr) + return 0; + + float heat = bmgr->getHeatAtPosOriginal(pos, np_heat, np_heat_blend, seed); + if (!heat) + return 0; + + float humidity = bmgr->getHumidityAtPosOriginal(pos, np_humidity, + np_humidity_blend, seed); + if (!humidity) + return 0; + + const Biome *biome = bmgr->getBiomeFromNoiseOriginal(heat, humidity, pos); + if (!biome || biome->index == OBJDEF_INVALID_INDEX) + return 0; + + lua_newtable(L); + + lua_pushinteger(L, biome->index); + lua_setfield(L, -2, "biome"); + + lua_pushnumber(L, heat); + lua_setfield(L, -2, "heat"); + + lua_pushnumber(L, humidity); + lua_setfield(L, -2, "humidity"); + + return 1; +} + + +// get_mapgen_object(objectname) +// returns the requested object used during map generation +int ModApiMapgen::l_get_mapgen_object(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + const char *mgobjstr = lua_tostring(L, 1); + + int mgobjint; + if (!string_to_enum(es_MapgenObject, mgobjint, mgobjstr ? mgobjstr : "")) + return 0; + + enum MapgenObject mgobj = (MapgenObject)mgobjint; + + EmergeManager *emerge = getServer(L)->getEmergeManager(); + Mapgen *mg = emerge->getCurrentMapgen(); + if (!mg) + throw LuaError("Must only be called in a mapgen thread!"); + + size_t maplen = mg->csize.X * mg->csize.Z; + + switch (mgobj) { + case MGOBJ_VMANIP: { + MMVManip *vm = mg->vm; + + // VoxelManip object + LuaVoxelManip *o = new LuaVoxelManip(vm, true); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, "VoxelManip"); + lua_setmetatable(L, -2); + + // emerged min pos + push_v3s16(L, vm->m_area.MinEdge); + + // emerged max pos + push_v3s16(L, vm->m_area.MaxEdge); + + return 3; + } + case MGOBJ_HEIGHTMAP: { + if (!mg->heightmap) + return 0; + + lua_createtable(L, maplen, 0); + for (size_t i = 0; i != maplen; i++) { + lua_pushinteger(L, mg->heightmap[i]); + lua_rawseti(L, -2, i + 1); + } + + return 1; + } + case MGOBJ_BIOMEMAP: { + if (!mg->biomegen) + return 0; + + lua_createtable(L, maplen, 0); + for (size_t i = 0; i != maplen; i++) { + lua_pushinteger(L, mg->biomegen->biomemap[i]); + lua_rawseti(L, -2, i + 1); + } + + return 1; + } + case MGOBJ_HEATMAP: { + if (!mg->biomegen || mg->biomegen->getType() != BIOMEGEN_ORIGINAL) + return 0; + + BiomeGenOriginal *bg = (BiomeGenOriginal *)mg->biomegen; + + lua_createtable(L, maplen, 0); + for (size_t i = 0; i != maplen; i++) { + lua_pushnumber(L, bg->heatmap[i]); + lua_rawseti(L, -2, i + 1); + } + + return 1; + } + + case MGOBJ_HUMIDMAP: { + if (!mg->biomegen || mg->biomegen->getType() != BIOMEGEN_ORIGINAL) + return 0; + + BiomeGenOriginal *bg = (BiomeGenOriginal *)mg->biomegen; + + lua_createtable(L, maplen, 0); + for (size_t i = 0; i != maplen; i++) { + lua_pushnumber(L, bg->humidmap[i]); + lua_rawseti(L, -2, i + 1); + } + + return 1; + } + case MGOBJ_GENNOTIFY: { + std::map >event_map; + + mg->gennotify.getEvents(event_map); + + lua_createtable(L, 0, event_map.size()); + for (auto it = event_map.begin(); it != event_map.end(); ++it) { + lua_createtable(L, it->second.size(), 0); + + for (size_t j = 0; j != it->second.size(); j++) { + push_v3s16(L, it->second[j]); + lua_rawseti(L, -2, j + 1); + } + + lua_setfield(L, -2, it->first.c_str()); + } + + return 1; + } + } + + return 0; +} + + +// get_spawn_level(x = num, z = num) +int ModApiMapgen::l_get_spawn_level(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + s16 x = luaL_checkinteger(L, 1); + s16 z = luaL_checkinteger(L, 2); + + EmergeManager *emerge = getServer(L)->getEmergeManager(); + int spawn_level = emerge->getSpawnLevelAtPoint(v2s16(x, z)); + // Unsuitable spawn point + if (spawn_level == MAX_MAP_GENERATION_LIMIT) + return 0; + + // 'findSpawnPos()' in server.cpp adds at least 1 + lua_pushinteger(L, spawn_level + 1); + + return 1; +} + + +int ModApiMapgen::l_get_mapgen_params(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + log_deprecated(L, "get_mapgen_params is deprecated; " + "use get_mapgen_setting instead"); + + std::string value; + + MapSettingsManager *settingsmgr = + getServer(L)->getEmergeManager()->map_settings_mgr; + + lua_newtable(L); + + settingsmgr->getMapSetting("mg_name", &value); + lua_pushstring(L, value.c_str()); + lua_setfield(L, -2, "mgname"); + + settingsmgr->getMapSetting("seed", &value); + std::istringstream ss(value); + u64 seed; + ss >> seed; + lua_pushinteger(L, seed); + lua_setfield(L, -2, "seed"); + + settingsmgr->getMapSetting("water_level", &value); + lua_pushinteger(L, stoi(value, -32768, 32767)); + lua_setfield(L, -2, "water_level"); + + settingsmgr->getMapSetting("chunksize", &value); + lua_pushinteger(L, stoi(value, -32768, 32767)); + lua_setfield(L, -2, "chunksize"); + + settingsmgr->getMapSetting("mg_flags", &value); + lua_pushstring(L, value.c_str()); + lua_setfield(L, -2, "flags"); + + return 1; +} + + +// set_mapgen_params(params) +// set mapgen parameters +int ModApiMapgen::l_set_mapgen_params(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + log_deprecated(L, "set_mapgen_params is deprecated; " + "use set_mapgen_setting instead"); + + if (!lua_istable(L, 1)) + return 0; + + MapSettingsManager *settingsmgr = + getServer(L)->getEmergeManager()->map_settings_mgr; + + lua_getfield(L, 1, "mgname"); + if (lua_isstring(L, -1)) + settingsmgr->setMapSetting("mg_name", readParam(L, -1), true); + + lua_getfield(L, 1, "seed"); + if (lua_isnumber(L, -1)) + settingsmgr->setMapSetting("seed", readParam(L, -1), true); + + lua_getfield(L, 1, "water_level"); + if (lua_isnumber(L, -1)) + settingsmgr->setMapSetting("water_level", readParam(L, -1), true); + + lua_getfield(L, 1, "chunksize"); + if (lua_isnumber(L, -1)) + settingsmgr->setMapSetting("chunksize", readParam(L, -1), true); + + warn_if_field_exists(L, 1, "flagmask", + "Obsolete: flags field now includes unset flags."); + + lua_getfield(L, 1, "flags"); + if (lua_isstring(L, -1)) + settingsmgr->setMapSetting("mg_flags", readParam(L, -1), true); + + return 0; +} + +// get_mapgen_setting(name) +int ModApiMapgen::l_get_mapgen_setting(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + std::string value; + MapSettingsManager *settingsmgr = + getServer(L)->getEmergeManager()->map_settings_mgr; + + const char *name = luaL_checkstring(L, 1); + if (!settingsmgr->getMapSetting(name, &value)) + return 0; + + lua_pushstring(L, value.c_str()); + return 1; +} + +// get_mapgen_setting_noiseparams(name) +int ModApiMapgen::l_get_mapgen_setting_noiseparams(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + NoiseParams np; + MapSettingsManager *settingsmgr = + getServer(L)->getEmergeManager()->map_settings_mgr; + + const char *name = luaL_checkstring(L, 1); + if (!settingsmgr->getMapSettingNoiseParams(name, &np)) + return 0; + + push_noiseparams(L, &np); + return 1; +} + +// set_mapgen_setting(name, value, override_meta) +// set mapgen config values +int ModApiMapgen::l_set_mapgen_setting(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + MapSettingsManager *settingsmgr = + getServer(L)->getEmergeManager()->map_settings_mgr; + + const char *name = luaL_checkstring(L, 1); + const char *value = luaL_checkstring(L, 2); + bool override_meta = readParam(L, 3, false); + + if (!settingsmgr->setMapSetting(name, value, override_meta)) { + errorstream << "set_mapgen_setting: cannot set '" + << name << "' after initialization" << std::endl; + } + + return 0; +} + + +// set_mapgen_setting_noiseparams(name, noiseparams, set_default) +// set mapgen config values for noise parameters +int ModApiMapgen::l_set_mapgen_setting_noiseparams(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + MapSettingsManager *settingsmgr = + getServer(L)->getEmergeManager()->map_settings_mgr; + + const char *name = luaL_checkstring(L, 1); + + NoiseParams np; + if (!read_noiseparams(L, 2, &np)) { + errorstream << "set_mapgen_setting_noiseparams: cannot set '" << name + << "'; invalid noiseparams table" << std::endl; + return 0; + } + + bool override_meta = readParam(L, 3, false); + + if (!settingsmgr->setMapSettingNoiseParams(name, &np, override_meta)) { + errorstream << "set_mapgen_setting_noiseparams: cannot set '" + << name << "' after initialization" << std::endl; + } + + return 0; +} + + +// set_noiseparams(name, noiseparams, set_default) +// set global config values for noise parameters +int ModApiMapgen::l_set_noiseparams(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + const char *name = luaL_checkstring(L, 1); + + NoiseParams np; + if (!read_noiseparams(L, 2, &np)) { + errorstream << "set_noiseparams: cannot set '" << name + << "'; invalid noiseparams table" << std::endl; + return 0; + } + + bool set_default = !lua_isboolean(L, 3) || readParam(L, 3); + + g_settings->setNoiseParams(name, np, set_default); + + return 0; +} + + +// get_noiseparams(name) +int ModApiMapgen::l_get_noiseparams(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + std::string name = luaL_checkstring(L, 1); + + NoiseParams np; + if (!g_settings->getNoiseParams(name, np)) + return 0; + + push_noiseparams(L, &np); + return 1; +} + + +// set_gen_notify(flags, {deco_id_table}) +int ModApiMapgen::l_set_gen_notify(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + u32 flags = 0, flagmask = 0; + EmergeManager *emerge = getServer(L)->getEmergeManager(); + + if (read_flags(L, 1, flagdesc_gennotify, &flags, &flagmask)) { + emerge->gen_notify_on &= ~flagmask; + emerge->gen_notify_on |= flags; + } + + if (lua_istable(L, 2)) { + lua_pushnil(L); + while (lua_next(L, 2)) { + if (lua_isnumber(L, -1)) + emerge->gen_notify_on_deco_ids.insert((u32)lua_tonumber(L, -1)); + lua_pop(L, 1); + } + } + + return 0; +} + + +// get_gen_notify() +int ModApiMapgen::l_get_gen_notify(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + EmergeManager *emerge = getServer(L)->getEmergeManager(); + push_flags_string(L, flagdesc_gennotify, emerge->gen_notify_on, + emerge->gen_notify_on); + + lua_newtable(L); + int i = 1; + for (u32 gen_notify_on_deco_id : emerge->gen_notify_on_deco_ids) { + lua_pushnumber(L, gen_notify_on_deco_id); + lua_rawseti(L, -2, i++); + } + return 2; +} + + +// get_decoration_id(decoration_name) +// returns the decoration ID as used in gennotify +int ModApiMapgen::l_get_decoration_id(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + const char *deco_str = luaL_checkstring(L, 1); + if (!deco_str) + return 0; + + const DecorationManager *dmgr = + getServer(L)->getEmergeManager()->getDecorationManager(); + + if (!dmgr) + return 0; + + Decoration *deco = (Decoration *)dmgr->getByName(deco_str); + + if (!deco) + return 0; + + lua_pushinteger(L, deco->index); + + return 1; +} + + +// register_biome({lots of stuff}) +int ModApiMapgen::l_register_biome(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + int index = 1; + luaL_checktype(L, index, LUA_TTABLE); + + const NodeDefManager *ndef = getServer(L)->getNodeDefManager(); + BiomeManager *bmgr = getServer(L)->getEmergeManager()->getWritableBiomeManager(); + + Biome *biome = read_biome_def(L, index, ndef); + if (!biome) + return 0; + + ObjDefHandle handle = bmgr->add(biome); + if (handle == OBJDEF_INVALID_HANDLE) { + delete biome; + return 0; + } + + lua_pushinteger(L, handle); + return 1; +} + + +// register_decoration({lots of stuff}) +int ModApiMapgen::l_register_decoration(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + int index = 1; + luaL_checktype(L, index, LUA_TTABLE); + + const NodeDefManager *ndef = getServer(L)->getNodeDefManager(); + EmergeManager *emerge = getServer(L)->getEmergeManager(); + DecorationManager *decomgr = emerge->getWritableDecorationManager(); + BiomeManager *biomemgr = emerge->getWritableBiomeManager(); + SchematicManager *schemmgr = emerge->getWritableSchematicManager(); + + enum DecorationType decotype = (DecorationType)getenumfield(L, index, + "deco_type", es_DecorationType, -1); + + Decoration *deco = decomgr->create(decotype); + if (!deco) { + errorstream << "register_decoration: decoration placement type " + << decotype << " not implemented" << std::endl; + return 0; + } + + deco->name = getstringfield_default(L, index, "name", ""); + deco->fill_ratio = getfloatfield_default(L, index, "fill_ratio", 0.02); + deco->y_min = getintfield_default(L, index, "y_min", -31000); + deco->y_max = getintfield_default(L, index, "y_max", 31000); + deco->nspawnby = getintfield_default(L, index, "num_spawn_by", -1); + deco->place_offset_y = getintfield_default(L, index, "place_offset_y", 0); + deco->sidelen = getintfield_default(L, index, "sidelen", 8); + if (deco->sidelen <= 0) { + errorstream << "register_decoration: sidelen must be " + "greater than 0" << std::endl; + delete deco; + return 0; + } + + //// Get node name(s) to place decoration on + size_t nread = getstringlistfield(L, index, "place_on", &deco->m_nodenames); + deco->m_nnlistsizes.push_back(nread); + + //// Get decoration flags + getflagsfield(L, index, "flags", flagdesc_deco, &deco->flags, NULL); + + //// Get NoiseParams to define how decoration is placed + lua_getfield(L, index, "noise_params"); + if (read_noiseparams(L, -1, &deco->np)) + deco->flags |= DECO_USE_NOISE; + lua_pop(L, 1); + + //// Get biomes associated with this decoration (if any) + lua_getfield(L, index, "biomes"); + if (get_biome_list(L, -1, biomemgr, &deco->biomes)) + infostream << "register_decoration: couldn't get all biomes " << std::endl; + lua_pop(L, 1); + + //// Get node name(s) to 'spawn by' + size_t nnames = getstringlistfield(L, index, "spawn_by", &deco->m_nodenames); + deco->m_nnlistsizes.push_back(nnames); + if (nnames == 0 && deco->nspawnby != -1) { + errorstream << "register_decoration: no spawn_by nodes defined," + " but num_spawn_by specified" << std::endl; + } + + //// Handle decoration type-specific parameters + bool success = false; + switch (decotype) { + case DECO_SIMPLE: + success = read_deco_simple(L, (DecoSimple *)deco); + break; + case DECO_SCHEMATIC: + success = read_deco_schematic(L, schemmgr, (DecoSchematic *)deco); + break; + case DECO_LSYSTEM: + break; + } + + if (!success) { + delete deco; + return 0; + } + + ndef->pendNodeResolve(deco); + + ObjDefHandle handle = decomgr->add(deco); + if (handle == OBJDEF_INVALID_HANDLE) { + delete deco; + return 0; + } + + lua_pushinteger(L, handle); + return 1; +} + + +bool read_deco_simple(lua_State *L, DecoSimple *deco) +{ + int index = 1; + int param2; + int param2_max; + + deco->deco_height = getintfield_default(L, index, "height", 1); + deco->deco_height_max = getintfield_default(L, index, "height_max", 0); + + if (deco->deco_height <= 0) { + errorstream << "register_decoration: simple decoration height" + " must be greater than 0" << std::endl; + return false; + } + + size_t nnames = getstringlistfield(L, index, "decoration", &deco->m_nodenames); + deco->m_nnlistsizes.push_back(nnames); + + if (nnames == 0) { + errorstream << "register_decoration: no decoration nodes " + "defined" << std::endl; + return false; + } + + param2 = getintfield_default(L, index, "param2", 0); + param2_max = getintfield_default(L, index, "param2_max", 0); + + if (param2 < 0 || param2 > 255 || param2_max < 0 || param2_max > 255) { + errorstream << "register_decoration: param2 or param2_max out of bounds (0-255)" + << std::endl; + return false; + } + + deco->deco_param2 = (u8)param2; + deco->deco_param2_max = (u8)param2_max; + + return true; +} + + +bool read_deco_schematic(lua_State *L, SchematicManager *schemmgr, DecoSchematic *deco) +{ + int index = 1; + + deco->rotation = (Rotation)getenumfield(L, index, "rotation", + ModApiMapgen::es_Rotation, ROTATE_0); + + StringMap replace_names; + lua_getfield(L, index, "replacements"); + if (lua_istable(L, -1)) + read_schematic_replacements(L, -1, &replace_names); + lua_pop(L, 1); + + lua_getfield(L, index, "schematic"); + Schematic *schem = get_or_load_schematic(L, -1, schemmgr, &replace_names); + lua_pop(L, 1); + + deco->schematic = schem; + return schem != NULL; +} + + +// register_ore({lots of stuff}) +int ModApiMapgen::l_register_ore(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + int index = 1; + luaL_checktype(L, index, LUA_TTABLE); + + const NodeDefManager *ndef = getServer(L)->getNodeDefManager(); + EmergeManager *emerge = getServer(L)->getEmergeManager(); + BiomeManager *bmgr = emerge->getWritableBiomeManager(); + OreManager *oremgr = emerge->getWritableOreManager(); + + enum OreType oretype = (OreType)getenumfield(L, index, + "ore_type", es_OreType, ORE_SCATTER); + Ore *ore = oremgr->create(oretype); + if (!ore) { + errorstream << "register_ore: ore_type " << oretype << " not implemented\n"; + return 0; + } + + ore->name = getstringfield_default(L, index, "name", ""); + ore->ore_param2 = (u8)getintfield_default(L, index, "ore_param2", 0); + ore->clust_scarcity = getintfield_default(L, index, "clust_scarcity", 1); + ore->clust_num_ores = getintfield_default(L, index, "clust_num_ores", 1); + ore->clust_size = getintfield_default(L, index, "clust_size", 0); + ore->noise = NULL; + ore->flags = 0; + + //// Get noise_threshold + warn_if_field_exists(L, index, "noise_threshhold", + "Deprecated: new name is \"noise_threshold\"."); + + float nthresh; + if (!getfloatfield(L, index, "noise_threshold", nthresh) && + !getfloatfield(L, index, "noise_threshhold", nthresh)) + nthresh = 0; + ore->nthresh = nthresh; + + //// Get y_min/y_max + warn_if_field_exists(L, index, "height_min", + "Deprecated: new name is \"y_min\"."); + warn_if_field_exists(L, index, "height_max", + "Deprecated: new name is \"y_max\"."); + + int ymin, ymax; + if (!getintfield(L, index, "y_min", ymin) && + !getintfield(L, index, "height_min", ymin)) + ymin = -31000; + if (!getintfield(L, index, "y_max", ymax) && + !getintfield(L, index, "height_max", ymax)) + ymax = 31000; + ore->y_min = ymin; + ore->y_max = ymax; + + if (ore->clust_scarcity <= 0 || ore->clust_num_ores <= 0) { + errorstream << "register_ore: clust_scarcity and clust_num_ores" + "must be greater than 0" << std::endl; + delete ore; + return 0; + } + + //// Get flags + getflagsfield(L, index, "flags", flagdesc_ore, &ore->flags, NULL); + + //// Get biomes associated with this decoration (if any) + lua_getfield(L, index, "biomes"); + if (get_biome_list(L, -1, bmgr, &ore->biomes)) + infostream << "register_ore: couldn't get all biomes " << std::endl; + lua_pop(L, 1); + + //// Get noise parameters if needed + lua_getfield(L, index, "noise_params"); + if (read_noiseparams(L, -1, &ore->np)) { + ore->flags |= OREFLAG_USE_NOISE; + } else if (ore->NEEDS_NOISE) { + errorstream << "register_ore: specified ore type requires valid " + "'noise_params' parameter" << std::endl; + delete ore; + return 0; + } + lua_pop(L, 1); + + //// Get type-specific parameters + switch (oretype) { + case ORE_SHEET: { + OreSheet *oresheet = (OreSheet *)ore; + + oresheet->column_height_min = getintfield_default(L, index, + "column_height_min", 1); + oresheet->column_height_max = getintfield_default(L, index, + "column_height_max", ore->clust_size); + oresheet->column_midpoint_factor = getfloatfield_default(L, index, + "column_midpoint_factor", 0.5f); + + break; + } + case ORE_PUFF: { + OrePuff *orepuff = (OrePuff *)ore; + + lua_getfield(L, index, "np_puff_top"); + read_noiseparams(L, -1, &orepuff->np_puff_top); + lua_pop(L, 1); + + lua_getfield(L, index, "np_puff_bottom"); + read_noiseparams(L, -1, &orepuff->np_puff_bottom); + lua_pop(L, 1); + + break; + } + case ORE_VEIN: { + OreVein *orevein = (OreVein *)ore; + + orevein->random_factor = getfloatfield_default(L, index, + "random_factor", 1.f); + + break; + } + case ORE_STRATUM: { + OreStratum *orestratum = (OreStratum *)ore; + + lua_getfield(L, index, "np_stratum_thickness"); + if (read_noiseparams(L, -1, &orestratum->np_stratum_thickness)) + ore->flags |= OREFLAG_USE_NOISE2; + lua_pop(L, 1); + + orestratum->stratum_thickness = getintfield_default(L, index, + "stratum_thickness", 8); + + break; + } + default: + break; + } + + ObjDefHandle handle = oremgr->add(ore); + if (handle == OBJDEF_INVALID_HANDLE) { + delete ore; + return 0; + } + + ore->m_nodenames.push_back(getstringfield_default(L, index, "ore", "")); + + size_t nnames = getstringlistfield(L, index, "wherein", &ore->m_nodenames); + ore->m_nnlistsizes.push_back(nnames); + + ndef->pendNodeResolve(ore); + + lua_pushinteger(L, handle); + return 1; +} + + +// register_schematic({schematic}, replacements={}) +int ModApiMapgen::l_register_schematic(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + SchematicManager *schemmgr = + getServer(L)->getEmergeManager()->getWritableSchematicManager(); + + StringMap replace_names; + if (lua_istable(L, 2)) + read_schematic_replacements(L, 2, &replace_names); + + Schematic *schem = load_schematic(L, 1, schemmgr->getNodeDef(), + &replace_names); + if (!schem) + return 0; + + ObjDefHandle handle = schemmgr->add(schem); + if (handle == OBJDEF_INVALID_HANDLE) { + delete schem; + return 0; + } + + lua_pushinteger(L, handle); + return 1; +} + + +// clear_registered_biomes() +int ModApiMapgen::l_clear_registered_biomes(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + BiomeManager *bmgr = + getServer(L)->getEmergeManager()->getWritableBiomeManager(); + bmgr->clear(); + return 0; +} + + +// clear_registered_decorations() +int ModApiMapgen::l_clear_registered_decorations(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + DecorationManager *dmgr = + getServer(L)->getEmergeManager()->getWritableDecorationManager(); + dmgr->clear(); + return 0; +} + + +// clear_registered_ores() +int ModApiMapgen::l_clear_registered_ores(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + OreManager *omgr = + getServer(L)->getEmergeManager()->getWritableOreManager(); + omgr->clear(); + return 0; +} + + +// clear_registered_schematics() +int ModApiMapgen::l_clear_registered_schematics(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + SchematicManager *smgr = + getServer(L)->getEmergeManager()->getWritableSchematicManager(); + smgr->clear(); + return 0; +} + + +// generate_ores(vm, p1, p2, [ore_id]) +int ModApiMapgen::l_generate_ores(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + EmergeManager *emerge = getServer(L)->getEmergeManager(); + + Mapgen mg; + mg.seed = emerge->mgparams->seed; + mg.vm = LuaVoxelManip::checkobject(L, 1)->vm; + mg.ndef = getServer(L)->getNodeDefManager(); + + v3s16 pmin = lua_istable(L, 2) ? check_v3s16(L, 2) : + mg.vm->m_area.MinEdge + v3s16(1,1,1) * MAP_BLOCKSIZE; + v3s16 pmax = lua_istable(L, 3) ? check_v3s16(L, 3) : + mg.vm->m_area.MaxEdge - v3s16(1,1,1) * MAP_BLOCKSIZE; + sortBoxVerticies(pmin, pmax); + + u32 blockseed = Mapgen::getBlockSeed(pmin, mg.seed); + + emerge->oremgr->placeAllOres(&mg, blockseed, pmin, pmax); + + return 0; +} + + +// generate_decorations(vm, p1, p2, [deco_id]) +int ModApiMapgen::l_generate_decorations(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + EmergeManager *emerge = getServer(L)->getEmergeManager(); + + Mapgen mg; + mg.seed = emerge->mgparams->seed; + mg.vm = LuaVoxelManip::checkobject(L, 1)->vm; + mg.ndef = getServer(L)->getNodeDefManager(); + + v3s16 pmin = lua_istable(L, 2) ? check_v3s16(L, 2) : + mg.vm->m_area.MinEdge + v3s16(1,1,1) * MAP_BLOCKSIZE; + v3s16 pmax = lua_istable(L, 3) ? check_v3s16(L, 3) : + mg.vm->m_area.MaxEdge - v3s16(1,1,1) * MAP_BLOCKSIZE; + sortBoxVerticies(pmin, pmax); + + u32 blockseed = Mapgen::getBlockSeed(pmin, mg.seed); + + emerge->decomgr->placeAllDecos(&mg, blockseed, pmin, pmax); + + return 0; +} + + +// create_schematic(p1, p2, probability_list, filename, y_slice_prob_list) +int ModApiMapgen::l_create_schematic(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + const NodeDefManager *ndef = getServer(L)->getNodeDefManager(); + + const char *filename = luaL_checkstring(L, 4); + CHECK_SECURE_PATH(L, filename, true); + + Map *map = &(getEnv(L)->getMap()); + Schematic schem; + + v3s16 p1 = check_v3s16(L, 1); + v3s16 p2 = check_v3s16(L, 2); + sortBoxVerticies(p1, p2); + + std::vector > prob_list; + if (lua_istable(L, 3)) { + lua_pushnil(L); + while (lua_next(L, 3)) { + if (lua_istable(L, -1)) { + lua_getfield(L, -1, "pos"); + v3s16 pos = check_v3s16(L, -1); + lua_pop(L, 1); + + u8 prob = getintfield_default(L, -1, "prob", MTSCHEM_PROB_ALWAYS); + prob_list.emplace_back(pos, prob); + } + + lua_pop(L, 1); + } + } + + std::vector > slice_prob_list; + if (lua_istable(L, 5)) { + lua_pushnil(L); + while (lua_next(L, 5)) { + if (lua_istable(L, -1)) { + s16 ypos = getintfield_default(L, -1, "ypos", 0); + u8 prob = getintfield_default(L, -1, "prob", MTSCHEM_PROB_ALWAYS); + slice_prob_list.emplace_back(ypos, prob); + } + + lua_pop(L, 1); + } + } + + if (!schem.getSchematicFromMap(map, p1, p2)) { + errorstream << "create_schematic: failed to get schematic " + "from map" << std::endl; + return 0; + } + + schem.applyProbabilities(p1, &prob_list, &slice_prob_list); + + schem.saveSchematicToFile(filename, ndef); + actionstream << "create_schematic: saved schematic file '" + << filename << "'." << std::endl; + + lua_pushboolean(L, true); + return 1; +} + + +// place_schematic(p, schematic, rotation, +// replacements, force_placement, flagstring) +int ModApiMapgen::l_place_schematic(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + GET_ENV_PTR; + + ServerMap *map = &(env->getServerMap()); + SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; + + //// Read position + v3s16 p = check_v3s16(L, 1); + + //// Read rotation + int rot = ROTATE_0; + std::string enumstr = readParam(L, 3, ""); + if (!enumstr.empty()) + string_to_enum(es_Rotation, rot, enumstr); + + //// Read force placement + bool force_placement = true; + if (lua_isboolean(L, 5)) + force_placement = readParam(L, 5); + + //// Read node replacements + StringMap replace_names; + if (lua_istable(L, 4)) + read_schematic_replacements(L, 4, &replace_names); + + //// Read schematic + Schematic *schem = get_or_load_schematic(L, 2, schemmgr, &replace_names); + if (!schem) { + errorstream << "place_schematic: failed to get schematic" << std::endl; + return 0; + } + + //// Read flags + u32 flags = 0; + read_flags(L, 6, flagdesc_deco, &flags, NULL); + + schem->placeOnMap(map, p, flags, (Rotation)rot, force_placement); + + lua_pushboolean(L, true); + return 1; +} + + +// place_schematic_on_vmanip(vm, p, schematic, rotation, +// replacements, force_placement, flagstring) +int ModApiMapgen::l_place_schematic_on_vmanip(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; + + //// Read VoxelManip object + MMVManip *vm = LuaVoxelManip::checkobject(L, 1)->vm; + + //// Read position + v3s16 p = check_v3s16(L, 2); + + //// Read rotation + int rot = ROTATE_0; + std::string enumstr = readParam(L, 4, ""); + if (!enumstr.empty()) + string_to_enum(es_Rotation, rot, std::string(enumstr)); + + //// Read force placement + bool force_placement = true; + if (lua_isboolean(L, 6)) + force_placement = readParam(L, 6); + + //// Read node replacements + StringMap replace_names; + if (lua_istable(L, 5)) + read_schematic_replacements(L, 5, &replace_names); + + //// Read schematic + Schematic *schem = get_or_load_schematic(L, 3, schemmgr, &replace_names); + if (!schem) { + errorstream << "place_schematic: failed to get schematic" << std::endl; + return 0; + } + + //// Read flags + u32 flags = 0; + read_flags(L, 7, flagdesc_deco, &flags, NULL); + + bool schematic_did_fit = schem->placeOnVManip( + vm, p, flags, (Rotation)rot, force_placement); + + lua_pushboolean(L, schematic_did_fit); + return 1; +} + + +// serialize_schematic(schematic, format, options={...}) +int ModApiMapgen::l_serialize_schematic(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + const SchematicManager *schemmgr = getServer(L)->getEmergeManager()->getSchematicManager(); + + //// Read options + bool use_comments = getboolfield_default(L, 3, "lua_use_comments", false); + u32 indent_spaces = getintfield_default(L, 3, "lua_num_indent_spaces", 0); + + //// Get schematic + bool was_loaded = false; + const Schematic *schem = (Schematic *)get_objdef(L, 1, schemmgr); + if (!schem) { + schem = load_schematic(L, 1, NULL, NULL); + was_loaded = true; + } + if (!schem) { + errorstream << "serialize_schematic: failed to get schematic" << std::endl; + return 0; + } + + //// Read format of definition to save as + int schem_format = SCHEM_FMT_MTS; + std::string enumstr = readParam(L, 2, ""); + if (!enumstr.empty()) + string_to_enum(es_SchematicFormatType, schem_format, enumstr); + + //// Serialize to binary string + std::ostringstream os(std::ios_base::binary); + switch (schem_format) { + case SCHEM_FMT_MTS: + schem->serializeToMts(&os, schem->m_nodenames); + break; + case SCHEM_FMT_LUA: + schem->serializeToLua(&os, schem->m_nodenames, + use_comments, indent_spaces); + break; + default: + return 0; + } + + if (was_loaded) + delete schem; + + std::string ser = os.str(); + lua_pushlstring(L, ser.c_str(), ser.length()); + return 1; +} + +// read_schematic(schematic, options={...}) +int ModApiMapgen::l_read_schematic(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + const SchematicManager *schemmgr = + getServer(L)->getEmergeManager()->getSchematicManager(); + + //// Read options + std::string write_yslice = getstringfield_default(L, 2, "write_yslice_prob", "all"); + + //// Get schematic + bool was_loaded = false; + Schematic *schem = (Schematic *)get_objdef(L, 1, schemmgr); + if (!schem) { + schem = load_schematic(L, 1, NULL, NULL); + was_loaded = true; + } + if (!schem) { + errorstream << "read_schematic: failed to get schematic" << std::endl; + return 0; + } + lua_pop(L, 2); + + //// Create the Lua table + u32 numnodes = schem->size.X * schem->size.Y * schem->size.Z; + const std::vector &names = schem->m_nodenames; + + lua_createtable(L, 0, (write_yslice == "none") ? 2 : 3); + + // Create the size field + push_v3s16(L, schem->size); + lua_setfield(L, 1, "size"); + + // Create the yslice_prob field + if (write_yslice != "none") { + lua_createtable(L, schem->size.Y, 0); + for (u16 y = 0; y != schem->size.Y; ++y) { + u8 probability = schem->slice_probs[y] & MTSCHEM_PROB_MASK; + if (probability < MTSCHEM_PROB_ALWAYS || write_yslice != "low") { + lua_createtable(L, 0, 2); + lua_pushinteger(L, y); + lua_setfield(L, 3, "ypos"); + lua_pushinteger(L, probability * 2); + lua_setfield(L, 3, "prob"); + lua_rawseti(L, 2, y + 1); + } + } + lua_setfield(L, 1, "yslice_prob"); + } + + // Create the data field + lua_createtable(L, numnodes, 0); // data table + for (u32 i = 0; i < numnodes; ++i) { + MapNode node = schem->schemdata[i]; + u8 probability = node.param1 & MTSCHEM_PROB_MASK; + bool force_place = node.param1 & MTSCHEM_FORCE_PLACE; + lua_createtable(L, 0, force_place ? 4 : 3); + lua_pushstring(L, names[schem->schemdata[i].getContent()].c_str()); + lua_setfield(L, 3, "name"); + lua_pushinteger(L, probability * 2); + lua_setfield(L, 3, "prob"); + lua_pushinteger(L, node.param2); + lua_setfield(L, 3, "param2"); + if (force_place) { + lua_pushboolean(L, 1); + lua_setfield(L, 3, "force_place"); + } + lua_rawseti(L, 2, i + 1); + } + lua_setfield(L, 1, "data"); + + if (was_loaded) + delete schem; + + return 1; +} + + +void ModApiMapgen::Initialize(lua_State *L, int top) +{ + API_FCT(get_biome_id); + API_FCT(get_biome_name); + API_FCT(get_heat); + API_FCT(get_humidity); + API_FCT(get_biome_data); + API_FCT(get_mapgen_object); + API_FCT(get_spawn_level); + + API_FCT(get_mapgen_params); + API_FCT(set_mapgen_params); + API_FCT(get_mapgen_setting); + API_FCT(set_mapgen_setting); + API_FCT(get_mapgen_setting_noiseparams); + API_FCT(set_mapgen_setting_noiseparams); + API_FCT(set_noiseparams); + API_FCT(get_noiseparams); + API_FCT(set_gen_notify); + API_FCT(get_gen_notify); + API_FCT(get_decoration_id); + + API_FCT(register_biome); + API_FCT(register_decoration); + API_FCT(register_ore); + API_FCT(register_schematic); + + API_FCT(clear_registered_biomes); + API_FCT(clear_registered_decorations); + API_FCT(clear_registered_ores); + API_FCT(clear_registered_schematics); + + API_FCT(generate_ores); + API_FCT(generate_decorations); + API_FCT(create_schematic); + API_FCT(place_schematic); + API_FCT(place_schematic_on_vmanip); + API_FCT(serialize_schematic); + API_FCT(read_schematic); +} diff --git a/src/script/lua_api/l_mapgen.h b/src/script/lua_api/l_mapgen.h new file mode 100644 index 000000000..01f5f3f1e --- /dev/null +++ b/src/script/lua_api/l_mapgen.h @@ -0,0 +1,149 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" + +typedef u16 biome_t; // copy from mg_biome.h to avoid an unnecessary include + +class ModApiMapgen : public ModApiBase +{ +private: + // get_biome_id(biomename) + // returns the biome id as used in biomemap and returned by 'get_biome_data()' + static int l_get_biome_id(lua_State *L); + + // get_biome_name(biome_id) + // returns the biome name string + static int l_get_biome_name(lua_State *L); + + // get_heat(pos) + // returns the heat at the position + static int l_get_heat(lua_State *L); + + // get_humidity(pos) + // returns the humidity at the position + static int l_get_humidity(lua_State *L); + + // get_biome_data(pos) + // returns a table containing the biome id, heat and humidity at the position + static int l_get_biome_data(lua_State *L); + + // get_mapgen_object(objectname) + // returns the requested object used during map generation + static int l_get_mapgen_object(lua_State *L); + + // get_spawn_level(x = num, z = num) + static int l_get_spawn_level(lua_State *L); + + // get_mapgen_params() + // returns the currently active map generation parameter set + static int l_get_mapgen_params(lua_State *L); + + // set_mapgen_params(params) + // set mapgen parameters + static int l_set_mapgen_params(lua_State *L); + + // get_mapgen_setting(name) + static int l_get_mapgen_setting(lua_State *L); + + // set_mapgen_setting(name, value, override_meta) + static int l_set_mapgen_setting(lua_State *L); + + // get_mapgen_setting_noiseparams(name) + static int l_get_mapgen_setting_noiseparams(lua_State *L); + + // set_mapgen_setting_noiseparams(name, value, override_meta) + static int l_set_mapgen_setting_noiseparams(lua_State *L); + + // set_noiseparam_defaults(name, noiseparams, set_default) + static int l_set_noiseparams(lua_State *L); + + // get_noiseparam_defaults(name) + static int l_get_noiseparams(lua_State *L); + + // set_gen_notify(flags, {deco_id_table}) + static int l_set_gen_notify(lua_State *L); + + // get_gen_notify() + static int l_get_gen_notify(lua_State *L); + + // get_decoration_id(decoration_name) + // returns the decoration ID as used in gennotify + static int l_get_decoration_id(lua_State *L); + + // register_biome({lots of stuff}) + static int l_register_biome(lua_State *L); + + // register_decoration({lots of stuff}) + static int l_register_decoration(lua_State *L); + + // register_ore({lots of stuff}) + static int l_register_ore(lua_State *L); + + // register_schematic({schematic}, replacements={}) + static int l_register_schematic(lua_State *L); + + // clear_registered_biomes() + static int l_clear_registered_biomes(lua_State *L); + + // clear_registered_decorations() + static int l_clear_registered_decorations(lua_State *L); + + // clear_registered_schematics() + static int l_clear_registered_schematics(lua_State *L); + + // generate_ores(vm, p1, p2) + static int l_generate_ores(lua_State *L); + + // generate_decorations(vm, p1, p2) + static int l_generate_decorations(lua_State *L); + + // clear_registered_ores + static int l_clear_registered_ores(lua_State *L); + + // create_schematic(p1, p2, probability_list, filename) + static int l_create_schematic(lua_State *L); + + // place_schematic(p, schematic, rotation, + // replacements, force_placement, flagstring) + static int l_place_schematic(lua_State *L); + + // place_schematic_on_vmanip(vm, p, schematic, rotation, + // replacements, force_placement, flagstring) + static int l_place_schematic_on_vmanip(lua_State *L); + + // serialize_schematic(schematic, format, options={...}) + static int l_serialize_schematic(lua_State *L); + + // read_schematic(schematic, options={...}) + static int l_read_schematic(lua_State *L); + +public: + static void Initialize(lua_State *L, int top); + + static struct EnumString es_BiomeTerrainType[]; + static struct EnumString es_DecorationType[]; + static struct EnumString es_MapgenObject[]; + static struct EnumString es_OreType[]; + static struct EnumString es_Rotation[]; + static struct EnumString es_SchematicFormatType[]; + static struct EnumString es_NodeResolveMethod[]; +}; diff --git a/src/script/lua_api/l_metadata.cpp b/src/script/lua_api/l_metadata.cpp new file mode 100644 index 000000000..2b4d4dc2f --- /dev/null +++ b/src/script/lua_api/l_metadata.cpp @@ -0,0 +1,302 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017-8 rubenwardy + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_metadata.h" +#include "lua_api/l_internal.h" +#include "common/c_content.h" +#include "serverenvironment.h" +#include "map.h" +#include "server.h" + +// LUALIB_API +void *luaL_checkudata_is_metadataref(lua_State *L, int ud) { + void *p = lua_touserdata(L, ud); + if (p != NULL && // value is a userdata? + lua_getmetatable(L, ud)) { // does it have a metatable? + lua_getfield(L, -1, "metadata_class"); + if (lua_type(L, -1) == LUA_TSTRING) { // does it have a metadata_class field? + return p; + } + } + luaL_typerror(L, ud, "MetaDataRef"); + return NULL; +} + +MetaDataRef* MetaDataRef::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata_is_metadataref(L, narg); + if (!ud) + luaL_typerror(L, narg, "MetaDataRef"); + + return *(MetaDataRef**)ud; // unbox pointer +} + +// Exported functions + +// contains(self, name) +int MetaDataRef::l_contains(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + std::string name = luaL_checkstring(L, 2); + + Metadata *meta = ref->getmeta(false); + if (meta == NULL) + return 0; + + lua_pushboolean(L, meta->contains(name)); + return 1; +} + +// get(self, name) +int MetaDataRef::l_get(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + std::string name = luaL_checkstring(L, 2); + + Metadata *meta = ref->getmeta(false); + if (meta == NULL) + return 0; + + std::string str; + if (meta->getStringToRef(name, str)) { + lua_pushlstring(L, str.c_str(), str.size()); + return 1; + } + return 0; +} + +// get_string(self, name) +int MetaDataRef::l_get_string(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + std::string name = luaL_checkstring(L, 2); + + Metadata *meta = ref->getmeta(false); + if (meta == NULL) { + lua_pushlstring(L, "", 0); + return 1; + } + + const std::string &str = meta->getString(name); + lua_pushlstring(L, str.c_str(), str.size()); + return 1; +} + +// set_string(self, name, var) +int MetaDataRef::l_set_string(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + std::string name = luaL_checkstring(L, 2); + size_t len = 0; + const char *s = lua_tolstring(L, 3, &len); + std::string str(s, len); + + Metadata *meta = ref->getmeta(!str.empty()); + if (meta == NULL || str == meta->getString(name)) + return 0; + + meta->setString(name, str); + ref->reportMetadataChange(&name); + return 0; +} + +// get_int(self, name) +int MetaDataRef::l_get_int(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + std::string name = luaL_checkstring(L, 2); + + Metadata *meta = ref->getmeta(false); + if (meta == NULL) { + lua_pushnumber(L, 0); + return 1; + } + + const std::string &str = meta->getString(name); + lua_pushnumber(L, stoi(str)); + return 1; +} + +// set_int(self, name, var) +int MetaDataRef::l_set_int(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + std::string name = luaL_checkstring(L, 2); + int a = luaL_checkint(L, 3); + std::string str = itos(a); + + Metadata *meta = ref->getmeta(true); + if (meta == NULL || str == meta->getString(name)) + return 0; + + meta->setString(name, str); + ref->reportMetadataChange(&name); + return 0; +} + +// get_float(self, name) +int MetaDataRef::l_get_float(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + std::string name = luaL_checkstring(L, 2); + + Metadata *meta = ref->getmeta(false); + if (meta == NULL) { + lua_pushnumber(L, 0); + return 1; + } + + const std::string &str = meta->getString(name); + lua_pushnumber(L, stof(str)); + return 1; +} + +// set_float(self, name, var) +int MetaDataRef::l_set_float(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + std::string name = luaL_checkstring(L, 2); + float a = readParam(L, 3); + std::string str = ftos(a); + + Metadata *meta = ref->getmeta(true); + if (meta == NULL || str == meta->getString(name)) + return 0; + + meta->setString(name, str); + ref->reportMetadataChange(&name); + return 0; +} + +// to_table(self) +int MetaDataRef::l_to_table(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + + Metadata *meta = ref->getmeta(true); + if (meta == NULL) { + lua_pushnil(L); + return 1; + } + lua_newtable(L); + + ref->handleToTable(L, meta); + + return 1; +} + +// from_table(self, table) +int MetaDataRef::l_from_table(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + int base = 2; + + ref->clearMeta(); + + if (!lua_istable(L, base)) { + // No metadata + lua_pushboolean(L, true); + return 1; + } + + // Create new metadata + Metadata *meta = ref->getmeta(true); + if (meta == NULL) { + lua_pushboolean(L, false); + return 1; + } + + bool was_successful = ref->handleFromTable(L, base, meta); + ref->reportMetadataChange(); + lua_pushboolean(L, was_successful); + return 1; +} + +void MetaDataRef::handleToTable(lua_State *L, Metadata *meta) +{ + lua_newtable(L); + { + const StringMap &fields = meta->getStrings(); + for (const auto &field : fields) { + const std::string &name = field.first; + const std::string &value = field.second; + lua_pushlstring(L, name.c_str(), name.size()); + lua_pushlstring(L, value.c_str(), value.size()); + lua_settable(L, -3); + } + } + lua_setfield(L, -2, "fields"); +} + +bool MetaDataRef::handleFromTable(lua_State *L, int table, Metadata *meta) +{ + // Set fields + lua_getfield(L, table, "fields"); + if (lua_istable(L, -1)) { + int fieldstable = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, fieldstable) != 0) { + // key at index -2 and value at index -1 + std::string name = readParam(L, -2); + size_t cl; + const char *cs = lua_tolstring(L, -1, &cl); + meta->setString(name, std::string(cs, cl)); + lua_pop(L, 1); // Remove value, keep key for next iteration + } + lua_pop(L, 1); + } + + return true; +} + +// equals(self, other) +int MetaDataRef::l_equals(lua_State *L) +{ + MetaDataRef *ref1 = checkobject(L, 1); + Metadata *data1 = ref1->getmeta(false); + MetaDataRef *ref2 = checkobject(L, 2); + Metadata *data2 = ref2->getmeta(false); + if (data1 == NULL || data2 == NULL) + lua_pushboolean(L, data1 == data2); + else + lua_pushboolean(L, *data1 == *data2); + return 1; +} diff --git a/src/script/lua_api/l_metadata.h b/src/script/lua_api/l_metadata.h new file mode 100644 index 000000000..a4bec1594 --- /dev/null +++ b/src/script/lua_api/l_metadata.h @@ -0,0 +1,81 @@ +/* +Minetest +Copyright (C) 2013-8 celeron55, Perttu Ahola +Copyright (C) 2017-8 rubenwardy + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "irrlichttypes_bloated.h" +#include "lua_api/l_base.h" + +class Metadata; + +/* + NodeMetaRef +*/ + +class MetaDataRef : public ModApiBase +{ +public: + virtual ~MetaDataRef() = default; + +protected: + static MetaDataRef *checkobject(lua_State *L, int narg); + + virtual void reportMetadataChange(const std::string *name = nullptr) {} + virtual Metadata *getmeta(bool auto_create) = 0; + virtual void clearMeta() = 0; + + virtual void handleToTable(lua_State *L, Metadata *meta); + virtual bool handleFromTable(lua_State *L, int table, Metadata *meta); + + // Exported functions + + // contains(self, name) + static int l_contains(lua_State *L); + + // get(self, name) + static int l_get(lua_State *L); + + // get_string(self, name) + static int l_get_string(lua_State *L); + + // set_string(self, name, var) + static int l_set_string(lua_State *L); + + // get_int(self, name) + static int l_get_int(lua_State *L); + + // set_int(self, name, var) + static int l_set_int(lua_State *L); + + // get_float(self, name) + static int l_get_float(lua_State *L); + + // set_float(self, name, var) + static int l_set_float(lua_State *L); + + // to_table(self) + static int l_to_table(lua_State *L); + + // from_table(self, table) + static int l_from_table(lua_State *L); + + // equals(self, other) + static int l_equals(lua_State *L); +}; diff --git a/src/script/lua_api/l_minimap.cpp b/src/script/lua_api/l_minimap.cpp new file mode 100644 index 000000000..9c1e9946e --- /dev/null +++ b/src/script/lua_api/l_minimap.cpp @@ -0,0 +1,231 @@ +/* +Minetest +Copyright (C) 2017 Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#include "lua_api/l_minimap.h" +#include "lua_api/l_internal.h" +#include "common/c_converter.h" +#include "client/client.h" +#include "client/minimap.h" +#include "settings.h" + +LuaMinimap::LuaMinimap(Minimap *m) : m_minimap(m) +{ +} + +void LuaMinimap::create(lua_State *L, Minimap *m) +{ + LuaMinimap *o = new LuaMinimap(m); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + + // Keep minimap object stack id + int minimap_object = lua_gettop(L); + + lua_getglobal(L, "core"); + lua_getfield(L, -1, "ui"); + luaL_checktype(L, -1, LUA_TTABLE); + int uitable = lua_gettop(L); + + lua_pushvalue(L, minimap_object); // Copy object to top of stack + lua_setfield(L, uitable, "minimap"); +} + +int LuaMinimap::l_get_pos(lua_State *L) +{ + LuaMinimap *ref = checkobject(L, 1); + Minimap *m = getobject(ref); + + push_v3s16(L, m->getPos()); + return 1; +} + +int LuaMinimap::l_set_pos(lua_State *L) +{ + LuaMinimap *ref = checkobject(L, 1); + Minimap *m = getobject(ref); + + m->setPos(read_v3s16(L, 2)); + return 1; +} + +int LuaMinimap::l_get_angle(lua_State *L) +{ + LuaMinimap *ref = checkobject(L, 1); + Minimap *m = getobject(ref); + + lua_pushinteger(L, m->getAngle()); + return 1; +} + +int LuaMinimap::l_set_angle(lua_State *L) +{ + LuaMinimap *ref = checkobject(L, 1); + Minimap *m = getobject(ref); + + m->setAngle(lua_tointeger(L, 2)); + return 1; +} + +int LuaMinimap::l_get_mode(lua_State *L) +{ + LuaMinimap *ref = checkobject(L, 1); + Minimap *m = getobject(ref); + + lua_pushinteger(L, m->getModeIndex()); + return 1; +} + +int LuaMinimap::l_set_mode(lua_State *L) +{ + LuaMinimap *ref = checkobject(L, 1); + Minimap *m = getobject(ref); + + u32 mode = lua_tointeger(L, 2); + if (mode >= m->getMaxModeIndex()) + return 0; + + m->setModeIndex(mode); + return 1; +} + +int LuaMinimap::l_set_shape(lua_State *L) +{ + LuaMinimap *ref = checkobject(L, 1); + Minimap *m = getobject(ref); + if (!lua_isnumber(L, 2)) + return 0; + + m->setMinimapShape((MinimapShape)((int)lua_tonumber(L, 2))); + return 0; +} + +int LuaMinimap::l_get_shape(lua_State *L) +{ + LuaMinimap *ref = checkobject(L, 1); + Minimap *m = getobject(ref); + + lua_pushnumber(L, (int)m->getMinimapShape()); + return 1; +} + +int LuaMinimap::l_show(lua_State *L) +{ + // If minimap is disabled by config, don't show it. + if (!g_settings->getBool("enable_minimap")) + return 1; + + Client *client = getClient(L); + assert(client); + + LuaMinimap *ref = checkobject(L, 1); + Minimap *m = getobject(ref); + + // This is not very adapted to new minimap mode management. Btw, tried + // to do something compatible. + + if (m->getModeIndex() == 0 && m->getMaxModeIndex() > 0) + m->setModeIndex(1); + + client->showMinimap(true); + return 1; +} + +int LuaMinimap::l_hide(lua_State *L) +{ + Client *client = getClient(L); + assert(client); + + LuaMinimap *ref = checkobject(L, 1); + Minimap *m = getobject(ref); + + // This is not very adapted to new minimap mode management. Btw, tried + // to do something compatible. + + if (m->getModeIndex() != 0) + m->setModeIndex(0); + + client->showMinimap(false); + return 1; +} + +LuaMinimap *LuaMinimap::checkobject(lua_State *L, int narg) +{ + NO_MAP_LOCK_REQUIRED; + + luaL_checktype(L, narg, LUA_TUSERDATA); + + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + + return *(LuaMinimap **)ud; // unbox pointer +} + +Minimap* LuaMinimap::getobject(LuaMinimap *ref) +{ + return ref->m_minimap; +} + +int LuaMinimap::gc_object(lua_State *L) { + LuaMinimap *o = *(LuaMinimap **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +void LuaMinimap::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable +} + +const char LuaMinimap::className[] = "Minimap"; +const luaL_Reg LuaMinimap::methods[] = { + luamethod(LuaMinimap, show), + luamethod(LuaMinimap, hide), + luamethod(LuaMinimap, get_pos), + luamethod(LuaMinimap, set_pos), + luamethod(LuaMinimap, get_angle), + luamethod(LuaMinimap, set_angle), + luamethod(LuaMinimap, get_mode), + luamethod(LuaMinimap, set_mode), + luamethod(LuaMinimap, set_shape), + luamethod(LuaMinimap, get_shape), + {0,0} +}; diff --git a/src/script/lua_api/l_minimap.h b/src/script/lua_api/l_minimap.h new file mode 100644 index 000000000..4a8394461 --- /dev/null +++ b/src/script/lua_api/l_minimap.h @@ -0,0 +1,62 @@ +/* +Minetest +Copyright (C) 2017 Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "l_base.h" + +class Minimap; + +class LuaMinimap : public ModApiBase +{ +private: + static const char className[]; + static const luaL_Reg methods[]; + + // garbage collector + static int gc_object(lua_State *L); + + static int l_get_pos(lua_State *L); + static int l_set_pos(lua_State *L); + + static int l_get_angle(lua_State *L); + static int l_set_angle(lua_State *L); + + static int l_get_mode(lua_State *L); + static int l_set_mode(lua_State *L); + + static int l_show(lua_State *L); + static int l_hide(lua_State *L); + + static int l_set_shape(lua_State *L); + static int l_get_shape(lua_State *L); + + Minimap *m_minimap = nullptr; + +public: + LuaMinimap(Minimap *m); + ~LuaMinimap() = default; + + static void create(lua_State *L, Minimap *object); + + static LuaMinimap *checkobject(lua_State *L, int narg); + static Minimap *getobject(LuaMinimap *ref); + + static void Register(lua_State *L); +}; diff --git a/src/script/lua_api/l_modchannels.cpp b/src/script/lua_api/l_modchannels.cpp new file mode 100644 index 000000000..d311a2002 --- /dev/null +++ b/src/script/lua_api/l_modchannels.cpp @@ -0,0 +1,153 @@ +/* +Minetest +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include "lua_api/l_modchannels.h" +#include "l_internal.h" +#include "modchannels.h" + +int ModApiChannels::l_mod_channel_join(lua_State *L) +{ + if (!lua_isstring(L, 1)) + return 0; + + std::string channel = luaL_checkstring(L, 1); + if (channel.empty()) + return 0; + + getGameDef(L)->joinModChannel(channel); + assert(getGameDef(L)->getModChannel(channel) != nullptr); + ModChannelRef::create(L, channel); + + int object = lua_gettop(L); + lua_pushvalue(L, object); + return 1; +} + +void ModApiChannels::Initialize(lua_State *L, int top) +{ + API_FCT(mod_channel_join); +} + +/* + * ModChannelRef + */ + +ModChannelRef::ModChannelRef(const std::string &modchannel) : + m_modchannel_name(modchannel) +{ +} + +int ModChannelRef::l_leave(lua_State *L) +{ + ModChannelRef *ref = checkobject(L, 1); + getGameDef(L)->leaveModChannel(ref->m_modchannel_name); + return 0; +} + +int ModChannelRef::l_send_all(lua_State *L) +{ + ModChannelRef *ref = checkobject(L, 1); + ModChannel *channel = getobject(L, ref); + if (!channel || !channel->canWrite()) + return 0; + + // @TODO serialize message + std::string message = luaL_checkstring(L, 2); + + getGameDef(L)->sendModChannelMessage(channel->getName(), message); + return 0; +} + +int ModChannelRef::l_is_writeable(lua_State *L) +{ + ModChannelRef *ref = checkobject(L, 1); + ModChannel *channel = getobject(L, ref); + if (!channel) + return 0; + + lua_pushboolean(L, channel->canWrite()); + return 1; +} +void ModChannelRef::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // Drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // Drop methodtable +} + +void ModChannelRef::create(lua_State *L, const std::string &channel) +{ + ModChannelRef *o = new ModChannelRef(channel); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); +} + +int ModChannelRef::gc_object(lua_State *L) +{ + ModChannelRef *o = *(ModChannelRef **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +ModChannelRef *ModChannelRef::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + + return *(ModChannelRef **)ud; // unbox pointer +} + +ModChannel *ModChannelRef::getobject(lua_State *L, ModChannelRef *ref) +{ + return getGameDef(L)->getModChannel(ref->m_modchannel_name); +} + +// clang-format off +const char ModChannelRef::className[] = "ModChannelRef"; +const luaL_Reg ModChannelRef::methods[] = { + luamethod(ModChannelRef, leave), + luamethod(ModChannelRef, is_writeable), + luamethod(ModChannelRef, send_all), + {0, 0}, +}; +// clang-format on diff --git a/src/script/lua_api/l_modchannels.h b/src/script/lua_api/l_modchannels.h new file mode 100644 index 000000000..333ebc266 --- /dev/null +++ b/src/script/lua_api/l_modchannels.h @@ -0,0 +1,66 @@ +/* +Minetest +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" +#include "config.h" + +class ModChannel; + +class ModApiChannels : public ModApiBase +{ +private: + // mod_channel_join(name) + static int l_mod_channel_join(lua_State *L); + +public: + static void Initialize(lua_State *L, int top); +}; + +class ModChannelRef : public ModApiBase +{ +public: + ModChannelRef(const std::string &modchannel); + ~ModChannelRef() = default; + + static void Register(lua_State *L); + static void create(lua_State *L, const std::string &channel); + + // leave() + static int l_leave(lua_State *L); + + // send(message) + static int l_send_all(lua_State *L); + + // is_writeable() + static int l_is_writeable(lua_State *L); + +private: + // garbage collector + static int gc_object(lua_State *L); + + static ModChannelRef *checkobject(lua_State *L, int narg); + static ModChannel *getobject(lua_State *L, ModChannelRef *ref); + + std::string m_modchannel_name; + + static const char className[]; + static const luaL_Reg methods[]; +}; diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp new file mode 100644 index 000000000..184f3c22f --- /dev/null +++ b/src/script/lua_api/l_nodemeta.cpp @@ -0,0 +1,276 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_nodemeta.h" +#include "lua_api/l_internal.h" +#include "lua_api/l_inventory.h" +#include "common/c_content.h" +#include "serverenvironment.h" +#include "map.h" +#include "mapblock.h" +#include "server.h" + +/* + NodeMetaRef +*/ +NodeMetaRef* NodeMetaRef::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if(!ud) luaL_typerror(L, narg, className); + return *(NodeMetaRef**)ud; // unbox pointer +} + +Metadata* NodeMetaRef::getmeta(bool auto_create) +{ + if (m_is_local) + return m_meta; + + NodeMetadata *meta = m_env->getMap().getNodeMetadata(m_p); + if (meta == NULL && auto_create) { + meta = new NodeMetadata(m_env->getGameDef()->idef()); + if (!m_env->getMap().setNodeMetadata(m_p, meta)) { + delete meta; + return NULL; + } + } + return meta; +} + +void NodeMetaRef::clearMeta() +{ + SANITY_CHECK(!m_is_local); + m_env->getMap().removeNodeMetadata(m_p); +} + +void NodeMetaRef::reportMetadataChange(const std::string *name) +{ + SANITY_CHECK(!m_is_local); + // NOTE: This same code is in rollback_interface.cpp + // Inform other things that the metadata has changed + NodeMetadata *meta = dynamic_cast(m_meta); + + MapEditEvent event; + event.type = MEET_BLOCK_NODE_METADATA_CHANGED; + event.p = m_p; + event.is_private_change = name && meta && meta->isPrivate(*name); + m_env->getMap().dispatchEvent(event); +} + +// Exported functions + +// garbage collector +int NodeMetaRef::gc_object(lua_State *L) { + NodeMetaRef *o = *(NodeMetaRef **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +// get_inventory(self) +int NodeMetaRef::l_get_inventory(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + NodeMetaRef *ref = checkobject(L, 1); + ref->getmeta(true); // try to ensure the metadata exists + InvRef::createNodeMeta(L, ref->m_p); + return 1; +} + +// mark_as_private(self, or {, , ...}) +int NodeMetaRef::l_mark_as_private(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + NodeMetaRef *ref = checkobject(L, 1); + NodeMetadata *meta = dynamic_cast(ref->getmeta(true)); + assert(meta); + + if (lua_istable(L, 2)) { + lua_pushnil(L); + while (lua_next(L, 2) != 0) { + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TSTRING); + meta->markPrivate(readParam(L, -1), true); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } else if (lua_isstring(L, 2)) { + meta->markPrivate(readParam(L, 2), true); + } + ref->reportMetadataChange(); + + return 0; +} + +void NodeMetaRef::handleToTable(lua_State *L, Metadata *_meta) +{ + // fields + MetaDataRef::handleToTable(L, _meta); + + NodeMetadata *meta = (NodeMetadata*) _meta; + + // inventory + lua_newtable(L); + Inventory *inv = meta->getInventory(); + if (inv) { + std::vector lists = inv->getLists(); + for(std::vector::const_iterator + i = lists.begin(); i != lists.end(); ++i) { + push_inventory_list(L, inv, (*i)->getName().c_str()); + lua_setfield(L, -2, (*i)->getName().c_str()); + } + } + lua_setfield(L, -2, "inventory"); +} + +// from_table(self, table) +bool NodeMetaRef::handleFromTable(lua_State *L, int table, Metadata *_meta) +{ + // fields + if (!MetaDataRef::handleFromTable(L, table, _meta)) + return false; + + NodeMetadata *meta = (NodeMetadata*) _meta; + + // inventory + Inventory *inv = meta->getInventory(); + lua_getfield(L, table, "inventory"); + if (lua_istable(L, -1)) { + int inventorytable = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, inventorytable) != 0) { + // key at index -2 and value at index -1 + std::string name = luaL_checkstring(L, -2); + read_inventory_list(L, -1, inv, name.c_str(), getServer(L)); + lua_pop(L, 1); // Remove value, keep key for next iteration + } + lua_pop(L, 1); + } + + return true; +} + + +NodeMetaRef::NodeMetaRef(v3s16 p, ServerEnvironment *env): + m_p(p), + m_env(env) +{ +} + +NodeMetaRef::NodeMetaRef(Metadata *meta): + m_meta(meta), + m_is_local(true) +{ +} + +// Creates an NodeMetaRef and leaves it on top of stack +// Not callable from Lua; all references are created on the C side. +void NodeMetaRef::create(lua_State *L, v3s16 p, ServerEnvironment *env) +{ + NodeMetaRef *o = new NodeMetaRef(p, env); + //infostream<<"NodeMetaRef::create: o="< + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" +#include "lua_api/l_metadata.h" +#include "irrlichttypes_bloated.h" +#include "nodemetadata.h" + +class ServerEnvironment; +class NodeMetadata; + +/* + NodeMetaRef +*/ + +class NodeMetaRef : public MetaDataRef { +private: + v3s16 m_p; + ServerEnvironment *m_env = nullptr; + Metadata *m_meta = nullptr; + bool m_is_local = false; + + static const char className[]; + static const luaL_Reg methodsServer[]; + static const luaL_Reg methodsClient[]; + + static NodeMetaRef *checkobject(lua_State *L, int narg); + + /** + * Retrieve metadata for a node. + * If @p auto_create is set and the specified node has no metadata information + * associated with it yet, the method attempts to attach a new metadata object + * to the node and returns a pointer to the metadata when successful. + * + * However, it is NOT guaranteed that the method will return a pointer, + * and @c NULL may be returned in case of an error regardless of @p auto_create. + * + * @param ref specifies the node for which the associated metadata is retrieved. + * @param auto_create when true, try to create metadata information for the node if it has none. + * @return pointer to a @c NodeMetadata object or @c NULL in case of error. + */ + virtual Metadata* getmeta(bool auto_create); + virtual void clearMeta(); + + virtual void reportMetadataChange(const std::string *name = nullptr); + + virtual void handleToTable(lua_State *L, Metadata *_meta); + virtual bool handleFromTable(lua_State *L, int table, Metadata *_meta); + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // get_inventory(self) + static int l_get_inventory(lua_State *L); + + // mark_as_private(self, or {, , ...}) + static int l_mark_as_private(lua_State *L); + +public: + NodeMetaRef(v3s16 p, ServerEnvironment *env); + NodeMetaRef(Metadata *meta); + + ~NodeMetaRef() = default; + + // Creates an NodeMetaRef and leaves it on top of stack + // Not callable from Lua; all references are created on the C side. + static void create(lua_State *L, v3s16 p, ServerEnvironment *env); + + // Client-sided version of the above + static void createClient(lua_State *L, Metadata *meta); + + static void RegisterCommon(lua_State *L); + static void Register(lua_State *L); + static void RegisterClient(lua_State *L); +}; diff --git a/src/script/lua_api/l_nodetimer.cpp b/src/script/lua_api/l_nodetimer.cpp new file mode 100644 index 000000000..57f8b9785 --- /dev/null +++ b/src/script/lua_api/l_nodetimer.cpp @@ -0,0 +1,141 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_nodetimer.h" +#include "lua_api/l_internal.h" +#include "serverenvironment.h" +#include "map.h" + + +int NodeTimerRef::gc_object(lua_State *L) { + NodeTimerRef *o = *(NodeTimerRef **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +NodeTimerRef* NodeTimerRef::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if(!ud) luaL_typerror(L, narg, className); + return *(NodeTimerRef**)ud; // unbox pointer +} + +int NodeTimerRef::l_set(lua_State *L) +{ + MAP_LOCK_REQUIRED; + NodeTimerRef *o = checkobject(L, 1); + f32 t = readParam(L,2); + f32 e = readParam(L,3); + o->m_map->setNodeTimer(NodeTimer(t, e, o->m_p)); + return 0; +} + +int NodeTimerRef::l_start(lua_State *L) +{ + MAP_LOCK_REQUIRED; + NodeTimerRef *o = checkobject(L, 1); + f32 t = readParam(L,2); + o->m_map->setNodeTimer(NodeTimer(t, 0, o->m_p)); + return 0; +} + +int NodeTimerRef::l_stop(lua_State *L) +{ + MAP_LOCK_REQUIRED; + NodeTimerRef *o = checkobject(L, 1); + o->m_map->removeNodeTimer(o->m_p); + return 0; +} + +int NodeTimerRef::l_is_started(lua_State *L) +{ + MAP_LOCK_REQUIRED; + NodeTimerRef *o = checkobject(L, 1); + NodeTimer t = o->m_map->getNodeTimer(o->m_p); + lua_pushboolean(L,(t.timeout != 0)); + return 1; +} + +int NodeTimerRef::l_get_timeout(lua_State *L) +{ + MAP_LOCK_REQUIRED; + NodeTimerRef *o = checkobject(L, 1); + NodeTimer t = o->m_map->getNodeTimer(o->m_p); + lua_pushnumber(L,t.timeout); + return 1; +} + +int NodeTimerRef::l_get_elapsed(lua_State *L) +{ + MAP_LOCK_REQUIRED; + NodeTimerRef *o = checkobject(L, 1); + NodeTimer t = o->m_map->getNodeTimer(o->m_p); + lua_pushnumber(L,t.elapsed); + return 1; +} + +// Creates an NodeTimerRef and leaves it on top of stack +// Not callable from Lua; all references are created on the C side. +void NodeTimerRef::create(lua_State *L, v3s16 p, ServerMap *map) +{ + NodeTimerRef *o = new NodeTimerRef(p, map); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); +} + +void NodeTimerRef::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Cannot be created from Lua + //lua_register(L, className, create_object); +} + +const char NodeTimerRef::className[] = "NodeTimerRef"; +const luaL_Reg NodeTimerRef::methods[] = { + luamethod(NodeTimerRef, start), + luamethod(NodeTimerRef, set), + luamethod(NodeTimerRef, stop), + luamethod(NodeTimerRef, is_started), + luamethod(NodeTimerRef, get_timeout), + luamethod(NodeTimerRef, get_elapsed), + {0,0} +}; diff --git a/src/script/lua_api/l_nodetimer.h b/src/script/lua_api/l_nodetimer.h new file mode 100644 index 000000000..4d3dbb27e --- /dev/null +++ b/src/script/lua_api/l_nodetimer.h @@ -0,0 +1,61 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "irr_v3d.h" +#include "lua_api/l_base.h" + +class ServerMap; + +class NodeTimerRef : public ModApiBase +{ +private: + v3s16 m_p; + ServerMap *m_map; + + static const char className[]; + static const luaL_Reg methods[]; + + static int gc_object(lua_State *L); + + static NodeTimerRef *checkobject(lua_State *L, int narg); + + static int l_set(lua_State *L); + + static int l_start(lua_State *L); + + static int l_stop(lua_State *L); + + static int l_is_started(lua_State *L); + + static int l_get_timeout(lua_State *L); + + static int l_get_elapsed(lua_State *L); + +public: + NodeTimerRef(v3s16 p, ServerMap *map) : m_p(p), m_map(map) {} + ~NodeTimerRef() = default; + + // Creates an NodeTimerRef and leaves it on top of stack + // Not callable from Lua; all references are created on the C side. + static void create(lua_State *L, v3s16 p, ServerMap *map); + + static void Register(lua_State *L); +}; diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp new file mode 100644 index 000000000..3f943ebba --- /dev/null +++ b/src/script/lua_api/l_noise.cpp @@ -0,0 +1,712 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_noise.h" +#include "lua_api/l_internal.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "log.h" +#include "porting.h" +#include "util/numeric.h" + +/////////////////////////////////////// +/* + LuaPerlinNoise +*/ + +LuaPerlinNoise::LuaPerlinNoise(NoiseParams *params) : + np(*params) +{ +} + + +int LuaPerlinNoise::l_get_2d(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaPerlinNoise *o = checkobject(L, 1); + v2f p = readParam(L, 2); + lua_Number val = NoisePerlin2D(&o->np, p.X, p.Y, 0); + lua_pushnumber(L, val); + return 1; +} + + +int LuaPerlinNoise::l_get_3d(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaPerlinNoise *o = checkobject(L, 1); + v3f p = check_v3f(L, 2); + lua_Number val = NoisePerlin3D(&o->np, p.X, p.Y, p.Z, 0); + lua_pushnumber(L, val); + return 1; +} + + +int LuaPerlinNoise::create_object(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + NoiseParams params; + + if (lua_istable(L, 1)) { + read_noiseparams(L, 1, ¶ms); + } else { + params.seed = luaL_checkint(L, 1); + params.octaves = luaL_checkint(L, 2); + params.persist = readParam(L, 3); + params.spread = v3f(1, 1, 1) * readParam(L, 4); + } + + LuaPerlinNoise *o = new LuaPerlinNoise(¶ms); + + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + + +int LuaPerlinNoise::gc_object(lua_State *L) +{ + LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + + +LuaPerlinNoise *LuaPerlinNoise::checkobject(lua_State *L, int narg) +{ + NO_MAP_LOCK_REQUIRED; + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + return *(LuaPerlinNoise **)ud; +} + + +void LuaPerlinNoise::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); + + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); + + lua_register(L, className, create_object); +} + + +const char LuaPerlinNoise::className[] = "PerlinNoise"; +luaL_Reg LuaPerlinNoise::methods[] = { + luamethod_aliased(LuaPerlinNoise, get_2d, get2d), + luamethod_aliased(LuaPerlinNoise, get_3d, get3d), + {0,0} +}; + +/////////////////////////////////////// +/* + LuaPerlinNoiseMap +*/ + +LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *params, s32 seed, v3s16 size) +{ + m_is3d = size.Z > 1; + np = *params; + try { + noise = new Noise(&np, seed, size.X, size.Y, size.Z); + } catch (InvalidNoiseParamsException &e) { + throw LuaError(e.what()); + } +} + + +LuaPerlinNoiseMap::~LuaPerlinNoiseMap() +{ + delete noise; +} + + +int LuaPerlinNoiseMap::l_get_2d_map(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + size_t i = 0; + + LuaPerlinNoiseMap *o = checkobject(L, 1); + v2f p = readParam(L, 2); + + Noise *n = o->noise; + n->perlinMap2D(p.X, p.Y); + + lua_createtable(L, n->sy, 0); + for (u32 y = 0; y != n->sy; y++) { + lua_createtable(L, n->sx, 0); + for (u32 x = 0; x != n->sx; x++) { + lua_pushnumber(L, n->result[i++]); + lua_rawseti(L, -2, x + 1); + } + lua_rawseti(L, -2, y + 1); + } + return 1; +} + + +int LuaPerlinNoiseMap::l_get_2d_map_flat(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaPerlinNoiseMap *o = checkobject(L, 1); + v2f p = readParam(L, 2); + bool use_buffer = lua_istable(L, 3); + + Noise *n = o->noise; + n->perlinMap2D(p.X, p.Y); + + size_t maplen = n->sx * n->sy; + + if (use_buffer) + lua_pushvalue(L, 3); + else + lua_createtable(L, maplen, 0); + + for (size_t i = 0; i != maplen; i++) { + lua_pushnumber(L, n->result[i]); + lua_rawseti(L, -2, i + 1); + } + return 1; +} + + +int LuaPerlinNoiseMap::l_get_3d_map(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + size_t i = 0; + + LuaPerlinNoiseMap *o = checkobject(L, 1); + v3f p = check_v3f(L, 2); + + if (!o->m_is3d) + return 0; + + Noise *n = o->noise; + n->perlinMap3D(p.X, p.Y, p.Z); + + lua_createtable(L, n->sz, 0); + for (u32 z = 0; z != n->sz; z++) { + lua_createtable(L, n->sy, 0); + for (u32 y = 0; y != n->sy; y++) { + lua_createtable(L, n->sx, 0); + for (u32 x = 0; x != n->sx; x++) { + lua_pushnumber(L, n->result[i++]); + lua_rawseti(L, -2, x + 1); + } + lua_rawseti(L, -2, y + 1); + } + lua_rawseti(L, -2, z + 1); + } + return 1; +} + + +int LuaPerlinNoiseMap::l_get_3d_map_flat(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaPerlinNoiseMap *o = checkobject(L, 1); + v3f p = check_v3f(L, 2); + bool use_buffer = lua_istable(L, 3); + + if (!o->m_is3d) + return 0; + + Noise *n = o->noise; + n->perlinMap3D(p.X, p.Y, p.Z); + + size_t maplen = n->sx * n->sy * n->sz; + + if (use_buffer) + lua_pushvalue(L, 3); + else + lua_createtable(L, maplen, 0); + + for (size_t i = 0; i != maplen; i++) { + lua_pushnumber(L, n->result[i]); + lua_rawseti(L, -2, i + 1); + } + return 1; +} + + +int LuaPerlinNoiseMap::l_calc_2d_map(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaPerlinNoiseMap *o = checkobject(L, 1); + v2f p = readParam(L, 2); + + Noise *n = o->noise; + n->perlinMap2D(p.X, p.Y); + + return 0; +} + +int LuaPerlinNoiseMap::l_calc_3d_map(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaPerlinNoiseMap *o = checkobject(L, 1); + v3f p = check_v3f(L, 2); + + if (!o->m_is3d) + return 0; + + Noise *n = o->noise; + n->perlinMap3D(p.X, p.Y, p.Z); + + return 0; +} + + +int LuaPerlinNoiseMap::l_get_map_slice(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaPerlinNoiseMap *o = checkobject(L, 1); + v3s16 slice_offset = read_v3s16(L, 2); + v3s16 slice_size = read_v3s16(L, 3); + bool use_buffer = lua_istable(L, 4); + + Noise *n = o->noise; + + if (use_buffer) + lua_pushvalue(L, 4); + else + lua_newtable(L); + + write_array_slice_float(L, lua_gettop(L), n->result, + v3u16(n->sx, n->sy, n->sz), + v3u16(slice_offset.X, slice_offset.Y, slice_offset.Z), + v3u16(slice_size.X, slice_size.Y, slice_size.Z)); + + return 1; +} + + +int LuaPerlinNoiseMap::create_object(lua_State *L) +{ + NoiseParams np; + if (!read_noiseparams(L, 1, &np)) + return 0; + v3s16 size = read_v3s16(L, 2); + + LuaPerlinNoiseMap *o = new LuaPerlinNoiseMap(&np, 0, size); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + + +int LuaPerlinNoiseMap::gc_object(lua_State *L) +{ + LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + + +LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + + return *(LuaPerlinNoiseMap **)ud; +} + + +void LuaPerlinNoiseMap::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); + + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); + + lua_register(L, className, create_object); +} + + +const char LuaPerlinNoiseMap::className[] = "PerlinNoiseMap"; +luaL_Reg LuaPerlinNoiseMap::methods[] = { + luamethod_aliased(LuaPerlinNoiseMap, get_2d_map, get2dMap), + luamethod_aliased(LuaPerlinNoiseMap, get_2d_map_flat, get2dMap_flat), + luamethod_aliased(LuaPerlinNoiseMap, calc_2d_map, calc2dMap), + luamethod_aliased(LuaPerlinNoiseMap, get_3d_map, get3dMap), + luamethod_aliased(LuaPerlinNoiseMap, get_3d_map_flat, get3dMap_flat), + luamethod_aliased(LuaPerlinNoiseMap, calc_3d_map, calc3dMap), + luamethod_aliased(LuaPerlinNoiseMap, get_map_slice, getMapSlice), + {0,0} +}; + +/////////////////////////////////////// +/* + LuaPseudoRandom +*/ + +int LuaPseudoRandom::l_next(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaPseudoRandom *o = checkobject(L, 1); + int min = 0; + int max = 32767; + lua_settop(L, 3); + if (lua_isnumber(L, 2)) + min = luaL_checkinteger(L, 2); + if (lua_isnumber(L, 3)) + max = luaL_checkinteger(L, 3); + if (max < min) { + errorstream<<"PseudoRandom.next(): max="< 32767/5) + throw LuaError("PseudoRandom.next() max-min is not 32767" + " and is > 32768/5. This is disallowed due to" + " the bad random distribution the" + " implementation would otherwise make."); + PseudoRandom &pseudo = o->m_pseudo; + int val = pseudo.next(); + val = (val % (max-min+1)) + min; + lua_pushinteger(L, val); + return 1; +} + + +int LuaPseudoRandom::create_object(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + u64 seed = luaL_checknumber(L, 1); + LuaPseudoRandom *o = new LuaPseudoRandom(seed); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + + +int LuaPseudoRandom::gc_object(lua_State *L) +{ + LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + + +LuaPseudoRandom *LuaPseudoRandom::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + return *(LuaPseudoRandom **)ud; +} + + +void LuaPseudoRandom::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); + + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); + + lua_register(L, className, create_object); +} + + +const char LuaPseudoRandom::className[] = "PseudoRandom"; +const luaL_Reg LuaPseudoRandom::methods[] = { + luamethod(LuaPseudoRandom, next), + {0,0} +}; + +/////////////////////////////////////// +/* + LuaPcgRandom +*/ + +int LuaPcgRandom::l_next(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaPcgRandom *o = checkobject(L, 1); + u32 min = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : o->m_rnd.RANDOM_MIN; + u32 max = lua_isnumber(L, 3) ? lua_tointeger(L, 3) : o->m_rnd.RANDOM_MAX; + + lua_pushinteger(L, o->m_rnd.range(min, max)); + return 1; +} + + +int LuaPcgRandom::l_rand_normal_dist(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaPcgRandom *o = checkobject(L, 1); + u32 min = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : o->m_rnd.RANDOM_MIN; + u32 max = lua_isnumber(L, 3) ? lua_tointeger(L, 3) : o->m_rnd.RANDOM_MAX; + int num_trials = lua_isnumber(L, 4) ? lua_tointeger(L, 4) : 6; + + lua_pushinteger(L, o->m_rnd.randNormalDist(min, max, num_trials)); + return 1; +} + + +int LuaPcgRandom::create_object(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + u64 seed = luaL_checknumber(L, 1); + LuaPcgRandom *o = lua_isnumber(L, 2) ? + new LuaPcgRandom(seed, lua_tointeger(L, 2)) : + new LuaPcgRandom(seed); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + + +int LuaPcgRandom::gc_object(lua_State *L) +{ + LuaPcgRandom *o = *(LuaPcgRandom **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + + +LuaPcgRandom *LuaPcgRandom::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + return *(LuaPcgRandom **)ud; +} + + +void LuaPcgRandom::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); + + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); + + lua_register(L, className, create_object); +} + + +const char LuaPcgRandom::className[] = "PcgRandom"; +const luaL_Reg LuaPcgRandom::methods[] = { + luamethod(LuaPcgRandom, next), + luamethod(LuaPcgRandom, rand_normal_dist), + {0,0} +}; + +/////////////////////////////////////// +/* + LuaSecureRandom +*/ + +bool LuaSecureRandom::fillRandBuf() +{ + return porting::secure_rand_fill_buf(m_rand_buf, RAND_BUF_SIZE); +} + +int LuaSecureRandom::l_next_bytes(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaSecureRandom *o = checkobject(L, 1); + u32 count = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : 1; + + // Limit count + count = MYMIN(RAND_BUF_SIZE, count); + + // Find out whether we can pass directly from our array, or have to do some gluing + size_t count_remaining = RAND_BUF_SIZE - o->m_rand_idx; + if (count_remaining >= count) { + lua_pushlstring(L, o->m_rand_buf + o->m_rand_idx, count); + o->m_rand_idx += count; + } else { + char output_buf[RAND_BUF_SIZE]; + + // Copy over with what we have left from our current buffer + memcpy(output_buf, o->m_rand_buf + o->m_rand_idx, count_remaining); + + // Refill buffer and copy over the remainder of what was requested + o->fillRandBuf(); + memcpy(output_buf + count_remaining, o->m_rand_buf, count - count_remaining); + + // Update index + o->m_rand_idx = count - count_remaining; + + lua_pushlstring(L, output_buf, count); + } + + return 1; +} + + +int LuaSecureRandom::create_object(lua_State *L) +{ + LuaSecureRandom *o = new LuaSecureRandom(); + + // Fail and return nil if we can't securely fill the buffer + if (!o->fillRandBuf()) { + delete o; + return 0; + } + + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + + +int LuaSecureRandom::gc_object(lua_State *L) +{ + LuaSecureRandom *o = *(LuaSecureRandom **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + + +LuaSecureRandom *LuaSecureRandom::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + return *(LuaSecureRandom **)ud; +} + + +void LuaSecureRandom::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); + + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); + + lua_register(L, className, create_object); +} + +const char LuaSecureRandom::className[] = "SecureRandom"; +const luaL_Reg LuaSecureRandom::methods[] = { + luamethod(LuaSecureRandom, next_bytes), + {0,0} +}; diff --git a/src/script/lua_api/l_noise.h b/src/script/lua_api/l_noise.h new file mode 100644 index 000000000..e64aa4190 --- /dev/null +++ b/src/script/lua_api/l_noise.h @@ -0,0 +1,194 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "irr_v3d.h" +#include "lua_api/l_base.h" +#include "noise.h" + +/* + LuaPerlinNoise +*/ +class LuaPerlinNoise : public ModApiBase +{ +private: + NoiseParams np; + static const char className[]; + static luaL_Reg methods[]; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + static int l_get_2d(lua_State *L); + static int l_get_3d(lua_State *L); + +public: + LuaPerlinNoise(NoiseParams *params); + ~LuaPerlinNoise() = default; + + // LuaPerlinNoise(seed, octaves, persistence, scale) + // Creates an LuaPerlinNoise and leaves it on top of stack + static int create_object(lua_State *L); + + static LuaPerlinNoise *checkobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; + +/* + LuaPerlinNoiseMap +*/ +class LuaPerlinNoiseMap : public ModApiBase +{ + NoiseParams np; + Noise *noise; + bool m_is3d; + static const char className[]; + static luaL_Reg methods[]; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + static int l_get_2d_map(lua_State *L); + static int l_get_2d_map_flat(lua_State *L); + static int l_get_3d_map(lua_State *L); + static int l_get_3d_map_flat(lua_State *L); + + static int l_calc_2d_map(lua_State *L); + static int l_calc_3d_map(lua_State *L); + static int l_get_map_slice(lua_State *L); + +public: + LuaPerlinNoiseMap(NoiseParams *np, s32 seed, v3s16 size); + + ~LuaPerlinNoiseMap(); + + // LuaPerlinNoiseMap(np, size) + // Creates an LuaPerlinNoiseMap and leaves it on top of stack + static int create_object(lua_State *L); + + static LuaPerlinNoiseMap *checkobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; + +/* + LuaPseudoRandom +*/ +class LuaPseudoRandom : public ModApiBase +{ +private: + PseudoRandom m_pseudo; + + static const char className[]; + static const luaL_Reg methods[]; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // next(self, min=0, max=32767) -> get next value + static int l_next(lua_State *L); + +public: + LuaPseudoRandom(s32 seed) : m_pseudo(seed) {} + + // LuaPseudoRandom(seed) + // Creates an LuaPseudoRandom and leaves it on top of stack + static int create_object(lua_State *L); + + static LuaPseudoRandom *checkobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; + +/* + LuaPcgRandom +*/ +class LuaPcgRandom : public ModApiBase +{ +private: + PcgRandom m_rnd; + + static const char className[]; + static const luaL_Reg methods[]; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // next(self, min=-2147483648, max=2147483647) -> get next value + static int l_next(lua_State *L); + + // rand_normal_dist(self, min=-2147483648, max=2147483647, num_trials=6) -> + // get next normally distributed random value + static int l_rand_normal_dist(lua_State *L); + +public: + LuaPcgRandom(u64 seed) : m_rnd(seed) {} + LuaPcgRandom(u64 seed, u64 seq) : m_rnd(seed, seq) {} + + // LuaPcgRandom(seed) + // Creates an LuaPcgRandom and leaves it on top of stack + static int create_object(lua_State *L); + + static LuaPcgRandom *checkobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; + +/* + LuaSecureRandom +*/ +class LuaSecureRandom : public ModApiBase +{ +private: + static const size_t RAND_BUF_SIZE = 2048; + static const char className[]; + static const luaL_Reg methods[]; + + u32 m_rand_idx; + char m_rand_buf[RAND_BUF_SIZE]; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // next_bytes(self, count) -> get count many bytes + static int l_next_bytes(lua_State *L); + +public: + bool fillRandBuf(); + + // LuaSecureRandom() + // Creates an LuaSecureRandom and leaves it on top of stack + static int create_object(lua_State *L); + + static LuaSecureRandom *checkobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp new file mode 100644 index 000000000..ff226b989 --- /dev/null +++ b/src/script/lua_api/l_object.cpp @@ -0,0 +1,2398 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_object.h" +#include +#include "lua_api/l_internal.h" +#include "lua_api/l_inventory.h" +#include "lua_api/l_item.h" +#include "lua_api/l_playermeta.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "log.h" +#include "tool.h" +#include "remoteplayer.h" +#include "server.h" +#include "hud.h" +#include "scripting_server.h" +#include "server/luaentity_sao.h" +#include "server/player_sao.h" +#include "server/serverinventorymgr.h" + +/* + ObjectRef +*/ + + +ObjectRef* ObjectRef::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if (ud == nullptr) + luaL_typerror(L, narg, className); + return *(ObjectRef**)ud; // unbox pointer +} + +ServerActiveObject* ObjectRef::getobject(ObjectRef *ref) +{ + ServerActiveObject *sao = ref->m_object; + if (sao && sao->isGone()) + return nullptr; + return sao; +} + +LuaEntitySAO* ObjectRef::getluaobject(ObjectRef *ref) +{ + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return nullptr; + if (sao->getType() != ACTIVEOBJECT_TYPE_LUAENTITY) + return nullptr; + return (LuaEntitySAO*)sao; +} + +PlayerSAO* ObjectRef::getplayersao(ObjectRef *ref) +{ + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return nullptr; + if (sao->getType() != ACTIVEOBJECT_TYPE_PLAYER) + return nullptr; + return (PlayerSAO*)sao; +} + +RemotePlayer *ObjectRef::getplayer(ObjectRef *ref) +{ + PlayerSAO *playersao = getplayersao(ref); + if (playersao == nullptr) + return nullptr; + return playersao->getPlayer(); +} + +// Exported functions + +// garbage collector +int ObjectRef::gc_object(lua_State *L) { + ObjectRef *obj = *(ObjectRef **)(lua_touserdata(L, 1)); + delete obj; + return 0; +} + +// remove(self) +int ObjectRef::l_remove(lua_State *L) +{ + GET_ENV_PTR; + + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) + return 0; + + sao->clearChildAttachments(); + sao->clearParentAttachment(); + + verbosestream << "ObjectRef::l_remove(): id=" << sao->getId() << std::endl; + sao->m_pending_removal = true; + return 0; +} + +// get_pos(self) +int ObjectRef::l_get_pos(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + push_v3f(L, sao->getBasePosition() / BS); + return 1; +} + +// set_pos(self, pos) +int ObjectRef::l_set_pos(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + v3f pos = checkFloatPos(L, 2); + + sao->setPos(pos); + return 0; +} + +// move_to(self, pos, continuous) +int ObjectRef::l_move_to(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + v3f pos = checkFloatPos(L, 2); + bool continuous = readParam(L, 3); + + sao->moveTo(pos, continuous); + return 0; +} + +// punch(self, puncher, time_from_last_punch, tool_capabilities, dir) +int ObjectRef::l_punch(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ObjectRef *puncher_ref = checkobject(L, 2); + ServerActiveObject *sao = getobject(ref); + ServerActiveObject *puncher = getobject(puncher_ref); + if (sao == nullptr || puncher == nullptr) + return 0; + + float time_from_last_punch = readParam(L, 3, 1000000.0f); + ToolCapabilities toolcap = read_tool_capabilities(L, 4); + v3f dir = readParam(L, 5, sao->getBasePosition() - puncher->getBasePosition()); + + dir.normalize(); + u16 src_original_hp = sao->getHP(); + u16 dst_origin_hp = puncher->getHP(); + + u16 wear = sao->punch(dir, &toolcap, puncher, time_from_last_punch); + lua_pushnumber(L, wear); + + // If the punched is a player, and its HP changed + if (src_original_hp != sao->getHP() && + sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + getServer(L)->SendPlayerHPOrDie((PlayerSAO *)sao, + PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); + } + + // If the puncher is a player, and its HP changed + if (dst_origin_hp != puncher->getHP() && + puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + getServer(L)->SendPlayerHPOrDie((PlayerSAO *)puncher, + PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, sao)); + } + return 1; +} + +// right_click(self, clicker) +int ObjectRef::l_right_click(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ObjectRef *ref2 = checkobject(L, 2); + ServerActiveObject *sao = getobject(ref); + ServerActiveObject *sao2 = getobject(ref2); + if (sao == nullptr || sao2 == nullptr) + return 0; + + sao->rightClick(sao2); + return 0; +} + +// set_hp(self, hp, reason) +int ObjectRef::l_set_hp(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + int hp = readParam(L, 2); + PlayerHPChangeReason reason(PlayerHPChangeReason::SET_HP); + + reason.from_mod = true; + if (lua_istable(L, 3)) { + lua_pushvalue(L, 3); + + lua_getfield(L, -1, "type"); + if (lua_isstring(L, -1) && + !reason.setTypeFromString(readParam(L, -1))) { + errorstream << "Bad type given!" << std::endl; + } + lua_pop(L, 1); + + reason.lua_reference = luaL_ref(L, LUA_REGISTRYINDEX); + } + + sao->setHP(hp, reason); + if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) + getServer(L)->SendPlayerHPOrDie((PlayerSAO *)sao, reason); + if (reason.hasLuaReference()) + luaL_unref(L, LUA_REGISTRYINDEX, reason.lua_reference); + return 0; +} + +// get_hp(self) +int ObjectRef::l_get_hp(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) { + // Default hp is 1 + lua_pushnumber(L, 1); + return 1; + } + + int hp = sao->getHP(); + + lua_pushnumber(L, hp); + return 1; +} + +// get_inventory(self) +int ObjectRef::l_get_inventory(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + InventoryLocation loc = sao->getInventoryLocation(); + if (getServerInventoryMgr(L)->getInventory(loc) != nullptr) + InvRef::create(L, loc); + else + lua_pushnil(L); // An object may have no inventory (nil) + return 1; +} + +// get_wield_list(self) +int ObjectRef::l_get_wield_list(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + lua_pushstring(L, sao->getWieldList().c_str()); + return 1; +} + +// get_wield_index(self) +int ObjectRef::l_get_wield_index(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + lua_pushinteger(L, sao->getWieldIndex() + 1); + return 1; +} + +// get_wielded_item(self) +int ObjectRef::l_get_wielded_item(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) { + // Empty ItemStack + LuaItemStack::create(L, ItemStack()); + return 1; + } + + ItemStack selected_item; + sao->getWieldedItem(&selected_item, nullptr); + LuaItemStack::create(L, selected_item); + return 1; +} + +// set_wielded_item(self, item) +int ObjectRef::l_set_wielded_item(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + ItemStack item = read_item(L, 2, getServer(L)->idef()); + + bool success = sao->setWieldedItem(item); + if (success && sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + getServer(L)->SendInventory((PlayerSAO *)sao, true); + } + lua_pushboolean(L, success); + return 1; +} + +// set_armor_groups(self, groups) +int ObjectRef::l_set_armor_groups(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + ItemGroupList groups; + + read_groups(L, 2, groups); + sao->setArmorGroups(groups); + return 0; +} + +// get_armor_groups(self) +int ObjectRef::l_get_armor_groups(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + push_groups(L, sao->getArmorGroups()); + return 1; +} + +// set_animation(self, frame_range, frame_speed, frame_blend, frame_loop) +int ObjectRef::l_set_animation(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + v2f frame_range = readParam(L, 2, v2f(1, 1)); + float frame_speed = readParam(L, 3, 15.0f); + float frame_blend = readParam(L, 4, 0.0f); + bool frame_loop = readParam(L, 5, true); + + sao->setAnimation(frame_range, frame_speed, frame_blend, frame_loop); + return 0; +} + +// get_animation(self) +int ObjectRef::l_get_animation(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + v2f frames = v2f(1,1); + float frame_speed = 15; + float frame_blend = 0; + bool frame_loop = true; + + sao->getAnimation(&frames, &frame_speed, &frame_blend, &frame_loop); + push_v2f(L, frames); + lua_pushnumber(L, frame_speed); + lua_pushnumber(L, frame_blend); + lua_pushboolean(L, frame_loop); + return 4; +} + +// set_local_animation(self, idle, walk, dig, walk_while_dig, frame_speed) +int ObjectRef::l_set_local_animation(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + v2s32 frames[4]; + for (int i=0;i<4;i++) { + if (!lua_isnil(L, 2+1)) + frames[i] = read_v2s32(L, 2+i); + } + float frame_speed = readParam(L, 6, 30.0f); + + getServer(L)->setLocalPlayerAnimations(player, frames, frame_speed); + lua_pushboolean(L, true); + return 1; +} + +// get_local_animation(self) +int ObjectRef::l_get_local_animation(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + v2s32 frames[4]; + float frame_speed; + player->getLocalAnimations(frames, &frame_speed); + + for (const v2s32 &frame : frames) { + push_v2s32(L, frame); + } + + lua_pushnumber(L, frame_speed); + return 5; +} + +// set_eye_offset(self, firstperson, thirdperson) +int ObjectRef::l_set_eye_offset(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + v3f offset_first = read_v3f(L, 2); + v3f offset_third = read_v3f(L, 3); + + // Prevent abuse of offset values (keep player always visible) + offset_third.X = rangelim(offset_third.X,-10,10); + offset_third.Z = rangelim(offset_third.Z,-5,5); + /* TODO: if possible: improve the camera collision detection to allow Y <= -1.5) */ + offset_third.Y = rangelim(offset_third.Y,-10,15); //1.5*BS + + getServer(L)->setPlayerEyeOffset(player, offset_first, offset_third); + lua_pushboolean(L, true); + return 1; +} + +// get_eye_offset(self) +int ObjectRef::l_get_eye_offset(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + push_v3f(L, player->eye_offset_first); + push_v3f(L, player->eye_offset_third); + return 2; +} + +// send_mapblock(self, pos) +int ObjectRef::l_send_mapblock(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + v3s16 pos = read_v3s16(L, 2); + + session_t peer_id = player->getPeerId(); + bool r = getServer(L)->SendBlock(peer_id, pos); + + lua_pushboolean(L, r); + return 1; +} + +// set_animation_frame_speed(self, frame_speed) +int ObjectRef::l_set_animation_frame_speed(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + if (!lua_isnil(L, 2)) { + float frame_speed = readParam(L, 2); + sao->setAnimationSpeed(frame_speed); + lua_pushboolean(L, true); + } else { + lua_pushboolean(L, false); + } + return 1; +} + +// set_bone_position(self, bone, position, rotation) +int ObjectRef::l_set_bone_position(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + std::string bone = readParam(L, 2); + v3f position = check_v3f(L, 3); + v3f rotation = check_v3f(L, 4); + + sao->setBonePosition(bone, position, rotation); + return 0; +} + +// get_bone_position(self, bone) +int ObjectRef::l_get_bone_position(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + std::string bone = readParam(L, 2); + + v3f position = v3f(0, 0, 0); + v3f rotation = v3f(0, 0, 0); + sao->getBonePosition(bone, &position, &rotation); + + push_v3f(L, position); + push_v3f(L, rotation); + return 2; +} + +// set_attach(self, parent, bone, position, rotation, force_visible) +int ObjectRef::l_set_attach(lua_State *L) +{ + GET_ENV_PTR; + ObjectRef *ref = checkobject(L, 1); + ObjectRef *parent_ref = checkobject(L, 2); + ServerActiveObject *sao = getobject(ref); + ServerActiveObject *parent = getobject(parent_ref); + if (sao == nullptr || parent == nullptr) + return 0; + if (sao == parent) + throw LuaError("ObjectRef::set_attach: attaching object to itself is not allowed."); + + int parent_id = 0; + std::string bone; + v3f position = v3f(0, 0, 0); + v3f rotation = v3f(0, 0, 0); + bool force_visible; + + sao->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible); + if (parent_id) { + ServerActiveObject *old_parent = env->getActiveObject(parent_id); + old_parent->removeAttachmentChild(sao->getId()); + } + + bone = readParam(L, 3, ""); + position = read_v3f(L, 4); + rotation = read_v3f(L, 5); + force_visible = readParam(L, 6, false); + + sao->setAttachment(parent->getId(), bone, position, rotation, force_visible); + parent->addAttachmentChild(sao->getId()); + return 0; +} + +// get_attach(self) +int ObjectRef::l_get_attach(lua_State *L) +{ + GET_ENV_PTR; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + int parent_id = 0; + std::string bone; + v3f position = v3f(0, 0, 0); + v3f rotation = v3f(0, 0, 0); + bool force_visible; + + sao->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible); + if (parent_id == 0) + return 0; + + ServerActiveObject *parent = env->getActiveObject(parent_id); + getScriptApiBase(L)->objectrefGetOrCreate(L, parent); + lua_pushlstring(L, bone.c_str(), bone.size()); + push_v3f(L, position); + push_v3f(L, rotation); + lua_pushboolean(L, force_visible); + return 5; +} + +// get_children(self) +int ObjectRef::l_get_children(lua_State *L) +{ + GET_ENV_PTR; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + const std::unordered_set child_ids = sao->getAttachmentChildIds(); + int i = 0; + + lua_createtable(L, child_ids.size(), 0); + for (const int id : child_ids) { + ServerActiveObject *child = env->getActiveObject(id); + getScriptApiBase(L)->objectrefGetOrCreate(L, child); + lua_rawseti(L, -2, ++i); + } + return 1; +} + +// set_detach(self) +int ObjectRef::l_set_detach(lua_State *L) +{ + GET_ENV_PTR; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + sao->clearParentAttachment(); + return 0; +} + +// set_properties(self, properties) +int ObjectRef::l_set_properties(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + ObjectProperties *prop = sao->accessObjectProperties(); + if (prop == nullptr) + return 0; + + read_object_properties(L, 2, sao, prop, getServer(L)->idef()); + sao->notifyObjectPropertiesModified(); + return 0; +} + +// get_properties(self) +int ObjectRef::l_get_properties(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + ObjectProperties *prop = sao->accessObjectProperties(); + if (prop == nullptr) + return 0; + + push_object_properties(L, prop); + return 1; +} + +// is_player(self) +int ObjectRef::l_is_player(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + lua_pushboolean(L, (player != nullptr)); + return 1; +} + +// set_nametag_attributes(self, attributes) +int ObjectRef::l_set_nametag_attributes(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + ObjectProperties *prop = sao->accessObjectProperties(); + if (prop == nullptr) + return 0; + + lua_getfield(L, 2, "color"); + if (!lua_isnil(L, -1)) { + video::SColor color = prop->nametag_color; + read_color(L, -1, &color); + prop->nametag_color = color; + } + lua_pop(L, 1); + + std::string nametag = getstringfield_default(L, 2, "text", ""); + prop->nametag = nametag; + + sao->notifyObjectPropertiesModified(); + lua_pushboolean(L, true); + return 1; +} + +// get_nametag_attributes(self) +int ObjectRef::l_get_nametag_attributes(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + ObjectProperties *prop = sao->accessObjectProperties(); + if (!prop) + return 0; + + video::SColor color = prop->nametag_color; + + lua_newtable(L); + push_ARGB8(L, color); + lua_setfield(L, -2, "color"); + lua_pushstring(L, prop->nametag.c_str()); + lua_setfield(L, -2, "text"); + return 1; +} + +/* LuaEntitySAO-only */ + +// set_velocity(self, velocity) +int ObjectRef::l_set_velocity(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *sao = getluaobject(ref); + if (sao == nullptr) + return 0; + + v3f vel = checkFloatPos(L, 2); + + sao->setVelocity(vel); + return 0; +} + +// add_velocity(self, velocity) +int ObjectRef::l_add_velocity(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + v3f vel = checkFloatPos(L, 2); + + if (sao->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) { + LuaEntitySAO *entitysao = dynamic_cast(sao); + entitysao->addVelocity(vel); + } else if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + PlayerSAO *playersao = dynamic_cast(sao); + playersao->setMaxSpeedOverride(vel); + getServer(L)->SendPlayerSpeed(playersao->getPeerID(), vel); + } + + return 0; +} + +// get_velocity(self) +int ObjectRef::l_get_velocity(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + if (sao->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) { + LuaEntitySAO *entitysao = dynamic_cast(sao); + v3f vel = entitysao->getVelocity(); + pushFloatPos(L, vel); + return 1; + } else if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + RemotePlayer *player = dynamic_cast(sao)->getPlayer(); + push_v3f(L, player->getSpeed() / BS); + return 1; + } + + lua_pushnil(L); + return 1; +} + +// set_acceleration(self, acceleration) +int ObjectRef::l_set_acceleration(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + v3f acceleration = checkFloatPos(L, 2); + + entitysao->setAcceleration(acceleration); + return 0; +} + +// get_acceleration(self) +int ObjectRef::l_get_acceleration(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + v3f acceleration = entitysao->getAcceleration(); + pushFloatPos(L, acceleration); + return 1; +} + +// set_rotation(self, rotation) +int ObjectRef::l_set_rotation(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + v3f rotation = check_v3f(L, 2) * core::RADTODEG; + + entitysao->setRotation(rotation); + return 0; +} + +// get_rotation(self) +int ObjectRef::l_get_rotation(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + v3f rotation = entitysao->getRotation() * core::DEGTORAD; + + lua_newtable(L); + push_v3f(L, rotation); + return 1; +} + +// set_yaw(self, yaw) +int ObjectRef::l_set_yaw(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + if (isNaN(L, 2)) + throw LuaError("ObjectRef::set_yaw: NaN value is not allowed."); + + float yaw = readParam(L, 2) * core::RADTODEG; + + entitysao->setRotation(v3f(0, yaw, 0)); + return 0; +} + +// get_yaw(self) +int ObjectRef::l_get_yaw(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + float yaw = entitysao->getRotation().Y * core::DEGTORAD; + + lua_pushnumber(L, yaw); + return 1; +} + +// set_texture_mod(self, mod) +int ObjectRef::l_set_texture_mod(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + std::string mod = readParam(L, 2); + + entitysao->setTextureMod(mod); + return 0; +} + +// get_texture_mod(self) +int ObjectRef::l_get_texture_mod(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + std::string mod = entitysao->getTextureMod(); + + lua_pushstring(L, mod.c_str()); + return 1; +} + +// set_sprite(self, start_frame, num_frames, framelength, select_x_by_camera) +int ObjectRef::l_set_sprite(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + v2s16 start_frame = readParam(L, 2, v2s16(0,0)); + int num_frames = readParam(L, 3, 1); + float framelength = readParam(L, 4, 0.2f); + bool select_x_by_camera = readParam(L, 5, false); + + entitysao->setSprite(start_frame, num_frames, framelength, select_x_by_camera); + return 0; +} + +// DEPRECATED +// get_entity_name(self) +int ObjectRef::l_get_entity_name(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *entitysao = getluaobject(ref); + log_deprecated(L,"Deprecated call to \"get_entity_name"); + if (entitysao == nullptr) + return 0; + + std::string name = entitysao->getName(); + + lua_pushstring(L, name.c_str()); + return 1; +} + +// get_luaentity(self) +int ObjectRef::l_get_luaentity(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + luaentity_get(L, entitysao->getId()); + return 1; +} + +/* Player-only */ + +// get_player_name(self) +int ObjectRef::l_get_player_name(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) { + lua_pushlstring(L, "", 0); + return 1; + } + + lua_pushstring(L, player->getName()); + return 1; +} + +// get_look_dir(self) +int ObjectRef::l_get_look_dir(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + float pitch = playersao->getRadLookPitchDep(); + float yaw = playersao->getRadYawDep(); + v3f v(std::cos(pitch) * std::cos(yaw), std::sin(pitch), std::cos(pitch) * + std::sin(yaw)); + + push_v3f(L, v); + return 1; +} + +// DEPRECATED +// get_look_pitch(self) +int ObjectRef::l_get_look_pitch(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + log_deprecated(L, + "Deprecated call to get_look_pitch, use get_look_vertical instead"); + + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + lua_pushnumber(L, playersao->getRadLookPitchDep()); + return 1; +} + +// DEPRECATED +// get_look_yaw(self) +int ObjectRef::l_get_look_yaw(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + log_deprecated(L, + "Deprecated call to get_look_yaw, use get_look_horizontal instead"); + + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + lua_pushnumber(L, playersao->getRadYawDep()); + return 1; +} + +// get_look_vertical(self) +int ObjectRef::l_get_look_vertical(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + lua_pushnumber(L, playersao->getRadLookPitch()); + return 1; +} + +// get_look_horizontal(self) +int ObjectRef::l_get_look_horizontal(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + lua_pushnumber(L, playersao->getRadRotation().Y); + return 1; +} + +// set_look_vertical(self, radians) +int ObjectRef::l_set_look_vertical(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + float pitch = readParam(L, 2) * core::RADTODEG; + + playersao->setLookPitchAndSend(pitch); + return 1; +} + +// set_look_horizontal(self, radians) +int ObjectRef::l_set_look_horizontal(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + float yaw = readParam(L, 2) * core::RADTODEG; + + playersao->setPlayerYawAndSend(yaw); + return 1; +} + +// DEPRECATED +// set_look_pitch(self, radians) +int ObjectRef::l_set_look_pitch(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + log_deprecated(L, + "Deprecated call to set_look_pitch, use set_look_vertical instead."); + + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + float pitch = readParam(L, 2) * core::RADTODEG; + + playersao->setLookPitchAndSend(pitch); + return 1; +} + +// DEPRECATED +// set_look_yaw(self, radians) +int ObjectRef::l_set_look_yaw(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + log_deprecated(L, + "Deprecated call to set_look_yaw, use set_look_horizontal instead."); + + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + float yaw = readParam(L, 2) * core::RADTODEG; + + playersao->setPlayerYawAndSend(yaw); + return 1; +} + +// set_fov(self, degrees, is_multiplier, transition_time) +int ObjectRef::l_set_fov(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + float degrees = static_cast(luaL_checknumber(L, 2)); + bool is_multiplier = readParam(L, 3, false); + float transition_time = lua_isnumber(L, 4) ? + static_cast(luaL_checknumber(L, 4)) : 0.0f; + + player->setFov({degrees, is_multiplier, transition_time}); + getServer(L)->SendPlayerFov(player->getPeerId()); + return 0; +} + +// get_fov(self) +int ObjectRef::l_get_fov(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + PlayerFovSpec fov_spec = player->getFov(); + + lua_pushnumber(L, fov_spec.fov); + lua_pushboolean(L, fov_spec.is_multiplier); + lua_pushnumber(L, fov_spec.transition_time); + return 3; +} + +// set_breath(self, breath) +int ObjectRef::l_set_breath(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + u16 breath = luaL_checknumber(L, 2); + + playersao->setBreath(breath); + return 0; +} + +// get_breath(self) +int ObjectRef::l_get_breath(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + u16 breath = playersao->getBreath(); + + lua_pushinteger(L, breath); + return 1; +} + +// set_attribute(self, attribute, value) +int ObjectRef::l_set_attribute(lua_State *L) +{ + log_deprecated(L, + "Deprecated call to set_attribute, use MetaDataRef methods instead."); + + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + std::string attr = luaL_checkstring(L, 2); + if (lua_isnil(L, 3)) { + playersao->getMeta().removeString(attr); + } else { + std::string value = luaL_checkstring(L, 3); + playersao->getMeta().setString(attr, value); + } + return 1; +} + +// get_attribute(self, attribute) +int ObjectRef::l_get_attribute(lua_State *L) +{ + log_deprecated(L, + "Deprecated call to get_attribute, use MetaDataRef methods instead."); + + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + std::string attr = luaL_checkstring(L, 2); + + std::string value; + if (playersao->getMeta().getStringToRef(attr, value)) { + lua_pushstring(L, value.c_str()); + return 1; + } + + return 0; +} + + +// get_meta(self, attribute) +int ObjectRef::l_get_meta(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + PlayerSAO *playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + PlayerMetaRef::create(L, &playersao->getMeta()); + return 1; +} + + +// set_inventory_formspec(self, formspec) +int ObjectRef::l_set_inventory_formspec(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + std::string formspec = luaL_checkstring(L, 2); + + player->inventory_formspec = formspec; + getServer(L)->reportInventoryFormspecModified(player->getName()); + lua_pushboolean(L, true); + return 1; +} + +// get_inventory_formspec(self) -> formspec +int ObjectRef::l_get_inventory_formspec(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + std::string formspec = player->inventory_formspec; + + lua_pushlstring(L, formspec.c_str(), formspec.size()); + return 1; +} + +// set_formspec_prepend(self, formspec) +int ObjectRef::l_set_formspec_prepend(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + std::string formspec = luaL_checkstring(L, 2); + + player->formspec_prepend = formspec; + getServer(L)->reportFormspecPrependModified(player->getName()); + lua_pushboolean(L, true); + return 1; +} + +// get_formspec_prepend(self) +int ObjectRef::l_get_formspec_prepend(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + std::string formspec = player->formspec_prepend; + + lua_pushlstring(L, formspec.c_str(), formspec.size()); + return 1; +} + +// get_player_control(self) +int ObjectRef::l_get_player_control(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) { + lua_pushlstring(L, "", 0); + return 1; + } + + const PlayerControl &control = player->getPlayerControl(); + lua_newtable(L); + lua_pushboolean(L, control.up); + lua_setfield(L, -2, "up"); + lua_pushboolean(L, control.down); + lua_setfield(L, -2, "down"); + lua_pushboolean(L, control.left); + lua_setfield(L, -2, "left"); + lua_pushboolean(L, control.right); + lua_setfield(L, -2, "right"); + lua_pushboolean(L, control.jump); + lua_setfield(L, -2, "jump"); + lua_pushboolean(L, control.aux1); + lua_setfield(L, -2, "aux1"); + lua_pushboolean(L, control.sneak); + lua_setfield(L, -2, "sneak"); + lua_pushboolean(L, control.dig); + lua_setfield(L, -2, "dig"); + lua_pushboolean(L, control.place); + lua_setfield(L, -2, "place"); + // Legacy fields to ensure mod compatibility + lua_pushboolean(L, control.dig); + lua_setfield(L, -2, "LMB"); + lua_pushboolean(L, control.place); + lua_setfield(L, -2, "RMB"); + lua_pushboolean(L, control.zoom); + lua_setfield(L, -2, "zoom"); + return 1; +} + +// get_player_control_bits(self) +int ObjectRef::l_get_player_control_bits(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) { + lua_pushlstring(L, "", 0); + return 1; + } + + lua_pushnumber(L, player->keyPressed); + return 1; +} + +// set_physics_override(self, override_table) +int ObjectRef::l_set_physics_override(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO *playersao = (PlayerSAO *) getobject(ref); + if (playersao == nullptr) + return 0; + + if (lua_istable(L, 2)) { + bool modified = false; + modified |= getfloatfield(L, 2, "speed", playersao->m_physics_override_speed); + modified |= getfloatfield(L, 2, "jump", playersao->m_physics_override_jump); + modified |= getfloatfield(L, 2, "gravity", playersao->m_physics_override_gravity); + modified |= getboolfield(L, 2, "sneak", playersao->m_physics_override_sneak); + modified |= getboolfield(L, 2, "sneak_glitch", playersao->m_physics_override_sneak_glitch); + modified |= getboolfield(L, 2, "new_move", playersao->m_physics_override_new_move); + if (modified) + playersao->m_physics_override_sent = false; + } else { + // old, non-table format + // TODO: Remove this code after version 5.4.0 + log_deprecated(L, "Deprecated use of set_physics_override(num, num, num)"); + + if (!lua_isnil(L, 2)) { + playersao->m_physics_override_speed = lua_tonumber(L, 2); + playersao->m_physics_override_sent = false; + } + if (!lua_isnil(L, 3)) { + playersao->m_physics_override_jump = lua_tonumber(L, 3); + playersao->m_physics_override_sent = false; + } + if (!lua_isnil(L, 4)) { + playersao->m_physics_override_gravity = lua_tonumber(L, 4); + playersao->m_physics_override_sent = false; + } + } + return 0; +} + +// get_physics_override(self) +int ObjectRef::l_get_physics_override(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO *playersao = (PlayerSAO *)getobject(ref); + if (playersao == nullptr) + return 0; + + lua_newtable(L); + lua_pushnumber(L, playersao->m_physics_override_speed); + lua_setfield(L, -2, "speed"); + lua_pushnumber(L, playersao->m_physics_override_jump); + lua_setfield(L, -2, "jump"); + lua_pushnumber(L, playersao->m_physics_override_gravity); + lua_setfield(L, -2, "gravity"); + lua_pushboolean(L, playersao->m_physics_override_sneak); + lua_setfield(L, -2, "sneak"); + lua_pushboolean(L, playersao->m_physics_override_sneak_glitch); + lua_setfield(L, -2, "sneak_glitch"); + lua_pushboolean(L, playersao->m_physics_override_new_move); + lua_setfield(L, -2, "new_move"); + return 1; +} + +// hud_add(self, hud) +int ObjectRef::l_hud_add(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + HudElement *elem = new HudElement; + read_hud_element(L, elem); + + u32 id = getServer(L)->hudAdd(player, elem); + if (id == U32_MAX) { + delete elem; + return 0; + } + + lua_pushnumber(L, id); + return 1; +} + +// hud_remove(self, id) +int ObjectRef::l_hud_remove(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + u32 id = luaL_checkint(L, 2); + + if (!getServer(L)->hudRemove(player, id)) + return 0; + + lua_pushboolean(L, true); + return 1; +} + +// hud_change(self, id, stat, data) +int ObjectRef::l_hud_change(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + u32 id = luaL_checkint(L, 2); + + HudElement *elem = player->getHud(id); + if (elem == nullptr) + return 0; + + void *value = nullptr; + HudElementStat stat = read_hud_change(L, elem, &value); + + getServer(L)->hudChange(player, id, stat, value); + + lua_pushboolean(L, true); + return 1; +} + +// hud_get(self, id) +int ObjectRef::l_hud_get(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + u32 id = luaL_checkint(L, 2); + + HudElement *elem = player->getHud(id); + if (elem == nullptr) + return 0; + + push_hud_element(L, elem); + return 1; +} + +// hud_set_flags(self, flags) +int ObjectRef::l_hud_set_flags(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + u32 flags = 0; + u32 mask = 0; + bool flag; + + const EnumString *esp = es_HudBuiltinElement; + for (int i = 0; esp[i].str; i++) { + if (getboolfield(L, 2, esp[i].str, flag)) { + flags |= esp[i].num * flag; + mask |= esp[i].num; + } + } + if (!getServer(L)->hudSetFlags(player, flags, mask)) + return 0; + + lua_pushboolean(L, true); + return 1; +} + +// hud_get_flags(self) +int ObjectRef::l_hud_get_flags(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + lua_newtable(L); + lua_pushboolean(L, player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE); + lua_setfield(L, -2, "hotbar"); + lua_pushboolean(L, player->hud_flags & HUD_FLAG_HEALTHBAR_VISIBLE); + lua_setfield(L, -2, "healthbar"); + lua_pushboolean(L, player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE); + lua_setfield(L, -2, "crosshair"); + lua_pushboolean(L, player->hud_flags & HUD_FLAG_WIELDITEM_VISIBLE); + lua_setfield(L, -2, "wielditem"); + lua_pushboolean(L, player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE); + lua_setfield(L, -2, "breathbar"); + lua_pushboolean(L, player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE); + lua_setfield(L, -2, "minimap"); + lua_pushboolean(L, player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE); + lua_setfield(L, -2, "minimap_radar"); + return 1; +} + +// hud_set_hotbar_itemcount(self, hotbar_itemcount) +int ObjectRef::l_hud_set_hotbar_itemcount(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + s32 hotbar_itemcount = luaL_checkint(L, 2); + + if (!getServer(L)->hudSetHotbarItemcount(player, hotbar_itemcount)) + return 0; + + lua_pushboolean(L, true); + return 1; +} + +// hud_get_hotbar_itemcount(self) +int ObjectRef::l_hud_get_hotbar_itemcount(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + lua_pushnumber(L, player->getHotbarItemcount()); + return 1; +} + +// hud_set_hotbar_image(self, name) +int ObjectRef::l_hud_set_hotbar_image(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + std::string name = readParam(L, 2); + + getServer(L)->hudSetHotbarImage(player, name); + return 1; +} + +// hud_get_hotbar_image(self) +int ObjectRef::l_hud_get_hotbar_image(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + const std::string &name = player->getHotbarImage(); + + lua_pushlstring(L, name.c_str(), name.size()); + return 1; +} + +// hud_set_hotbar_selected_image(self, name) +int ObjectRef::l_hud_set_hotbar_selected_image(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + std::string name = readParam(L, 2); + + getServer(L)->hudSetHotbarSelectedImage(player, name); + return 1; +} + +// hud_get_hotbar_selected_image(self) +int ObjectRef::l_hud_get_hotbar_selected_image(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + const std::string &name = player->getHotbarSelectedImage(); + + lua_pushlstring(L, name.c_str(), name.size()); + return 1; +} + +// set_sky(self, sky_parameters) +int ObjectRef::l_set_sky(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + SkyboxParams sky_params = player->getSkyParams(); + bool is_colorspec = is_color_table(L, 2); + + if (lua_istable(L, 2) && !is_colorspec) { + lua_getfield(L, 2, "base_color"); + if (!lua_isnil(L, -1)) + read_color(L, -1, &sky_params.bgcolor); + lua_pop(L, 1); + + lua_getfield(L, 2, "type"); + if (!lua_isnil(L, -1)) + sky_params.type = luaL_checkstring(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 2, "textures"); + sky_params.textures.clear(); + if (lua_istable(L, -1) && sky_params.type == "skybox") { + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + // Key is at index -2 and value at index -1 + sky_params.textures.emplace_back(readParam(L, -1)); + // Removes the value, but keeps the key for iteration + lua_pop(L, 1); + } + } + lua_pop(L, 1); + + /* + We want to avoid crashes, so we're checking even if we're not using them. + However, we want to ensure that the skybox can be set to nil when + using "regular" or "plain" skybox modes as textures aren't needed. + */ + + if (sky_params.textures.size() != 6 && sky_params.textures.size() > 0) + throw LuaError("Skybox expects 6 textures!"); + + sky_params.clouds = getboolfield_default(L, 2, + "clouds", sky_params.clouds); + + lua_getfield(L, 2, "sky_color"); + if (lua_istable(L, -1)) { + lua_getfield(L, -1, "day_sky"); + read_color(L, -1, &sky_params.sky_color.day_sky); + lua_pop(L, 1); + + lua_getfield(L, -1, "day_horizon"); + read_color(L, -1, &sky_params.sky_color.day_horizon); + lua_pop(L, 1); + + lua_getfield(L, -1, "dawn_sky"); + read_color(L, -1, &sky_params.sky_color.dawn_sky); + lua_pop(L, 1); + + lua_getfield(L, -1, "dawn_horizon"); + read_color(L, -1, &sky_params.sky_color.dawn_horizon); + lua_pop(L, 1); + + lua_getfield(L, -1, "night_sky"); + read_color(L, -1, &sky_params.sky_color.night_sky); + lua_pop(L, 1); + + lua_getfield(L, -1, "night_horizon"); + read_color(L, -1, &sky_params.sky_color.night_horizon); + lua_pop(L, 1); + + lua_getfield(L, -1, "indoors"); + read_color(L, -1, &sky_params.sky_color.indoors); + lua_pop(L, 1); + + // Prevent flickering clouds at dawn/dusk: + sky_params.fog_sun_tint = video::SColor(255, 255, 255, 255); + lua_getfield(L, -1, "fog_sun_tint"); + read_color(L, -1, &sky_params.fog_sun_tint); + lua_pop(L, 1); + + sky_params.fog_moon_tint = video::SColor(255, 255, 255, 255); + lua_getfield(L, -1, "fog_moon_tint"); + read_color(L, -1, &sky_params.fog_moon_tint); + lua_pop(L, 1); + + lua_getfield(L, -1, "fog_tint_type"); + if (!lua_isnil(L, -1)) + sky_params.fog_tint_type = luaL_checkstring(L, -1); + lua_pop(L, 1); + + // Because we need to leave the "sky_color" table. + lua_pop(L, 1); + } + } else { + // Handle old set_sky calls, and log deprecated: + log_deprecated(L, "Deprecated call to set_sky, please check lua_api.txt"); + + // Fix sun, moon and stars showing when classic textured skyboxes are used + SunParams sun_params = player->getSunParams(); + MoonParams moon_params = player->getMoonParams(); + StarParams star_params = player->getStarParams(); + + // Prevent erroneous background colors + sky_params.bgcolor = video::SColor(255, 255, 255, 255); + read_color(L, 2, &sky_params.bgcolor); + + sky_params.type = luaL_checkstring(L, 3); + + // Preserve old behaviour of the sun, moon and stars + // when using the old set_sky call. + if (sky_params.type == "regular") { + sun_params.visible = true; + sun_params.sunrise_visible = true; + moon_params.visible = true; + star_params.visible = true; + } else { + sun_params.visible = false; + sun_params.sunrise_visible = false; + moon_params.visible = false; + star_params.visible = false; + } + + sky_params.textures.clear(); + if (lua_istable(L, 4)) { + lua_pushnil(L); + while (lua_next(L, 4) != 0) { + // Key at index -2, and value at index -1 + if (lua_isstring(L, -1)) + sky_params.textures.emplace_back(readParam(L, -1)); + else + sky_params.textures.emplace_back(""); + // Remove the value, keep the key for the next iteration + lua_pop(L, 1); + } + } + if (sky_params.type == "skybox" && sky_params.textures.size() != 6) + throw LuaError("Skybox expects 6 textures."); + + sky_params.clouds = true; + if (lua_isboolean(L, 5)) + sky_params.clouds = readParam(L, 5); + + getServer(L)->setSun(player, sun_params); + getServer(L)->setMoon(player, moon_params); + getServer(L)->setStars(player, star_params); + } + getServer(L)->setSky(player, sky_params); + lua_pushboolean(L, true); + return 1; +} + +// get_sky(self) +int ObjectRef::l_get_sky(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + SkyboxParams skybox_params = player->getSkyParams(); + + push_ARGB8(L, skybox_params.bgcolor); + lua_pushlstring(L, skybox_params.type.c_str(), skybox_params.type.size()); + + lua_newtable(L); + s16 i = 1; + for (const std::string &texture : skybox_params.textures) { + lua_pushlstring(L, texture.c_str(), texture.size()); + lua_rawseti(L, -2, i++); + } + lua_pushboolean(L, skybox_params.clouds); + return 4; +} + +// get_sky_color(self) +int ObjectRef::l_get_sky_color(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + const SkyboxParams &skybox_params = player->getSkyParams(); + + lua_newtable(L); + if (skybox_params.type == "regular") { + push_ARGB8(L, skybox_params.sky_color.day_sky); + lua_setfield(L, -2, "day_sky"); + push_ARGB8(L, skybox_params.sky_color.day_horizon); + lua_setfield(L, -2, "day_horizon"); + push_ARGB8(L, skybox_params.sky_color.dawn_sky); + lua_setfield(L, -2, "dawn_sky"); + push_ARGB8(L, skybox_params.sky_color.dawn_horizon); + lua_setfield(L, -2, "dawn_horizon"); + push_ARGB8(L, skybox_params.sky_color.night_sky); + lua_setfield(L, -2, "night_sky"); + push_ARGB8(L, skybox_params.sky_color.night_horizon); + lua_setfield(L, -2, "night_horizon"); + push_ARGB8(L, skybox_params.sky_color.indoors); + lua_setfield(L, -2, "indoors"); + } + push_ARGB8(L, skybox_params.fog_sun_tint); + lua_setfield(L, -2, "fog_sun_tint"); + push_ARGB8(L, skybox_params.fog_moon_tint); + lua_setfield(L, -2, "fog_moon_tint"); + lua_pushstring(L, skybox_params.fog_tint_type.c_str()); + lua_setfield(L, -2, "fog_tint_type"); + return 1; +} + +// set_sun(self, sun_parameters) +int ObjectRef::l_set_sun(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + luaL_checktype(L, 2, LUA_TTABLE); + SunParams sun_params = player->getSunParams(); + + sun_params.visible = getboolfield_default(L, 2, + "visible", sun_params.visible); + sun_params.texture = getstringfield_default(L, 2, + "texture", sun_params.texture); + sun_params.tonemap = getstringfield_default(L, 2, + "tonemap", sun_params.tonemap); + sun_params.sunrise = getstringfield_default(L, 2, + "sunrise", sun_params.sunrise); + sun_params.sunrise_visible = getboolfield_default(L, 2, + "sunrise_visible", sun_params.sunrise_visible); + sun_params.scale = getfloatfield_default(L, 2, + "scale", sun_params.scale); + + getServer(L)->setSun(player, sun_params); + lua_pushboolean(L, true); + return 1; +} + +//get_sun(self) +int ObjectRef::l_get_sun(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + const SunParams &sun_params = player->getSunParams(); + + lua_newtable(L); + lua_pushboolean(L, sun_params.visible); + lua_setfield(L, -2, "visible"); + lua_pushstring(L, sun_params.texture.c_str()); + lua_setfield(L, -2, "texture"); + lua_pushstring(L, sun_params.tonemap.c_str()); + lua_setfield(L, -2, "tonemap"); + lua_pushstring(L, sun_params.sunrise.c_str()); + lua_setfield(L, -2, "sunrise"); + lua_pushboolean(L, sun_params.sunrise_visible); + lua_setfield(L, -2, "sunrise_visible"); + lua_pushnumber(L, sun_params.scale); + lua_setfield(L, -2, "scale"); + return 1; +} + +// set_moon(self, moon_parameters) +int ObjectRef::l_set_moon(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + luaL_checktype(L, 2, LUA_TTABLE); + MoonParams moon_params = player->getMoonParams(); + + moon_params.visible = getboolfield_default(L, 2, + "visible", moon_params.visible); + moon_params.texture = getstringfield_default(L, 2, + "texture", moon_params.texture); + moon_params.tonemap = getstringfield_default(L, 2, + "tonemap", moon_params.tonemap); + moon_params.scale = getfloatfield_default(L, 2, + "scale", moon_params.scale); + + getServer(L)->setMoon(player, moon_params); + lua_pushboolean(L, true); + return 1; +} + +// get_moon(self) +int ObjectRef::l_get_moon(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + const MoonParams &moon_params = player->getMoonParams(); + + lua_newtable(L); + lua_pushboolean(L, moon_params.visible); + lua_setfield(L, -2, "visible"); + lua_pushstring(L, moon_params.texture.c_str()); + lua_setfield(L, -2, "texture"); + lua_pushstring(L, moon_params.tonemap.c_str()); + lua_setfield(L, -2, "tonemap"); + lua_pushnumber(L, moon_params.scale); + lua_setfield(L, -2, "scale"); + return 1; +} + +// set_stars(self, star_parameters) +int ObjectRef::l_set_stars(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + luaL_checktype(L, 2, LUA_TTABLE); + StarParams star_params = player->getStarParams(); + + star_params.visible = getboolfield_default(L, 2, + "visible", star_params.visible); + star_params.count = getintfield_default(L, 2, + "count", star_params.count); + + lua_getfield(L, 2, "star_color"); + if (!lua_isnil(L, -1)) + read_color(L, -1, &star_params.starcolor); + lua_pop(L, 1); + + star_params.scale = getfloatfield_default(L, 2, + "scale", star_params.scale); + + getServer(L)->setStars(player, star_params); + lua_pushboolean(L, true); + return 1; +} + +// get_stars(self) +int ObjectRef::l_get_stars(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + const StarParams &star_params = player->getStarParams(); + + lua_newtable(L); + lua_pushboolean(L, star_params.visible); + lua_setfield(L, -2, "visible"); + lua_pushnumber(L, star_params.count); + lua_setfield(L, -2, "count"); + push_ARGB8(L, star_params.starcolor); + lua_setfield(L, -2, "star_color"); + lua_pushnumber(L, star_params.scale); + lua_setfield(L, -2, "scale"); + return 1; +} + +// set_clouds(self, cloud_parameters) +int ObjectRef::l_set_clouds(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + luaL_checktype(L, 2, LUA_TTABLE); + CloudParams cloud_params = player->getCloudParams(); + + cloud_params.density = getfloatfield_default(L, 2, "density", cloud_params.density); + + lua_getfield(L, 2, "color"); + if (!lua_isnil(L, -1)) + read_color(L, -1, &cloud_params.color_bright); + lua_pop(L, 1); + lua_getfield(L, 2, "ambient"); + if (!lua_isnil(L, -1)) + read_color(L, -1, &cloud_params.color_ambient); + lua_pop(L, 1); + + cloud_params.height = getfloatfield_default(L, 2, "height", cloud_params.height ); + cloud_params.thickness = getfloatfield_default(L, 2, "thickness", cloud_params.thickness); + + lua_getfield(L, 2, "speed"); + if (lua_istable(L, -1)) { + v2f new_speed; + new_speed.X = getfloatfield_default(L, -1, "x", 0); + new_speed.Y = getfloatfield_default(L, -1, "z", 0); + cloud_params.speed = new_speed; + } + lua_pop(L, 1); + + getServer(L)->setClouds(player, cloud_params); + lua_pushboolean(L, true); + return 1; +} + +int ObjectRef::l_get_clouds(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + const CloudParams &cloud_params = player->getCloudParams(); + + lua_newtable(L); + lua_pushnumber(L, cloud_params.density); + lua_setfield(L, -2, "density"); + push_ARGB8(L, cloud_params.color_bright); + lua_setfield(L, -2, "color"); + push_ARGB8(L, cloud_params.color_ambient); + lua_setfield(L, -2, "ambient"); + lua_pushnumber(L, cloud_params.height); + lua_setfield(L, -2, "height"); + lua_pushnumber(L, cloud_params.thickness); + lua_setfield(L, -2, "thickness"); + lua_newtable(L); + lua_pushnumber(L, cloud_params.speed.X); + lua_setfield(L, -2, "x"); + lua_pushnumber(L, cloud_params.speed.Y); + lua_setfield(L, -2, "y"); + lua_setfield(L, -2, "speed"); + return 1; +} + + +// override_day_night_ratio(self, ratio) +int ObjectRef::l_override_day_night_ratio(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + bool do_override = false; + float ratio = 0.0f; + + if (!lua_isnil(L, 2)) { + do_override = true; + ratio = readParam(L, 2); + luaL_argcheck(L, ratio >= 0.0f && ratio <= 1.0f, 1, + "value must be between 0 and 1"); + } + + getServer(L)->overrideDayNightRatio(player, do_override, ratio); + lua_pushboolean(L, true); + return 1; +} + +// get_day_night_ratio(self) +int ObjectRef::l_get_day_night_ratio(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + bool do_override; + float ratio; + player->getDayNightRatio(&do_override, &ratio); + + if (do_override) + lua_pushnumber(L, ratio); + else + lua_pushnil(L); + + return 1; +} + +// set_minimap_modes(self, modes, selected_mode) +int ObjectRef::l_set_minimap_modes(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + luaL_checktype(L, 2, LUA_TTABLE); + std::vector modes; + s16 selected_mode = luaL_checkint(L, 3); + + lua_pushnil(L); + while (lua_next(L, 2) != 0) { + /* key is at index -2, value is at index -1 */ + if (lua_istable(L, -1)) { + bool ok = true; + MinimapMode mode; + std::string type = getstringfield_default(L, -1, "type", ""); + if (type == "off") + mode.type = MINIMAP_TYPE_OFF; + else if (type == "surface") + mode.type = MINIMAP_TYPE_SURFACE; + else if (type == "radar") + mode.type = MINIMAP_TYPE_RADAR; + else if (type == "texture") { + mode.type = MINIMAP_TYPE_TEXTURE; + mode.texture = getstringfield_default(L, -1, "texture", ""); + mode.scale = getintfield_default(L, -1, "scale", 1); + } else { + warningstream << "Minimap mode of unknown type \"" << type.c_str() + << "\" ignored.\n" << std::endl; + ok = false; + } + + if (ok) { + mode.label = getstringfield_default(L, -1, "label", ""); + // Size is limited to 512. Performance gets poor if size too large, and + // segfaults have been experienced. + mode.size = rangelim(getintfield_default(L, -1, "size", 0), 1, 512); + modes.push_back(mode); + } + } + /* removes 'value'; keeps 'key' for next iteration */ + lua_pop(L, 1); + } + lua_pop(L, 1); // Remove key + + getServer(L)->SendMinimapModes(player->getPeerId(), modes, selected_mode); + return 0; +} + +ObjectRef::ObjectRef(ServerActiveObject *object): + m_object(object) +{} + +// Creates an ObjectRef and leaves it on top of stack +// Not callable from Lua; all references are created on the C side. +void ObjectRef::create(lua_State *L, ServerActiveObject *object) +{ + ObjectRef *obj = new ObjectRef(object); + *(void **)(lua_newuserdata(L, sizeof(void *))) = obj; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); +} + +void ObjectRef::set_null(lua_State *L) +{ + ObjectRef *obj = checkobject(L, -1); + obj->m_object = nullptr; +} + +void ObjectRef::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable +} + +const char ObjectRef::className[] = "ObjectRef"; +luaL_Reg ObjectRef::methods[] = { + // ServerActiveObject + luamethod(ObjectRef, remove), + luamethod_aliased(ObjectRef, get_pos, getpos), + luamethod_aliased(ObjectRef, set_pos, setpos), + luamethod_aliased(ObjectRef, move_to, moveto), + luamethod(ObjectRef, punch), + luamethod(ObjectRef, right_click), + luamethod(ObjectRef, set_hp), + luamethod(ObjectRef, get_hp), + luamethod(ObjectRef, get_inventory), + luamethod(ObjectRef, get_wield_list), + luamethod(ObjectRef, get_wield_index), + luamethod(ObjectRef, get_wielded_item), + luamethod(ObjectRef, set_wielded_item), + luamethod(ObjectRef, set_armor_groups), + luamethod(ObjectRef, get_armor_groups), + luamethod(ObjectRef, set_animation), + luamethod(ObjectRef, get_animation), + luamethod(ObjectRef, set_animation_frame_speed), + luamethod(ObjectRef, set_bone_position), + luamethod(ObjectRef, get_bone_position), + luamethod(ObjectRef, set_attach), + luamethod(ObjectRef, get_attach), + luamethod(ObjectRef, get_children), + luamethod(ObjectRef, set_detach), + luamethod(ObjectRef, set_properties), + luamethod(ObjectRef, get_properties), + luamethod(ObjectRef, set_nametag_attributes), + luamethod(ObjectRef, get_nametag_attributes), + + luamethod_aliased(ObjectRef, set_velocity, setvelocity), + luamethod_aliased(ObjectRef, add_velocity, add_player_velocity), + luamethod_aliased(ObjectRef, get_velocity, getvelocity), + luamethod_dep(ObjectRef, get_velocity, get_player_velocity), + + // LuaEntitySAO-only + luamethod_aliased(ObjectRef, set_acceleration, setacceleration), + luamethod_aliased(ObjectRef, get_acceleration, getacceleration), + luamethod_aliased(ObjectRef, set_yaw, setyaw), + luamethod_aliased(ObjectRef, get_yaw, getyaw), + luamethod(ObjectRef, set_rotation), + luamethod(ObjectRef, get_rotation), + luamethod_aliased(ObjectRef, set_texture_mod, settexturemod), + luamethod(ObjectRef, get_texture_mod), + luamethod_aliased(ObjectRef, set_sprite, setsprite), + luamethod(ObjectRef, get_entity_name), + luamethod(ObjectRef, get_luaentity), + + // Player-only + luamethod(ObjectRef, is_player), + luamethod(ObjectRef, get_player_name), + luamethod(ObjectRef, get_look_dir), + luamethod(ObjectRef, get_look_pitch), + luamethod(ObjectRef, get_look_yaw), + luamethod(ObjectRef, get_look_vertical), + luamethod(ObjectRef, get_look_horizontal), + luamethod(ObjectRef, set_look_horizontal), + luamethod(ObjectRef, set_look_vertical), + luamethod(ObjectRef, set_look_yaw), + luamethod(ObjectRef, set_look_pitch), + luamethod(ObjectRef, get_fov), + luamethod(ObjectRef, set_fov), + luamethod(ObjectRef, get_breath), + luamethod(ObjectRef, set_breath), + luamethod(ObjectRef, get_attribute), + luamethod(ObjectRef, set_attribute), + luamethod(ObjectRef, get_meta), + luamethod(ObjectRef, set_inventory_formspec), + luamethod(ObjectRef, get_inventory_formspec), + luamethod(ObjectRef, set_formspec_prepend), + luamethod(ObjectRef, get_formspec_prepend), + luamethod(ObjectRef, get_player_control), + luamethod(ObjectRef, get_player_control_bits), + luamethod(ObjectRef, set_physics_override), + luamethod(ObjectRef, get_physics_override), + luamethod(ObjectRef, hud_add), + luamethod(ObjectRef, hud_remove), + luamethod(ObjectRef, hud_change), + luamethod(ObjectRef, hud_get), + luamethod(ObjectRef, hud_set_flags), + luamethod(ObjectRef, hud_get_flags), + luamethod(ObjectRef, hud_set_hotbar_itemcount), + luamethod(ObjectRef, hud_get_hotbar_itemcount), + luamethod(ObjectRef, hud_set_hotbar_image), + luamethod(ObjectRef, hud_get_hotbar_image), + luamethod(ObjectRef, hud_set_hotbar_selected_image), + luamethod(ObjectRef, hud_get_hotbar_selected_image), + luamethod(ObjectRef, set_sky), + luamethod(ObjectRef, get_sky), + luamethod(ObjectRef, get_sky_color), + luamethod(ObjectRef, set_sun), + luamethod(ObjectRef, get_sun), + luamethod(ObjectRef, set_moon), + luamethod(ObjectRef, get_moon), + luamethod(ObjectRef, set_stars), + luamethod(ObjectRef, get_stars), + luamethod(ObjectRef, set_clouds), + luamethod(ObjectRef, get_clouds), + luamethod(ObjectRef, override_day_night_ratio), + luamethod(ObjectRef, get_day_night_ratio), + luamethod(ObjectRef, set_local_animation), + luamethod(ObjectRef, get_local_animation), + luamethod(ObjectRef, set_eye_offset), + luamethod(ObjectRef, get_eye_offset), + luamethod(ObjectRef, send_mapblock), + luamethod(ObjectRef, set_minimap_modes), + {0,0} +}; diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h new file mode 100644 index 000000000..941382c8a --- /dev/null +++ b/src/script/lua_api/l_object.h @@ -0,0 +1,378 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" +#include "irrlichttypes.h" + +class ServerActiveObject; +class LuaEntitySAO; +class PlayerSAO; +class RemotePlayer; + +/* + ObjectRef +*/ + +class ObjectRef : public ModApiBase { +public: + ObjectRef(ServerActiveObject *object); + + ~ObjectRef() = default; + + // Creates an ObjectRef and leaves it on top of stack + // Not callable from Lua; all references are created on the C side. + static void create(lua_State *L, ServerActiveObject *object); + + static void set_null(lua_State *L); + + static void Register(lua_State *L); + + static ObjectRef *checkobject(lua_State *L, int narg); + + static ServerActiveObject* getobject(ObjectRef *ref); +private: + ServerActiveObject *m_object = nullptr; + static const char className[]; + static luaL_Reg methods[]; + + + static LuaEntitySAO* getluaobject(ObjectRef *ref); + + static PlayerSAO* getplayersao(ObjectRef *ref); + + static RemotePlayer *getplayer(ObjectRef *ref); + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // remove(self) + static int l_remove(lua_State *L); + + // get_pos(self) + static int l_get_pos(lua_State *L); + + // set_pos(self, pos) + static int l_set_pos(lua_State *L); + + // move_to(self, pos, continuous) + static int l_move_to(lua_State *L); + + // punch(self, puncher, time_from_last_punch, tool_capabilities, dir) + static int l_punch(lua_State *L); + + // right_click(self, clicker) + static int l_right_click(lua_State *L); + + // set_hp(self, hp, reason) + static int l_set_hp(lua_State *L); + + // get_hp(self) + static int l_get_hp(lua_State *L); + + // get_inventory(self) + static int l_get_inventory(lua_State *L); + + // get_wield_list(self) + static int l_get_wield_list(lua_State *L); + + // get_wield_index(self) + static int l_get_wield_index(lua_State *L); + + // get_wielded_item(self) + static int l_get_wielded_item(lua_State *L); + + // set_wielded_item(self, item) + static int l_set_wielded_item(lua_State *L); + + // set_armor_groups(self, groups) + static int l_set_armor_groups(lua_State *L); + + // get_armor_groups(self) + static int l_get_armor_groups(lua_State *L); + + // set_physics_override(self, override_table) + static int l_set_physics_override(lua_State *L); + + // get_physics_override(self) + static int l_get_physics_override(lua_State *L); + + // set_animation(self, frame_range, frame_speed, frame_blend, frame_loop) + static int l_set_animation(lua_State *L); + + // set_animation_frame_speed(self, frame_speed) + static int l_set_animation_frame_speed(lua_State *L); + + // get_animation(self) + static int l_get_animation(lua_State *L); + + // set_bone_position(self, bone, position, rotation) + static int l_set_bone_position(lua_State *L); + + // get_bone_position(self, bone) + static int l_get_bone_position(lua_State *L); + + // set_attach(self, parent, bone, position, rotation) + static int l_set_attach(lua_State *L); + + // get_attach(self) + static int l_get_attach(lua_State *L); + + // get_children(self) + static int l_get_children(lua_State *L); + + // set_detach(self) + static int l_set_detach(lua_State *L); + + // set_properties(self, properties) + static int l_set_properties(lua_State *L); + + // get_properties(self) + static int l_get_properties(lua_State *L); + + // is_player(self) + static int l_is_player(lua_State *L); + + /* LuaEntitySAO-only */ + + // set_velocity(self, velocity) + static int l_set_velocity(lua_State *L); + + // add_velocity(self, velocity) + static int l_add_velocity(lua_State *L); + + // get_velocity(self) + static int l_get_velocity(lua_State *L); + + // set_acceleration(self, acceleration) + static int l_set_acceleration(lua_State *L); + + // get_acceleration(self) + static int l_get_acceleration(lua_State *L); + + // set_rotation(self, rotation) + static int l_set_rotation(lua_State *L); + + // get_rotation(self) + static int l_get_rotation(lua_State *L); + + // set_yaw(self, yaw) + static int l_set_yaw(lua_State *L); + + // get_yaw(self) + static int l_get_yaw(lua_State *L); + + // set_texture_mod(self, mod) + static int l_set_texture_mod(lua_State *L); + + // l_get_texture_mod(self) + static int l_get_texture_mod(lua_State *L); + + // set_sprite(self, start_frame, num_frames, framelength, select_x_by_camera) + static int l_set_sprite(lua_State *L); + + // DEPRECATED + // get_entity_name(self) + static int l_get_entity_name(lua_State *L); + + // get_luaentity(self) + static int l_get_luaentity(lua_State *L); + + /* Player-only */ + + // get_player_name(self) + static int l_get_player_name(lua_State *L); + + // get_fov(self) + static int l_get_fov(lua_State *L); + + // get_look_dir(self) + static int l_get_look_dir(lua_State *L); + + // DEPRECATED + // get_look_pitch(self) + static int l_get_look_pitch(lua_State *L); + + // DEPRECATED + // get_look_yaw(self) + static int l_get_look_yaw(lua_State *L); + + // get_look_pitch2(self) + static int l_get_look_vertical(lua_State *L); + + // get_look_yaw2(self) + static int l_get_look_horizontal(lua_State *L); + + // set_fov(self, degrees, is_multiplier, transition_time) + static int l_set_fov(lua_State *L); + + // set_look_vertical(self, radians) + static int l_set_look_vertical(lua_State *L); + + // set_look_horizontal(self, radians) + static int l_set_look_horizontal(lua_State *L); + + // DEPRECATED + // set_look_pitch(self, radians) + static int l_set_look_pitch(lua_State *L); + + // DEPRECATED + // set_look_yaw(self, radians) + static int l_set_look_yaw(lua_State *L); + + // set_breath(self, breath) + static int l_set_breath(lua_State *L); + + // get_breath(self, breath) + static int l_get_breath(lua_State *L); + + // DEPRECATED + // set_attribute(self, attribute, value) + static int l_set_attribute(lua_State *L); + + // DEPRECATED + // get_attribute(self, attribute) + static int l_get_attribute(lua_State *L); + + // get_meta(self) + static int l_get_meta(lua_State *L); + + // set_inventory_formspec(self, formspec) + static int l_set_inventory_formspec(lua_State *L); + + // get_inventory_formspec(self) + static int l_get_inventory_formspec(lua_State *L); + + // set_formspec_prepend(self, formspec) + static int l_set_formspec_prepend(lua_State *L); + + // get_formspec_prepend(self) + static int l_get_formspec_prepend(lua_State *L); + + // get_player_control(self) + static int l_get_player_control(lua_State *L); + + // get_player_control_bits(self) + static int l_get_player_control_bits(lua_State *L); + + // hud_add(self, id, form) + static int l_hud_add(lua_State *L); + + // hud_rm(self, id) + static int l_hud_remove(lua_State *L); + + // hud_change(self, id, stat, data) + static int l_hud_change(lua_State *L); + + // hud_get_next_id(self) + static u32 hud_get_next_id(lua_State *L); + + // hud_get(self, id) + static int l_hud_get(lua_State *L); + + // hud_set_flags(self, flags) + static int l_hud_set_flags(lua_State *L); + + // hud_get_flags() + static int l_hud_get_flags(lua_State *L); + + // hud_set_hotbar_itemcount(self, hotbar_itemcount) + static int l_hud_set_hotbar_itemcount(lua_State *L); + + // hud_get_hotbar_itemcount(self) + static int l_hud_get_hotbar_itemcount(lua_State *L); + + // hud_set_hotbar_image(self, name) + static int l_hud_set_hotbar_image(lua_State *L); + + // hud_get_hotbar_image(self) + static int l_hud_get_hotbar_image(lua_State *L); + + // hud_set_hotbar_selected_image(self, name) + static int l_hud_set_hotbar_selected_image(lua_State *L); + + // hud_get_hotbar_selected_image(self) + static int l_hud_get_hotbar_selected_image(lua_State *L); + + // set_sky(self, sky_parameters) + static int l_set_sky(lua_State *L); + + // get_sky(self) + static int l_get_sky(lua_State *L); + + // get_sky_color(self) + static int l_get_sky_color(lua_State* L); + + // set_sun(self, sun_parameters) + static int l_set_sun(lua_State *L); + + // get_sun(self) + static int l_get_sun(lua_State *L); + + // set_moon(self, moon_parameters) + static int l_set_moon(lua_State *L); + + // get_moon(self) + static int l_get_moon(lua_State *L); + + // set_stars(self, star_parameters) + static int l_set_stars(lua_State *L); + + // get_stars(self) + static int l_get_stars(lua_State *L); + + // set_clouds(self, cloud_parameters) + static int l_set_clouds(lua_State *L); + + // get_clouds(self) + static int l_get_clouds(lua_State *L); + + // override_day_night_ratio(self, type) + static int l_override_day_night_ratio(lua_State *L); + + // get_day_night_ratio(self) + static int l_get_day_night_ratio(lua_State *L); + + // set_local_animation(self, idle, walk, dig, walk_while_dig, frame_speed) + static int l_set_local_animation(lua_State *L); + + // get_local_animation(self) + static int l_get_local_animation(lua_State *L); + + // set_eye_offset(self, firstperson, thirdperson) + static int l_set_eye_offset(lua_State *L); + + // get_eye_offset(self) + static int l_get_eye_offset(lua_State *L); + + // set_nametag_attributes(self, attributes) + static int l_set_nametag_attributes(lua_State *L); + + // get_nametag_attributes(self) + static int l_get_nametag_attributes(lua_State *L); + + // send_mapblock(pos) + static int l_send_mapblock(lua_State *L); + + // set_minimap_modes(self, modes, wanted_mode) + static int l_set_minimap_modes(lua_State *L); +}; diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp new file mode 100644 index 000000000..712ce5105 --- /dev/null +++ b/src/script/lua_api/l_particles.cpp @@ -0,0 +1,279 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_particles.h" +#include "lua_api/l_object.h" +#include "lua_api/l_internal.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "server.h" +#include "particles.h" + +// add_particle({pos=, velocity=, acceleration=, expirationtime=, +// size=, collisiondetection=, collision_removal=, object_collision=, +// vertical=, texture=, player=}) +// pos/velocity/acceleration = {x=num, y=num, z=num} +// expirationtime = num (seconds) +// size = num +// collisiondetection = bool +// collision_removal = bool +// object_collision = bool +// vertical = bool +// texture = e.g."default_wood.png" +// animation = TileAnimation definition +// glow = num +int ModApiParticles::l_add_particle(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + // Get parameters + struct ParticleParameters p; + std::string playername; + + if (lua_gettop(L) > 1) // deprecated + { + log_deprecated(L, "Deprecated add_particle call with " + "individual parameters instead of definition"); + p.pos = check_v3f(L, 1); + p.vel = check_v3f(L, 2); + p.acc = check_v3f(L, 3); + p.expirationtime = luaL_checknumber(L, 4); + p.size = luaL_checknumber(L, 5); + p.collisiondetection = readParam(L, 6); + p.texture = luaL_checkstring(L, 7); + if (lua_gettop(L) == 8) // only spawn for a single player + playername = luaL_checkstring(L, 8); + } + else if (lua_istable(L, 1)) + { + lua_getfield(L, 1, "pos"); + if (lua_istable(L, -1)) + p.pos = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "vel"); + if (lua_istable(L, -1)) { + p.vel = check_v3f(L, -1); + log_deprecated(L, "The use of vel is deprecated. " + "Use velocity instead"); + } + lua_pop(L, 1); + + lua_getfield(L, 1, "velocity"); + if (lua_istable(L, -1)) + p.vel = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "acc"); + if (lua_istable(L, -1)) { + p.acc = check_v3f(L, -1); + log_deprecated(L, "The use of acc is deprecated. " + "Use acceleration instead"); + } + lua_pop(L, 1); + + lua_getfield(L, 1, "acceleration"); + if (lua_istable(L, -1)) + p.acc = check_v3f(L, -1); + lua_pop(L, 1); + + p.expirationtime = getfloatfield_default(L, 1, "expirationtime", + p.expirationtime); + p.size = getfloatfield_default(L, 1, "size", p.size); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); + + lua_getfield(L, 1, "animation"); + p.animation = read_animation_definition(L, -1); + lua_pop(L, 1); + + p.texture = getstringfield_default(L, 1, "texture", p.texture); + p.glow = getintfield_default(L, 1, "glow", p.glow); + + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); + + playername = getstringfield_default(L, 1, "playername", ""); + } + + getServer(L)->spawnParticle(playername, p); + return 1; +} + +// add_particlespawner({amount=, time=, +// minpos=, maxpos=, +// minvel=, maxvel=, +// minacc=, maxacc=, +// minexptime=, maxexptime=, +// minsize=, maxsize=, +// collisiondetection=, +// collision_removal=, +// object_collision=, +// vertical=, +// texture=, +// player=}) +// minpos/maxpos/minvel/maxvel/minacc/maxacc = {x=num, y=num, z=num} +// minexptime/maxexptime = num (seconds) +// minsize/maxsize = num +// collisiondetection = bool +// collision_removal = bool +// object_collision = bool +// vertical = bool +// texture = e.g."default_wood.png" +// animation = TileAnimation definition +// glow = num +int ModApiParticles::l_add_particlespawner(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + // Get parameters + ParticleSpawnerParameters p; + ServerActiveObject *attached = NULL; + std::string playername; + + if (lua_gettop(L) > 1) //deprecated + { + log_deprecated(L, "Deprecated add_particlespawner call with " + "individual parameters instead of definition"); + p.amount = luaL_checknumber(L, 1); + p.time = luaL_checknumber(L, 2); + p.minpos = check_v3f(L, 3); + p.maxpos = check_v3f(L, 4); + p.minvel = check_v3f(L, 5); + p.maxvel = check_v3f(L, 6); + p.minacc = check_v3f(L, 7); + p.maxacc = check_v3f(L, 8); + p.minexptime = luaL_checknumber(L, 9); + p.maxexptime = luaL_checknumber(L, 10); + p.minsize = luaL_checknumber(L, 11); + p.maxsize = luaL_checknumber(L, 12); + p.collisiondetection = readParam(L, 13); + p.texture = luaL_checkstring(L, 14); + if (lua_gettop(L) == 15) // only spawn for a single player + playername = luaL_checkstring(L, 15); + } + else if (lua_istable(L, 1)) + { + p.amount = getintfield_default(L, 1, "amount", p.amount); + p.time = getfloatfield_default(L, 1, "time", p.time); + + lua_getfield(L, 1, "minpos"); + if (lua_istable(L, -1)) + p.minpos = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "maxpos"); + if (lua_istable(L, -1)) + p.maxpos = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "minvel"); + if (lua_istable(L, -1)) + p.minvel = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "maxvel"); + if (lua_istable(L, -1)) + p.maxvel = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "minacc"); + if (lua_istable(L, -1)) + p.minacc = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "maxacc"); + if (lua_istable(L, -1)) + p.maxacc = check_v3f(L, -1); + lua_pop(L, 1); + + p.minexptime = getfloatfield_default(L, 1, "minexptime", p.minexptime); + p.maxexptime = getfloatfield_default(L, 1, "maxexptime", p.maxexptime); + p.minsize = getfloatfield_default(L, 1, "minsize", p.minsize); + p.maxsize = getfloatfield_default(L, 1, "maxsize", p.maxsize); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); + + lua_getfield(L, 1, "animation"); + p.animation = read_animation_definition(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "attached"); + if (!lua_isnil(L, -1)) { + ObjectRef *ref = ObjectRef::checkobject(L, -1); + lua_pop(L, 1); + attached = ObjectRef::getobject(ref); + } + + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); + p.texture = getstringfield_default(L, 1, "texture", p.texture); + playername = getstringfield_default(L, 1, "playername", ""); + p.glow = getintfield_default(L, 1, "glow", p.glow); + + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); + } + + u32 id = getServer(L)->addParticleSpawner(p, attached, playername); + lua_pushnumber(L, id); + + return 1; +} + +// delete_particlespawner(id, player) +// player (string) is optional +int ModApiParticles::l_delete_particlespawner(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + // Get parameters + u32 id = luaL_checknumber(L, 1); + std::string playername; + if (lua_gettop(L) == 2) { + playername = luaL_checkstring(L, 2); + } + + getServer(L)->deleteParticleSpawner(playername, id); + return 1; +} + +void ModApiParticles::Initialize(lua_State *L, int top) +{ + API_FCT(add_particle); + API_FCT(add_particlespawner); + API_FCT(delete_particlespawner); +} + diff --git a/src/script/lua_api/l_particles.h b/src/script/lua_api/l_particles.h new file mode 100644 index 000000000..19acde517 --- /dev/null +++ b/src/script/lua_api/l_particles.h @@ -0,0 +1,32 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" + +class ModApiParticles : public ModApiBase { +private: + static int l_add_particle(lua_State *L); + static int l_add_particlespawner(lua_State *L); + static int l_delete_particlespawner(lua_State *L); + +public: + static void Initialize(lua_State *L, int top); +}; diff --git a/src/script/lua_api/l_particles_local.cpp b/src/script/lua_api/l_particles_local.cpp new file mode 100644 index 000000000..e5959017d --- /dev/null +++ b/src/script/lua_api/l_particles_local.cpp @@ -0,0 +1,183 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 red-001 + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_particles_local.h" +#include "common/c_content.h" +#include "common/c_converter.h" +#include "lua_api/l_internal.h" +#include "lua_api/l_object.h" +#include "client/particles.h" +#include "client/client.h" +#include "client/clientevent.h" + +int ModApiParticlesLocal::l_add_particle(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TTABLE); + + // Get parameters + ParticleParameters p; + + lua_getfield(L, 1, "pos"); + if (lua_istable(L, -1)) + p.pos = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "velocity"); + if (lua_istable(L, -1)) + p.vel = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "acceleration"); + if (lua_istable(L, -1)) + p.acc = check_v3f(L, -1); + lua_pop(L, 1); + + p.expirationtime = getfloatfield_default(L, 1, "expirationtime", + p.expirationtime); + p.size = getfloatfield_default(L, 1, "size", p.size); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); + + lua_getfield(L, 1, "animation"); + p.animation = read_animation_definition(L, -1); + lua_pop(L, 1); + + p.texture = getstringfield_default(L, 1, "texture", p.texture); + p.glow = getintfield_default(L, 1, "glow", p.glow); + + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); + + ClientEvent *event = new ClientEvent(); + event->type = CE_SPAWN_PARTICLE; + event->spawn_particle = new ParticleParameters(p); + getClient(L)->pushToEventQueue(event); + + return 0; +} + +int ModApiParticlesLocal::l_add_particlespawner(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TTABLE); + + // Get parameters + ParticleSpawnerParameters p; + + p.amount = getintfield_default(L, 1, "amount", p.amount); + p.time = getfloatfield_default(L, 1, "time", p.time); + + lua_getfield(L, 1, "minpos"); + if (lua_istable(L, -1)) + p.minpos = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "maxpos"); + if (lua_istable(L, -1)) + p.maxpos = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "minvel"); + if (lua_istable(L, -1)) + p.minvel = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "maxvel"); + if (lua_istable(L, -1)) + p.maxvel = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "minacc"); + if (lua_istable(L, -1)) + p.minacc = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "maxacc"); + if (lua_istable(L, -1)) + p.maxacc = check_v3f(L, -1); + lua_pop(L, 1); + + p.minexptime = getfloatfield_default(L, 1, "minexptime", p.minexptime); + p.maxexptime = getfloatfield_default(L, 1, "maxexptime", p.maxexptime); + p.minsize = getfloatfield_default(L, 1, "minsize", p.minsize); + p.maxsize = getfloatfield_default(L, 1, "maxsize", p.maxsize); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); + + lua_getfield(L, 1, "animation"); + p.animation = read_animation_definition(L, -1); + lua_pop(L, 1); + + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); + p.texture = getstringfield_default(L, 1, "texture", p.texture); + p.glow = getintfield_default(L, 1, "glow", p.glow); + + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); + + u64 id = getClient(L)->getParticleManager()->generateSpawnerId(); + + auto event = new ClientEvent(); + event->type = CE_ADD_PARTICLESPAWNER; + event->add_particlespawner.p = new ParticleSpawnerParameters(p); + event->add_particlespawner.attached_id = 0; + event->add_particlespawner.id = id; + + getClient(L)->pushToEventQueue(event); + lua_pushnumber(L, id); + + return 1; +} + +int ModApiParticlesLocal::l_delete_particlespawner(lua_State *L) +{ + // Get parameters + u32 id = luaL_checknumber(L, 1); + + ClientEvent *event = new ClientEvent(); + event->type = CE_DELETE_PARTICLESPAWNER; + event->delete_particlespawner.id = id; + + getClient(L)->pushToEventQueue(event); + return 0; +} + +void ModApiParticlesLocal::Initialize(lua_State *L, int top) +{ + API_FCT(add_particle); + API_FCT(add_particlespawner); + API_FCT(delete_particlespawner); +} diff --git a/src/script/lua_api/l_particles_local.h b/src/script/lua_api/l_particles_local.h new file mode 100644 index 000000000..549e43cd7 --- /dev/null +++ b/src/script/lua_api/l_particles_local.h @@ -0,0 +1,34 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 red-001 + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" + +class ModApiParticlesLocal : public ModApiBase +{ +private: + static int l_add_particle(lua_State *L); + static int l_add_particlespawner(lua_State *L); + static int l_delete_particlespawner(lua_State *L); + +public: + static void Initialize(lua_State *L, int top); +}; diff --git a/src/script/lua_api/l_playermeta.cpp b/src/script/lua_api/l_playermeta.cpp new file mode 100644 index 000000000..6f9ca0838 --- /dev/null +++ b/src/script/lua_api/l_playermeta.cpp @@ -0,0 +1,123 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017-8 rubenwardy + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_playermeta.h" +#include "lua_api/l_internal.h" +#include "common/c_content.h" + +/* + PlayerMetaRef +*/ +PlayerMetaRef *PlayerMetaRef::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + + return *(PlayerMetaRef **)ud; // unbox pointer +} + +Metadata *PlayerMetaRef::getmeta(bool auto_create) +{ + return metadata; +} + +void PlayerMetaRef::clearMeta() +{ + metadata->clear(); +} + +void PlayerMetaRef::reportMetadataChange(const std::string *name) +{ + // TODO +} + +// garbage collector +int PlayerMetaRef::gc_object(lua_State *L) +{ + PlayerMetaRef *o = *(PlayerMetaRef **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +// Creates an PlayerMetaRef and leaves it on top of stack +// Not callable from Lua; all references are created on the C side. +void PlayerMetaRef::create(lua_State *L, Metadata *metadata) +{ + PlayerMetaRef *o = new PlayerMetaRef(metadata); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); +} + +void PlayerMetaRef::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "metadata_class"); + lua_pushlstring(L, className, strlen(className)); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pushliteral(L, "__eq"); + lua_pushcfunction(L, l_equals); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); + + // Cannot be created from Lua + // lua_register(L, className, create_object); +} + +// clang-format off +const char PlayerMetaRef::className[] = "PlayerMetaRef"; +const luaL_Reg PlayerMetaRef::methods[] = { + luamethod(MetaDataRef, contains), + luamethod(MetaDataRef, get), + luamethod(MetaDataRef, get_string), + luamethod(MetaDataRef, set_string), + luamethod(MetaDataRef, get_int), + luamethod(MetaDataRef, set_int), + luamethod(MetaDataRef, get_float), + luamethod(MetaDataRef, set_float), + luamethod(MetaDataRef, to_table), + luamethod(MetaDataRef, from_table), + luamethod(MetaDataRef, equals), + {0,0} +}; +// clang-format on diff --git a/src/script/lua_api/l_playermeta.h b/src/script/lua_api/l_playermeta.h new file mode 100644 index 000000000..7e1f42e60 --- /dev/null +++ b/src/script/lua_api/l_playermeta.h @@ -0,0 +1,57 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017-8 rubenwardy + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" +#include "lua_api/l_metadata.h" +#include "irrlichttypes_bloated.h" +#include "inventory.h" +#include "metadata.h" + +class PlayerMetaRef : public MetaDataRef +{ +private: + Metadata *metadata = nullptr; + + static const char className[]; + static const luaL_Reg methods[]; + + static PlayerMetaRef *checkobject(lua_State *L, int narg); + + virtual Metadata *getmeta(bool auto_create); + + virtual void clearMeta(); + + virtual void reportMetadataChange(const std::string *name = nullptr); + + // garbage collector + static int gc_object(lua_State *L); + +public: + PlayerMetaRef(Metadata *metadata) : metadata(metadata) {} + ~PlayerMetaRef() = default; + + // Creates an ItemStackMetaRef and leaves it on top of stack + // Not callable from Lua; all references are created on the C side. + static void create(lua_State *L, Metadata *metadata); + + static void Register(lua_State *L); +}; diff --git a/src/script/lua_api/l_rollback.cpp b/src/script/lua_api/l_rollback.cpp new file mode 100644 index 000000000..d872adc2f --- /dev/null +++ b/src/script/lua_api/l_rollback.cpp @@ -0,0 +1,117 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_rollback.h" +#include "lua_api/l_internal.h" +#include "common/c_converter.h" +#include "server.h" +#include "rollback_interface.h" + + +void push_RollbackNode(lua_State *L, RollbackNode &node) +{ + lua_createtable(L, 0, 3); + lua_pushstring(L, node.name.c_str()); + lua_setfield(L, -2, "name"); + lua_pushnumber(L, node.param1); + lua_setfield(L, -2, "param1"); + lua_pushnumber(L, node.param2); + lua_setfield(L, -2, "param2"); +} + +// rollback_get_node_actions(pos, range, seconds, limit) -> {{actor, pos, time, oldnode, newnode}, ...} +int ModApiRollback::l_rollback_get_node_actions(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + v3s16 pos = read_v3s16(L, 1); + int range = luaL_checknumber(L, 2); + time_t seconds = (time_t) luaL_checknumber(L, 3); + int limit = luaL_checknumber(L, 4); + Server *server = getServer(L); + IRollbackManager *rollback = server->getRollbackManager(); + if (rollback == NULL) { + return 0; + } + + std::list actions = rollback->getNodeActors(pos, range, seconds, limit); + std::list::iterator iter = actions.begin(); + + lua_createtable(L, actions.size(), 0); + for (unsigned int i = 1; iter != actions.end(); ++iter, ++i) { + lua_createtable(L, 0, 5); // Make a table with enough space pre-allocated + + lua_pushstring(L, iter->actor.c_str()); + lua_setfield(L, -2, "actor"); + + push_v3s16(L, iter->p); + lua_setfield(L, -2, "pos"); + + lua_pushnumber(L, iter->unix_time); + lua_setfield(L, -2, "time"); + + push_RollbackNode(L, iter->n_old); + lua_setfield(L, -2, "oldnode"); + + push_RollbackNode(L, iter->n_new); + lua_setfield(L, -2, "newnode"); + + lua_rawseti(L, -2, i); // Add action table to main table + } + + return 1; +} + +// rollback_revert_actions_by(actor, seconds) -> bool, log messages +int ModApiRollback::l_rollback_revert_actions_by(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + std::string actor = luaL_checkstring(L, 1); + int seconds = luaL_checknumber(L, 2); + Server *server = getServer(L); + IRollbackManager *rollback = server->getRollbackManager(); + + // If rollback is disabled, tell it's not a success. + if (rollback == NULL) { + lua_pushboolean(L, false); + lua_newtable(L); + return 2; + } + std::list actions = rollback->getRevertActions(actor, seconds); + std::list log; + bool success = server->rollbackRevertActions(actions, &log); + // Push boolean result + lua_pushboolean(L, success); + lua_createtable(L, log.size(), 0); + unsigned long i = 0; + for(std::list::const_iterator iter = log.begin(); + iter != log.end(); ++i, ++iter) { + lua_pushnumber(L, i); + lua_pushstring(L, iter->c_str()); + lua_settable(L, -3); + } + return 2; +} + +void ModApiRollback::Initialize(lua_State *L, int top) +{ + API_FCT(rollback_get_node_actions); + API_FCT(rollback_revert_actions_by); +} diff --git a/src/script/lua_api/l_rollback.h b/src/script/lua_api/l_rollback.h new file mode 100644 index 000000000..ae74a808a --- /dev/null +++ b/src/script/lua_api/l_rollback.h @@ -0,0 +1,35 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" + +class ModApiRollback : public ModApiBase +{ +private: + // rollback_get_node_actions(pos, range, seconds) -> {{actor, pos, time, oldnode, newnode}, ...} + static int l_rollback_get_node_actions(lua_State *L); + + // rollback_revert_actions_by(actor, seconds) -> bool, log messages + static int l_rollback_revert_actions_by(lua_State *L); + +public: + static void Initialize(lua_State *L, int top); +}; diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp new file mode 100644 index 000000000..b63dd3ac9 --- /dev/null +++ b/src/script/lua_api/l_server.cpp @@ -0,0 +1,571 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_server.h" +#include "lua_api/l_internal.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "cpp_api/s_base.h" +#include "cpp_api/s_security.h" +#include "server.h" +#include "environment.h" +#include "remoteplayer.h" +#include "log.h" +#include + +// request_shutdown() +int ModApiServer::l_request_shutdown(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *msg = lua_tolstring(L, 1, NULL); + bool reconnect = readParam(L, 2); + float seconds_before_shutdown = lua_tonumber(L, 3); + getServer(L)->requestShutdown(msg ? msg : "", reconnect, seconds_before_shutdown); + return 0; +} + +// get_server_status() +int ModApiServer::l_get_server_status(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + lua_pushstring(L, wide_to_narrow(getServer(L)->getStatusString()).c_str()); + return 1; +} + +// get_server_uptime() +int ModApiServer::l_get_server_uptime(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + lua_pushnumber(L, getServer(L)->getUptime()); + return 1; +} + + +// print(text) +int ModApiServer::l_print(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string text; + text = luaL_checkstring(L, 1); + getServer(L)->printToConsoleOnly(text); + return 0; +} + +// chat_send_all(text) +int ModApiServer::l_chat_send_all(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *text = luaL_checkstring(L, 1); + // Get server from registry + Server *server = getServer(L); + // Send + server->notifyPlayers(utf8_to_wide(text)); + return 0; +} + +// chat_send_player(name, text) +int ModApiServer::l_chat_send_player(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *name = luaL_checkstring(L, 1); + const char *text = luaL_checkstring(L, 2); + + // Get server from registry + Server *server = getServer(L); + // Send + server->notifyPlayer(name, utf8_to_wide(text)); + return 0; +} + +// get_player_privs(name, text) +int ModApiServer::l_get_player_privs(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *name = luaL_checkstring(L, 1); + // Get server from registry + Server *server = getServer(L); + // Do it + lua_newtable(L); + int table = lua_gettop(L); + std::set privs_s = server->getPlayerEffectivePrivs(name); + for (const std::string &privs_ : privs_s) { + lua_pushboolean(L, true); + lua_setfield(L, table, privs_.c_str()); + } + lua_pushvalue(L, table); + return 1; +} + +// get_player_ip() +int ModApiServer::l_get_player_ip(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char * name = luaL_checkstring(L, 1); + RemotePlayer *player = dynamic_cast(getEnv(L))->getPlayer(name); + if(player == NULL) + { + lua_pushnil(L); // no such player + return 1; + } + try + { + Address addr = getServer(L)->getPeerAddress(player->getPeerId()); + std::string ip_str = addr.serializeString(); + lua_pushstring(L, ip_str.c_str()); + return 1; + } catch (const con::PeerNotFoundException &) { + dstream << FUNCTION_NAME << ": peer was not found" << std::endl; + lua_pushnil(L); // error + return 1; + } +} + +// get_player_information(name) +int ModApiServer::l_get_player_information(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + Server *server = getServer(L); + + const char *name = luaL_checkstring(L, 1); + RemotePlayer *player = server->getEnv().getPlayer(name); + if (!player) { + lua_pushnil(L); // no such player + return 1; + } + + Address addr; + try { + addr = server->getPeerAddress(player->getPeerId()); + } catch (const con::PeerNotFoundException &) { + dstream << FUNCTION_NAME << ": peer was not found" << std::endl; + lua_pushnil(L); // error + return 1; + } + + float min_rtt, max_rtt, avg_rtt, min_jitter, max_jitter, avg_jitter; + ClientState state; + u32 uptime; + u16 prot_vers; + u8 ser_vers, major, minor, patch; + std::string vers_string, lang_code; + + auto getConInfo = [&] (con::rtt_stat_type type, float *value) -> bool { + return server->getClientConInfo(player->getPeerId(), type, value); + }; + + bool have_con_info = + getConInfo(con::MIN_RTT, &min_rtt) && + getConInfo(con::MAX_RTT, &max_rtt) && + getConInfo(con::AVG_RTT, &avg_rtt) && + getConInfo(con::MIN_JITTER, &min_jitter) && + getConInfo(con::MAX_JITTER, &max_jitter) && + getConInfo(con::AVG_JITTER, &avg_jitter); + + bool r = server->getClientInfo(player->getPeerId(), &state, &uptime, + &ser_vers, &prot_vers, &major, &minor, &patch, &vers_string, + &lang_code); + if (!r) { + dstream << FUNCTION_NAME << ": peer was not found" << std::endl; + lua_pushnil(L); // error + return 1; + } + + lua_newtable(L); + int table = lua_gettop(L); + + lua_pushstring(L,"address"); + lua_pushstring(L, addr.serializeString().c_str()); + lua_settable(L, table); + + lua_pushstring(L,"ip_version"); + if (addr.getFamily() == AF_INET) { + lua_pushnumber(L, 4); + } else if (addr.getFamily() == AF_INET6) { + lua_pushnumber(L, 6); + } else { + lua_pushnumber(L, 0); + } + lua_settable(L, table); + + if (have_con_info) { // may be missing + lua_pushstring(L, "min_rtt"); + lua_pushnumber(L, min_rtt); + lua_settable(L, table); + + lua_pushstring(L, "max_rtt"); + lua_pushnumber(L, max_rtt); + lua_settable(L, table); + + lua_pushstring(L, "avg_rtt"); + lua_pushnumber(L, avg_rtt); + lua_settable(L, table); + + lua_pushstring(L, "min_jitter"); + lua_pushnumber(L, min_jitter); + lua_settable(L, table); + + lua_pushstring(L, "max_jitter"); + lua_pushnumber(L, max_jitter); + lua_settable(L, table); + + lua_pushstring(L, "avg_jitter"); + lua_pushnumber(L, avg_jitter); + lua_settable(L, table); + } + + lua_pushstring(L,"connection_uptime"); + lua_pushnumber(L, uptime); + lua_settable(L, table); + + lua_pushstring(L,"protocol_version"); + lua_pushnumber(L, prot_vers); + lua_settable(L, table); + + lua_pushstring(L, "formspec_version"); + lua_pushnumber(L, player->formspec_version); + lua_settable(L, table); + + lua_pushstring(L, "lang_code"); + lua_pushstring(L, lang_code.c_str()); + lua_settable(L, table); + +#ifndef NDEBUG + lua_pushstring(L,"serialization_version"); + lua_pushnumber(L, ser_vers); + lua_settable(L, table); + + lua_pushstring(L,"major"); + lua_pushnumber(L, major); + lua_settable(L, table); + + lua_pushstring(L,"minor"); + lua_pushnumber(L, minor); + lua_settable(L, table); + + lua_pushstring(L,"patch"); + lua_pushnumber(L, patch); + lua_settable(L, table); + + lua_pushstring(L,"version_string"); + lua_pushstring(L, vers_string.c_str()); + lua_settable(L, table); + + lua_pushstring(L,"state"); + lua_pushstring(L,ClientInterface::state2Name(state).c_str()); + lua_settable(L, table); +#endif + + return 1; +} + +// get_ban_list() +int ModApiServer::l_get_ban_list(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + lua_pushstring(L, getServer(L)->getBanDescription("").c_str()); + return 1; +} + +// get_ban_description() +int ModApiServer::l_get_ban_description(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char * ip_or_name = luaL_checkstring(L, 1); + lua_pushstring(L, getServer(L)->getBanDescription(std::string(ip_or_name)).c_str()); + return 1; +} + +// ban_player() +int ModApiServer::l_ban_player(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char * name = luaL_checkstring(L, 1); + RemotePlayer *player = dynamic_cast(getEnv(L))->getPlayer(name); + if (player == NULL) { + lua_pushboolean(L, false); // no such player + return 1; + } + try + { + Address addr = getServer(L)->getPeerAddress( + dynamic_cast(getEnv(L))->getPlayer(name)->getPeerId()); + std::string ip_str = addr.serializeString(); + getServer(L)->setIpBanned(ip_str, name); + } catch(const con::PeerNotFoundException &) { + dstream << FUNCTION_NAME << ": peer was not found" << std::endl; + lua_pushboolean(L, false); // error + return 1; + } + lua_pushboolean(L, true); + return 1; +} + +// kick_player(name, [reason]) -> success +int ModApiServer::l_kick_player(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *name = luaL_checkstring(L, 1); + std::string message("Kicked"); + if (lua_isstring(L, 2)) + message.append(": ").append(readParam(L, 2)); + else + message.append("."); + + RemotePlayer *player = dynamic_cast(getEnv(L))->getPlayer(name); + if (player == NULL) { + lua_pushboolean(L, false); // No such player + return 1; + } + getServer(L)->DenyAccess_Legacy(player->getPeerId(), utf8_to_wide(message)); + lua_pushboolean(L, true); + return 1; +} + +// remove_player(name) +int ModApiServer::l_remove_player(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string name = luaL_checkstring(L, 1); + ServerEnvironment *s_env = dynamic_cast(getEnv(L)); + assert(s_env); + + RemotePlayer *player = s_env->getPlayer(name.c_str()); + if (!player) + lua_pushinteger(L, s_env->removePlayerFromDatabase(name) ? 0 : 1); + else + lua_pushinteger(L, 2); + + return 1; +} + +// unban_player_or_ip() +int ModApiServer::l_unban_player_or_ip(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char * ip_or_name = luaL_checkstring(L, 1); + getServer(L)->unsetIpBanned(ip_or_name); + lua_pushboolean(L, true); + return 1; +} + +// show_formspec(playername,formname,formspec) +int ModApiServer::l_show_formspec(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *playername = luaL_checkstring(L, 1); + const char *formname = luaL_checkstring(L, 2); + const char *formspec = luaL_checkstring(L, 3); + + if(getServer(L)->showFormspec(playername,formspec,formname)) + { + lua_pushboolean(L, true); + }else{ + lua_pushboolean(L, false); + } + return 1; +} + +// get_current_modname() +int ModApiServer::l_get_current_modname(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); + return 1; +} + +// get_modpath(modname) +int ModApiServer::l_get_modpath(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string modname = luaL_checkstring(L, 1); + const ModSpec *mod = getServer(L)->getModSpec(modname); + if (!mod) { + lua_pushnil(L); + return 1; + } + lua_pushstring(L, mod->path.c_str()); + return 1; +} + +// get_modnames() +// the returned list is sorted alphabetically for you +int ModApiServer::l_get_modnames(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + // Get a list of mods + std::vector modlist; + getServer(L)->getModNames(modlist); + + std::sort(modlist.begin(), modlist.end()); + + // Package them up for Lua + lua_createtable(L, modlist.size(), 0); + std::vector::iterator iter = modlist.begin(); + for (u16 i = 0; iter != modlist.end(); ++iter) { + lua_pushstring(L, iter->c_str()); + lua_rawseti(L, -2, ++i); + } + return 1; +} + +// get_worldpath() +int ModApiServer::l_get_worldpath(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string worldpath = getServer(L)->getWorldPath(); + lua_pushstring(L, worldpath.c_str()); + return 1; +} + +// sound_play(spec, parameters, [ephemeral]) +int ModApiServer::l_sound_play(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + SimpleSoundSpec spec; + read_soundspec(L, 1, spec); + ServerSoundParams params; + read_server_sound_params(L, 2, params); + bool ephemeral = lua_gettop(L) > 2 && readParam(L, 3); + if (ephemeral) { + getServer(L)->playSound(spec, params, true); + lua_pushnil(L); + } else { + s32 handle = getServer(L)->playSound(spec, params); + lua_pushinteger(L, handle); + } + return 1; +} + +// sound_stop(handle) +int ModApiServer::l_sound_stop(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + s32 handle = luaL_checkinteger(L, 1); + getServer(L)->stopSound(handle); + return 0; +} + +int ModApiServer::l_sound_fade(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + s32 handle = luaL_checkinteger(L, 1); + float step = readParam(L, 2); + float gain = readParam(L, 3); + getServer(L)->fadeSound(handle, step, gain); + return 0; +} + +// dynamic_add_media(filepath) +int ModApiServer::l_dynamic_add_media(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + // Reject adding media before the server has started up + if (!getEnv(L)) + throw LuaError("Dynamic media cannot be added before server has started up"); + + std::string filepath = readParam(L, 1); + CHECK_SECURE_PATH(L, filepath.c_str(), false); + + bool ok = getServer(L)->dynamicAddMedia(filepath); + lua_pushboolean(L, ok); + return 1; +} + +// is_singleplayer() +int ModApiServer::l_is_singleplayer(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + lua_pushboolean(L, getServer(L)->isSingleplayer()); + return 1; +} + +// notify_authentication_modified(name) +int ModApiServer::l_notify_authentication_modified(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string name; + if(lua_isstring(L, 1)) + name = readParam(L, 1); + getServer(L)->reportPrivsModified(name); + return 0; +} + +// get_last_run_mod() +int ModApiServer::l_get_last_run_mod(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); + std::string current_mod = readParam(L, -1, ""); + if (current_mod.empty()) { + lua_pop(L, 1); + lua_pushstring(L, getScriptApiBase(L)->getOrigin().c_str()); + } + return 1; +} + +// set_last_run_mod(modname) +int ModApiServer::l_set_last_run_mod(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; +#ifdef SCRIPTAPI_DEBUG + const char *mod = lua_tostring(L, 1); + getScriptApiBase(L)->setOriginDirect(mod); + //printf(">>>> last mod set from Lua: %s\n", mod); +#endif + return 0; +} + +void ModApiServer::Initialize(lua_State *L, int top) +{ + API_FCT(request_shutdown); + API_FCT(get_server_status); + API_FCT(get_server_uptime); + API_FCT(get_worldpath); + API_FCT(is_singleplayer); + + API_FCT(get_current_modname); + API_FCT(get_modpath); + API_FCT(get_modnames); + + API_FCT(print); + + API_FCT(chat_send_all); + API_FCT(chat_send_player); + API_FCT(show_formspec); + API_FCT(sound_play); + API_FCT(sound_stop); + API_FCT(sound_fade); + API_FCT(dynamic_add_media); + + API_FCT(get_player_information); + API_FCT(get_player_privs); + API_FCT(get_player_ip); + API_FCT(get_ban_list); + API_FCT(get_ban_description); + API_FCT(ban_player); + API_FCT(kick_player); + API_FCT(unban_player_or_ip); + API_FCT(notify_authentication_modified); + + API_FCT(get_last_run_mod); + API_FCT(set_last_run_mod); +} diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h new file mode 100644 index 000000000..9bf6a1469 --- /dev/null +++ b/src/script/lua_api/l_server.h @@ -0,0 +1,114 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" + +class ModApiServer : public ModApiBase +{ +private: + // request_shutdown([message], [reconnect]) + static int l_request_shutdown(lua_State *L); + + // get_server_status() + static int l_get_server_status(lua_State *L); + + // get_server_uptime() + static int l_get_server_uptime(lua_State *L); + + // get_worldpath() + static int l_get_worldpath(lua_State *L); + + // is_singleplayer() + static int l_is_singleplayer(lua_State *L); + + // get_current_modname() + static int l_get_current_modname(lua_State *L); + + // get_modpath(modname) + static int l_get_modpath(lua_State *L); + + // get_modnames() + // the returned list is sorted alphabetically for you + static int l_get_modnames(lua_State *L); + + // print(text) + static int l_print(lua_State *L); + + // chat_send_all(text) + static int l_chat_send_all(lua_State *L); + + // chat_send_player(name, text) + static int l_chat_send_player(lua_State *L); + + // show_formspec(playername,formname,formspec) + static int l_show_formspec(lua_State *L); + + // sound_play(spec, parameters) + static int l_sound_play(lua_State *L); + + // sound_stop(handle) + static int l_sound_stop(lua_State *L); + + // sound_fade(handle, step, gain) + static int l_sound_fade(lua_State *L); + + // dynamic_add_media(filepath) + static int l_dynamic_add_media(lua_State *L); + + // get_player_privs(name, text) + static int l_get_player_privs(lua_State *L); + + // get_player_ip() + static int l_get_player_ip(lua_State *L); + + // get_player_information(name) + static int l_get_player_information(lua_State *L); + + // get_ban_list() + static int l_get_ban_list(lua_State *L); + + // get_ban_description() + static int l_get_ban_description(lua_State *L); + + // ban_player() + static int l_ban_player(lua_State *L); + + // unban_player_or_ip() + static int l_unban_player_or_ip(lua_State *L); + + // kick_player(name, [message]) -> success + static int l_kick_player(lua_State *L); + + // remove_player(name) + static int l_remove_player(lua_State *L); + + // notify_authentication_modified(name) + static int l_notify_authentication_modified(lua_State *L); + + // get_last_run_mod() + static int l_get_last_run_mod(lua_State *L); + + // set_last_run_mod(modname) + static int l_set_last_run_mod(lua_State *L); + +public: + static void Initialize(lua_State *L, int top); +}; diff --git a/src/script/lua_api/l_settings.cpp b/src/script/lua_api/l_settings.cpp new file mode 100644 index 000000000..54d61a826 --- /dev/null +++ b/src/script/lua_api/l_settings.cpp @@ -0,0 +1,341 @@ +/* +Minetest +Copyright (C) 2013 PilzAdam + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_settings.h" +#include "lua_api/l_internal.h" +#include "cpp_api/s_security.h" +#include "util/string.h" // FlagDesc +#include "settings.h" +#include "noise.h" +#include "log.h" + + +#define SET_SECURITY_CHECK(L, name) \ + if (o->m_settings == g_settings && ScriptApiSecurity::isSecure(L) && \ + name.compare(0, 7, "secure.") == 0) { \ + throw LuaError("Attempt to set secure setting."); \ + } + +LuaSettings::LuaSettings(Settings *settings, const std::string &filename) : + m_settings(settings), + m_filename(filename) +{ +} + +LuaSettings::LuaSettings(const std::string &filename, bool write_allowed) : + m_filename(filename), + m_is_own_settings(true), + m_write_allowed(write_allowed) +{ + m_settings = new Settings(); + m_settings->readConfigFile(filename.c_str()); +} + +LuaSettings::~LuaSettings() +{ + if (m_is_own_settings) + delete m_settings; +} + + +void LuaSettings::create(lua_State *L, Settings *settings, + const std::string &filename) +{ + LuaSettings *o = new LuaSettings(settings, filename); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); +} + + +// garbage collector +int LuaSettings::gc_object(lua_State* L) +{ + LuaSettings* o = *(LuaSettings **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + + +// get(self, key) -> value +int LuaSettings::l_get(lua_State* L) +{ + NO_MAP_LOCK_REQUIRED; + LuaSettings* o = checkobject(L, 1); + + std::string key = std::string(luaL_checkstring(L, 2)); + if (o->m_settings->exists(key)) { + std::string value = o->m_settings->get(key); + lua_pushstring(L, value.c_str()); + } else { + lua_pushnil(L); + } + + return 1; +} + +// get_bool(self, key) -> boolean +int LuaSettings::l_get_bool(lua_State* L) +{ + NO_MAP_LOCK_REQUIRED; + LuaSettings* o = checkobject(L, 1); + + std::string key = std::string(luaL_checkstring(L, 2)); + if (o->m_settings->exists(key)) { + bool value = o->m_settings->getBool(key); + lua_pushboolean(L, value); + } else { + // Push default value + if (lua_isboolean(L, 3)) + lua_pushboolean(L, readParam(L, 3)); + else + lua_pushnil(L); + } + + return 1; +} + +// get_np_group(self, key) -> value +int LuaSettings::l_get_np_group(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaSettings *o = checkobject(L, 1); + + std::string key = std::string(luaL_checkstring(L, 2)); + if (o->m_settings->exists(key)) { + NoiseParams np; + o->m_settings->getNoiseParams(key, np); + push_noiseparams(L, &np); + } else { + lua_pushnil(L); + } + + return 1; +} + +int LuaSettings::l_get_flags(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaSettings *o = checkobject(L, 1); + std::string key = std::string(luaL_checkstring(L, 2)); + + u32 flags = 0; + auto flagdesc = o->m_settings->getFlagDescFallback(key); + if (o->m_settings->getFlagStrNoEx(key, flags, flagdesc)) { + lua_newtable(L); + int table = lua_gettop(L); + for (size_t i = 0; flagdesc[i].name; ++i) { + lua_pushboolean(L, flags & flagdesc[i].flag); + lua_setfield(L, table, flagdesc[i].name); + } + lua_pushvalue(L, table); + } else { + lua_pushnil(L); + } + + return 1; +} + +// set(self, key, value) +int LuaSettings::l_set(lua_State* L) +{ + NO_MAP_LOCK_REQUIRED; + LuaSettings* o = checkobject(L, 1); + + std::string key = std::string(luaL_checkstring(L, 2)); + const char* value = luaL_checkstring(L, 3); + + SET_SECURITY_CHECK(L, key); + + if (!o->m_settings->set(key, value)) + throw LuaError("Invalid sequence found in setting parameters"); + + return 0; +} + +// set_bool(self, key, value) +int LuaSettings::l_set_bool(lua_State* L) +{ + NO_MAP_LOCK_REQUIRED; + LuaSettings* o = checkobject(L, 1); + + std::string key = std::string(luaL_checkstring(L, 2)); + bool value = readParam(L, 3); + + SET_SECURITY_CHECK(L, key); + + o->m_settings->setBool(key, value); + + return 1; +} + +// set(self, key, value) +int LuaSettings::l_set_np_group(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaSettings *o = checkobject(L, 1); + + std::string key = std::string(luaL_checkstring(L, 2)); + NoiseParams value; + read_noiseparams(L, 3, &value); + + SET_SECURITY_CHECK(L, key); + + o->m_settings->setNoiseParams(key, value, false); + + return 0; +} + +// remove(self, key) -> success +int LuaSettings::l_remove(lua_State* L) +{ + NO_MAP_LOCK_REQUIRED; + LuaSettings* o = checkobject(L, 1); + + std::string key = std::string(luaL_checkstring(L, 2)); + + SET_SECURITY_CHECK(L, key); + + bool success = o->m_settings->remove(key); + lua_pushboolean(L, success); + + return 1; +} + +// get_names(self) -> {key1, ...} +int LuaSettings::l_get_names(lua_State* L) +{ + NO_MAP_LOCK_REQUIRED; + LuaSettings* o = checkobject(L, 1); + + std::vector keys = o->m_settings->getNames(); + + lua_newtable(L); + for (unsigned int i=0; i < keys.size(); i++) + { + lua_pushstring(L, keys[i].c_str()); + lua_rawseti(L, -2, i + 1); + } + + return 1; +} + +// write(self) -> success +int LuaSettings::l_write(lua_State* L) +{ + NO_MAP_LOCK_REQUIRED; + LuaSettings* o = checkobject(L, 1); + + if (!o->m_write_allowed) { + throw LuaError("Settings: writing " + o->m_filename + + " not allowed with mod security on."); + } + + bool success = o->m_settings->updateConfigFile(o->m_filename.c_str()); + lua_pushboolean(L, success); + + return 1; +} + +// to_table(self) -> {[key1]=value1,...} +int LuaSettings::l_to_table(lua_State* L) +{ + NO_MAP_LOCK_REQUIRED; + LuaSettings* o = checkobject(L, 1); + + std::vector keys = o->m_settings->getNames(); + + lua_newtable(L); + for (const std::string &key : keys) { + lua_pushstring(L, o->m_settings->get(key).c_str()); + lua_setfield(L, -2, key.c_str()); + } + + return 1; +} + + +void LuaSettings::Register(lua_State* L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Can be created from Lua (Settings(filename)) + lua_register(L, className, create_object); +} + +// LuaSettings(filename) +// Creates a LuaSettings and leaves it on top of the stack +int LuaSettings::create_object(lua_State* L) +{ + NO_MAP_LOCK_REQUIRED; + bool write_allowed = true; + const char* filename = luaL_checkstring(L, 1); + CHECK_SECURE_PATH_POSSIBLE_WRITE(L, filename, &write_allowed); + LuaSettings* o = new LuaSettings(filename, write_allowed); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + +LuaSettings* LuaSettings::checkobject(lua_State* L, int narg) +{ + NO_MAP_LOCK_REQUIRED; + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + return *(LuaSettings**) ud; // unbox pointer +} + +const char LuaSettings::className[] = "Settings"; +const luaL_Reg LuaSettings::methods[] = { + luamethod(LuaSettings, get), + luamethod(LuaSettings, get_bool), + luamethod(LuaSettings, get_np_group), + luamethod(LuaSettings, get_flags), + luamethod(LuaSettings, set), + luamethod(LuaSettings, set_bool), + luamethod(LuaSettings, set_np_group), + luamethod(LuaSettings, remove), + luamethod(LuaSettings, get_names), + luamethod(LuaSettings, write), + luamethod(LuaSettings, to_table), + {0,0} +}; diff --git a/src/script/lua_api/l_settings.h b/src/script/lua_api/l_settings.h new file mode 100644 index 000000000..e1ff07fc1 --- /dev/null +++ b/src/script/lua_api/l_settings.h @@ -0,0 +1,88 @@ +/* +Minetest +Copyright (C) 2013 PilzAdam + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "common/c_content.h" +#include "lua_api/l_base.h" + +class Settings; + +class LuaSettings : public ModApiBase +{ +private: + static const char className[]; + static const luaL_Reg methods[]; + + // garbage collector + static int gc_object(lua_State *L); + + // get(self, key) -> value + static int l_get(lua_State *L); + + // get_bool(self, key) -> boolean + static int l_get_bool(lua_State *L); + + // get_np_group(self, key) -> noiseparam + static int l_get_np_group(lua_State *L); + + // get_flags(self, key) -> key/value table + static int l_get_flags(lua_State *L); + + // set(self, key, value) + static int l_set(lua_State *L); + + // set_bool(self, key, value) + static int l_set_bool(lua_State *L); + + // set_np_group(self, key, value) + static int l_set_np_group(lua_State *L); + + // remove(self, key) -> success + static int l_remove(lua_State *L); + + // get_names(self) -> {key1, ...} + static int l_get_names(lua_State *L); + + // write(self) -> success + static int l_write(lua_State *L); + + // to_table(self) -> {[key1]=value1,...} + static int l_to_table(lua_State *L); + + Settings *m_settings = nullptr; + std::string m_filename; + bool m_is_own_settings = false; + bool m_write_allowed = true; + +public: + LuaSettings(Settings *settings, const std::string &filename); + LuaSettings(const std::string &filename, bool write_allowed); + ~LuaSettings(); + + static void create(lua_State *L, Settings *settings, const std::string &filename); + + // LuaSettings(filename) + // Creates a LuaSettings and leaves it on top of the stack + static int create_object(lua_State *L); + + static LuaSettings *checkobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; diff --git a/src/script/lua_api/l_sound.cpp b/src/script/lua_api/l_sound.cpp new file mode 100644 index 000000000..e2218639d --- /dev/null +++ b/src/script/lua_api/l_sound.cpp @@ -0,0 +1,53 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "l_sound.h" +#include "l_internal.h" +#include "common/c_content.h" +#include "gui/guiEngine.h" + + +int ModApiSound::l_sound_play(lua_State *L) +{ + SimpleSoundSpec spec; + read_soundspec(L, 1, spec); + bool looped = readParam(L, 2); + + s32 handle = getGuiEngine(L)->playSound(spec, looped); + + lua_pushinteger(L, handle); + + return 1; +} + +int ModApiSound::l_sound_stop(lua_State *L) +{ + u32 handle = luaL_checkinteger(L, 1); + + getGuiEngine(L)->stopSound(handle); + + return 1; +} + +void ModApiSound::Initialize(lua_State *L, int top) +{ + API_FCT(sound_play); + API_FCT(sound_stop); +} diff --git a/src/script/lua_api/l_sound.h b/src/script/lua_api/l_sound.h new file mode 100644 index 000000000..666f88dc5 --- /dev/null +++ b/src/script/lua_api/l_sound.h @@ -0,0 +1,33 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" + +class ModApiSound : public ModApiBase +{ +private: + static int l_sound_play(lua_State *L); + static int l_sound_stop(lua_State *L); + +public: + static void Initialize(lua_State *L, int top); +}; diff --git a/src/script/lua_api/l_storage.cpp b/src/script/lua_api/l_storage.cpp new file mode 100644 index 000000000..229c0995e --- /dev/null +++ b/src/script/lua_api/l_storage.cpp @@ -0,0 +1,155 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_storage.h" +#include "l_internal.h" +#include "content/mods.h" +#include "server.h" + +int ModApiStorage::l_get_mod_storage(lua_State *L) +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); + if (!lua_isstring(L, -1)) { + return 0; + } + + std::string mod_name = readParam(L, -1); + + ModMetadata *store = new ModMetadata(mod_name); + if (IGameDef *gamedef = getGameDef(L)) { + store->load(gamedef->getModStoragePath()); + gamedef->registerModStorage(store); + } else { + delete store; + assert(false); // this should not happen + } + + StorageRef::create(L, store); + int object = lua_gettop(L); + + lua_pushvalue(L, object); + return 1; +} + +void ModApiStorage::Initialize(lua_State *L, int top) +{ + API_FCT(get_mod_storage); +} + +StorageRef::StorageRef(ModMetadata *object): + m_object(object) +{ +} + +StorageRef::~StorageRef() +{ + delete m_object; +} + +void StorageRef::create(lua_State *L, ModMetadata *object) +{ + StorageRef *o = new StorageRef(object); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); +} + +int StorageRef::gc_object(lua_State *L) +{ + StorageRef *o = *(StorageRef **)(lua_touserdata(L, 1)); + // Server side + if (IGameDef *gamedef = getGameDef(L)) + gamedef->unregisterModStorage(getobject(o)->getModName()); + delete o; + return 0; +} + +void StorageRef::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "metadata_class"); + lua_pushlstring(L, className, strlen(className)); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pushliteral(L, "__eq"); + lua_pushcfunction(L, l_equals); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable +} + +StorageRef* StorageRef::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if (!ud) luaL_typerror(L, narg, className); + return *(StorageRef**)ud; // unbox pointer +} + +ModMetadata* StorageRef::getobject(StorageRef *ref) +{ + ModMetadata *co = ref->m_object; + return co; +} + +Metadata* StorageRef::getmeta(bool auto_create) +{ + return m_object; +} + +void StorageRef::clearMeta() +{ + m_object->clear(); +} + +const char StorageRef::className[] = "StorageRef"; +const luaL_Reg StorageRef::methods[] = { + luamethod(MetaDataRef, contains), + luamethod(MetaDataRef, get), + luamethod(MetaDataRef, get_string), + luamethod(MetaDataRef, set_string), + luamethod(MetaDataRef, get_int), + luamethod(MetaDataRef, set_int), + luamethod(MetaDataRef, get_float), + luamethod(MetaDataRef, set_float), + luamethod(MetaDataRef, to_table), + luamethod(MetaDataRef, from_table), + luamethod(MetaDataRef, equals), + {0,0} +}; diff --git a/src/script/lua_api/l_storage.h b/src/script/lua_api/l_storage.h new file mode 100644 index 000000000..2674f3a44 --- /dev/null +++ b/src/script/lua_api/l_storage.h @@ -0,0 +1,60 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "l_metadata.h" +#include "lua_api/l_base.h" + +class ModMetadata; + +class ModApiStorage : public ModApiBase +{ +protected: + static int l_get_mod_storage(lua_State *L); + +public: + static void Initialize(lua_State *L, int top); +}; + +class StorageRef : public MetaDataRef +{ +private: + ModMetadata *m_object = nullptr; + + static const char className[]; + static const luaL_Reg methods[]; + + virtual Metadata *getmeta(bool auto_create); + virtual void clearMeta(); + + // garbage collector + static int gc_object(lua_State *L); + +public: + StorageRef(ModMetadata *object); + ~StorageRef(); + + static void Register(lua_State *L); + static void create(lua_State *L, ModMetadata *object); + + static StorageRef *checkobject(lua_State *L, int narg); + static ModMetadata *getobject(StorageRef *ref); +}; diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp new file mode 100644 index 000000000..d3cda53b5 --- /dev/null +++ b/src/script/lua_api/l_util.cpp @@ -0,0 +1,573 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_util.h" +#include "lua_api/l_internal.h" +#include "lua_api/l_settings.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "cpp_api/s_async.h" +#include "serialization.h" +#include +#include "cpp_api/s_security.h" +#include "porting.h" +#include "convert_json.h" +#include "debug.h" +#include "log.h" +#include "tool.h" +#include "filesys.h" +#include "settings.h" +#include "util/auth.h" +#include "util/base64.h" +#include "config.h" +#include "version.h" +#include "util/hex.h" +#include "util/sha1.h" +#include + + +// log([level,] text) +// Writes a line to the logger. +// The one-argument version logs to LL_NONE. +// The two-argument version accepts a log level. +// Either the special case "deprecated" for deprecation notices, or any specified in +// Logger::stringToLevel(name). +int ModApiUtil::l_log(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string text; + LogLevel level = LL_NONE; + if (lua_isnone(L, 2)) { + text = luaL_checkstring(L, 1); + } else { + std::string name = luaL_checkstring(L, 1); + text = luaL_checkstring(L, 2); + if (name == "deprecated") { + log_deprecated(L, text, 2); + return 0; + } + level = Logger::stringToLevel(name); + if (level == LL_MAX) { + warningstream << "Tried to log at unknown level '" << name + << "'. Defaulting to \"none\"." << std::endl; + level = LL_NONE; + } + } + g_logger.log(level, text); + return 0; +} + +// get_us_time() +int ModApiUtil::l_get_us_time(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + lua_pushnumber(L, porting::getTimeUs()); + return 1; +} + +// parse_json(str[, nullvalue]) +int ModApiUtil::l_parse_json(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + const char *jsonstr = luaL_checkstring(L, 1); + + // Use passed nullvalue or default to nil + int nullindex = 2; + if (lua_isnone(L, nullindex)) { + lua_pushnil(L); + nullindex = lua_gettop(L); + } + + Json::Value root; + + { + std::istringstream stream(jsonstr); + + Json::CharReaderBuilder builder; + builder.settings_["collectComments"] = false; + std::string errs; + + if (!Json::parseFromStream(builder, stream, &root, &errs)) { + errorstream << "Failed to parse json data " << errs << std::endl; + size_t jlen = strlen(jsonstr); + if (jlen > 100) { + errorstream << "Data (" << jlen + << " bytes) printed to warningstream." << std::endl; + warningstream << "data: \"" << jsonstr << "\"" << std::endl; + } else { + errorstream << "data: \"" << jsonstr << "\"" << std::endl; + } + lua_pushnil(L); + return 1; + } + } + + if (!push_json_value(L, root, nullindex)) { + errorstream << "Failed to parse json data, " + << "depth exceeds lua stack limit" << std::endl; + errorstream << "data: \"" << jsonstr << "\"" << std::endl; + lua_pushnil(L); + } + return 1; +} + +// write_json(data[, styled]) -> string or nil and error message +int ModApiUtil::l_write_json(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + bool styled = false; + if (!lua_isnone(L, 2)) { + styled = readParam(L, 2); + lua_pop(L, 1); + } + + Json::Value root; + try { + read_json_value(L, root, 1); + } catch (SerializationError &e) { + lua_pushnil(L); + lua_pushstring(L, e.what()); + return 2; + } + + std::string out; + if (styled) { + out = root.toStyledString(); + } else { + out = fastWriteJson(root); + } + lua_pushlstring(L, out.c_str(), out.size()); + return 1; +} + +// get_dig_params(groups, tool_capabilities) +int ModApiUtil::l_get_dig_params(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ItemGroupList groups; + read_groups(L, 1, groups); + ToolCapabilities tp = read_tool_capabilities(L, 2); + push_dig_params(L, getDigParams(groups, &tp)); + return 1; +} + +// get_hit_params(groups, tool_capabilities[, time_from_last_punch]) +int ModApiUtil::l_get_hit_params(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::unordered_map groups; + read_groups(L, 1, groups); + ToolCapabilities tp = read_tool_capabilities(L, 2); + if(lua_isnoneornil(L, 3)) + push_hit_params(L, getHitParams(groups, &tp)); + else + push_hit_params(L, getHitParams(groups, &tp, readParam(L, 3))); + return 1; +} + +// check_password_entry(name, entry, password) +int ModApiUtil::l_check_password_entry(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string name = luaL_checkstring(L, 1); + std::string entry = luaL_checkstring(L, 2); + std::string password = luaL_checkstring(L, 3); + + if (base64_is_valid(entry)) { + std::string hash = translate_password(name, password); + lua_pushboolean(L, hash == entry); + return 1; + } + + std::string salt; + std::string verifier; + + if (!decode_srp_verifier_and_salt(entry, &verifier, &salt)) { + // invalid format + warningstream << "Invalid password format for " << name << std::endl; + lua_pushboolean(L, false); + return 1; + } + std::string gen_verifier = generate_srp_verifier(name, password, salt); + + lua_pushboolean(L, gen_verifier == verifier); + return 1; +} + +// get_password_hash(name, raw_password) +int ModApiUtil::l_get_password_hash(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string name = luaL_checkstring(L, 1); + std::string raw_password = luaL_checkstring(L, 2); + std::string hash = translate_password(name, raw_password); + lua_pushstring(L, hash.c_str()); + return 1; +} + +// is_yes(arg) +int ModApiUtil::l_is_yes(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + lua_getglobal(L, "tostring"); // function to be called + lua_pushvalue(L, 1); // 1st argument + lua_call(L, 1, 1); // execute function + std::string str = readParam(L, -1); // get result + lua_pop(L, 1); + + bool yes = is_yes(str); + lua_pushboolean(L, yes); + return 1; +} + +// is_nan(arg) +int ModApiUtil::l_is_nan(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + lua_pushboolean(L, isNaN(L, 1)); + return 1; +} + +// get_builtin_path() +int ModApiUtil::l_get_builtin_path(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + std::string path = porting::path_share + DIR_DELIM + "builtin" + DIR_DELIM; + lua_pushstring(L, path.c_str()); + + return 1; +} + +// compress(data, method, level) +int ModApiUtil::l_compress(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + size_t size; + const char *data = luaL_checklstring(L, 1, &size); + + int level = -1; + if (!lua_isnone(L, 3) && !lua_isnil(L, 3)) + level = readParam(L, 3); + + std::ostringstream os; + compressZlib(std::string(data, size), os, level); + + std::string out = os.str(); + + lua_pushlstring(L, out.data(), out.size()); + return 1; +} + +// decompress(data, method) +int ModApiUtil::l_decompress(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + size_t size; + const char *data = luaL_checklstring(L, 1, &size); + + std::istringstream is(std::string(data, size)); + std::ostringstream os; + decompressZlib(is, os); + + std::string out = os.str(); + + lua_pushlstring(L, out.data(), out.size()); + return 1; +} + +// encode_base64(string) +int ModApiUtil::l_encode_base64(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + size_t size; + const char *data = luaL_checklstring(L, 1, &size); + + std::string out = base64_encode((const unsigned char *)(data), size); + + lua_pushlstring(L, out.data(), out.size()); + return 1; +} + +// decode_base64(string) +int ModApiUtil::l_decode_base64(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + size_t size; + const char *d = luaL_checklstring(L, 1, &size); + const std::string data = std::string(d, size); + + if (!base64_is_valid(data)) + return 0; + + std::string out = base64_decode(data); + + lua_pushlstring(L, out.data(), out.size()); + return 1; +} + +// mkdir(path) +int ModApiUtil::l_mkdir(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *path = luaL_checkstring(L, 1); + CHECK_SECURE_PATH(L, path, true); + lua_pushboolean(L, fs::CreateAllDirs(path)); + return 1; +} + +// get_dir_list(path, is_dir) +int ModApiUtil::l_get_dir_list(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *path = luaL_checkstring(L, 1); + bool list_all = !lua_isboolean(L, 2); // if its not a boolean list all + bool list_dirs = readParam(L, 2); // true: list dirs, false: list files + + CHECK_SECURE_PATH(L, path, false); + + std::vector list = fs::GetDirListing(path); + + int index = 0; + lua_newtable(L); + + for (const fs::DirListNode &dln : list) { + if (list_all || list_dirs == dln.dir) { + lua_pushstring(L, dln.name.c_str()); + lua_rawseti(L, -2, ++index); + } + } + + return 1; +} + +// safe_file_write(path, content) +int ModApiUtil::l_safe_file_write(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *path = luaL_checkstring(L, 1); + size_t size; + const char *content = luaL_checklstring(L, 2, &size); + + CHECK_SECURE_PATH(L, path, true); + + bool ret = fs::safeWriteToFile(path, std::string(content, size)); + lua_pushboolean(L, ret); + + return 1; +} + +// request_insecure_environment() +int ModApiUtil::l_request_insecure_environment(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + // Just return _G if security is disabled + if (!ScriptApiSecurity::isSecure(L)) { + lua_getglobal(L, "_G"); + return 1; + } + + // We have to make sure that this function is being called directly by + // a mod, otherwise a malicious mod could override this function and + // steal its return value. + lua_Debug info; + // Make sure there's only one item below this function on the stack... + if (lua_getstack(L, 2, &info)) { + return 0; + } + FATAL_ERROR_IF(!lua_getstack(L, 1, &info), "lua_getstack() failed"); + FATAL_ERROR_IF(!lua_getinfo(L, "S", &info), "lua_getinfo() failed"); + // ...and that that item is the main file scope. + if (strcmp(info.what, "main") != 0) { + return 0; + } + + // Get mod name + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); + if (!lua_isstring(L, -1)) { + return 0; + } + + // Check secure.trusted_mods + std::string mod_name = readParam(L, -1); + std::string trusted_mods = g_settings->get("secure.trusted_mods"); + trusted_mods.erase(std::remove_if(trusted_mods.begin(), + trusted_mods.end(), static_cast(&std::isspace)), + trusted_mods.end()); + std::vector mod_list = str_split(trusted_mods, ','); + if (std::find(mod_list.begin(), mod_list.end(), mod_name) == + mod_list.end()) { + return 0; + } + + // Push insecure environment + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP); + return 1; +} + +// get_version() +int ModApiUtil::l_get_version(lua_State *L) +{ + lua_createtable(L, 0, 3); + int table = lua_gettop(L); + + lua_pushstring(L, PROJECT_NAME_C); + lua_setfield(L, table, "project"); + + lua_pushstring(L, g_version_string); + lua_setfield(L, table, "string"); + + if (strcmp(g_version_string, g_version_hash) != 0) { + lua_pushstring(L, g_version_hash); + lua_setfield(L, table, "hash"); + } + + return 1; +} + +int ModApiUtil::l_sha1(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + size_t size; + const char *data = luaL_checklstring(L, 1, &size); + bool hex = !lua_isboolean(L, 2) || !readParam(L, 2); + + // Compute actual checksum of data + std::string data_sha1; + { + SHA1 ctx; + ctx.addBytes(data, size); + unsigned char *data_tmpdigest = ctx.getDigest(); + data_sha1.assign((char*) data_tmpdigest, 20); + free(data_tmpdigest); + } + + if (hex) { + std::string sha1_hex = hex_encode(data_sha1); + lua_pushstring(L, sha1_hex.c_str()); + } else { + lua_pushlstring(L, data_sha1.data(), data_sha1.size()); + } + + return 1; +} + +void ModApiUtil::Initialize(lua_State *L, int top) +{ + API_FCT(log); + + API_FCT(get_us_time); + + API_FCT(parse_json); + API_FCT(write_json); + + API_FCT(get_dig_params); + API_FCT(get_hit_params); + + API_FCT(check_password_entry); + API_FCT(get_password_hash); + + API_FCT(is_yes); + API_FCT(is_nan); + + API_FCT(get_builtin_path); + + API_FCT(compress); + API_FCT(decompress); + + API_FCT(mkdir); + API_FCT(get_dir_list); + API_FCT(safe_file_write); + + API_FCT(request_insecure_environment); + + API_FCT(encode_base64); + API_FCT(decode_base64); + + API_FCT(get_version); + API_FCT(sha1); + + LuaSettings::create(L, g_settings, g_settings_path); + lua_setfield(L, top, "settings"); +} + +void ModApiUtil::InitializeClient(lua_State *L, int top) +{ + API_FCT(log); + + API_FCT(get_us_time); + + API_FCT(parse_json); + API_FCT(write_json); + + API_FCT(is_yes); + API_FCT(is_nan); + + API_FCT(compress); + API_FCT(decompress); + + API_FCT(request_insecure_environment); + + API_FCT(encode_base64); + API_FCT(decode_base64); + + API_FCT(get_version); + API_FCT(sha1); + + LuaSettings::create(L, g_settings, g_settings_path); + lua_setfield(L, top, "settings"); +} + +void ModApiUtil::InitializeAsync(lua_State *L, int top) +{ + API_FCT(log); + + API_FCT(get_us_time); + + API_FCT(parse_json); + API_FCT(write_json); + + API_FCT(is_yes); + + API_FCT(get_builtin_path); + + API_FCT(compress); + API_FCT(decompress); + + API_FCT(mkdir); + API_FCT(get_dir_list); + + API_FCT(encode_base64); + API_FCT(decode_base64); + + API_FCT(get_version); + API_FCT(sha1); + + LuaSettings::create(L, g_settings, g_settings_path); + lua_setfield(L, top, "settings"); +} diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h new file mode 100644 index 000000000..5d13ad35b --- /dev/null +++ b/src/script/lua_api/l_util.h @@ -0,0 +1,110 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" + +class AsyncEngine; + +class ModApiUtil : public ModApiBase +{ +private: + /* + NOTE: + The functions in this module are available in the in-game API + as well as in the mainmenu API. + + All functions that don't require either a Server or + GUIEngine instance should be in here. + */ + + // log([level,] text) + // Writes a line to the logger. + // The one-argument version logs to LL_NONE. + // The two-argument version accepts a log level. + static int l_log(lua_State *L); + + // get us precision time + static int l_get_us_time(lua_State *L); + + // parse_json(str[, nullvalue]) + static int l_parse_json(lua_State *L); + + // write_json(data[, styled]) + static int l_write_json(lua_State *L); + + // get_dig_params(groups, tool_capabilities[, time_from_last_punch]) + static int l_get_dig_params(lua_State *L); + + // get_hit_params(groups, tool_capabilities[, time_from_last_punch]) + static int l_get_hit_params(lua_State *L); + + // check_password_entry(name, entry, password) + static int l_check_password_entry(lua_State *L); + + // get_password_hash(name, raw_password) + static int l_get_password_hash(lua_State *L); + + // is_yes(arg) + static int l_is_yes(lua_State *L); + + // is_nan(arg) + static int l_is_nan(lua_State *L); + + // get_builtin_path() + static int l_get_builtin_path(lua_State *L); + + // compress(data, method, ...) + static int l_compress(lua_State *L); + + // decompress(data, method, ...) + static int l_decompress(lua_State *L); + + // mkdir(path) + static int l_mkdir(lua_State *L); + + // get_dir_list(path, is_dir) + static int l_get_dir_list(lua_State *L); + + // safe_file_write(path, content) + static int l_safe_file_write(lua_State *L); + + // request_insecure_environment() + static int l_request_insecure_environment(lua_State *L); + + // encode_base64(string) + static int l_encode_base64(lua_State *L); + + // decode_base64(string) + static int l_decode_base64(lua_State *L); + + // get_version() + static int l_get_version(lua_State *L); + + // sha1(string, raw) + static int l_sha1(lua_State *L); + +public: + static void Initialize(lua_State *L, int top); + static void InitializeAsync(lua_State *L, int top); + static void InitializeClient(lua_State *L, int top); + + static void InitializeAsync(AsyncEngine &engine); +}; diff --git a/src/script/lua_api/l_vmanip.cpp b/src/script/lua_api/l_vmanip.cpp new file mode 100644 index 000000000..ba5e4bcb3 --- /dev/null +++ b/src/script/lua_api/l_vmanip.cpp @@ -0,0 +1,479 @@ +/* +Minetest +Copyright (C) 2013 kwolekr, Ryan Kwolek + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#include "lua_api/l_vmanip.h" +#include "lua_api/l_internal.h" +#include "common/c_content.h" +#include "common/c_converter.h" +#include "emerge.h" +#include "environment.h" +#include "map.h" +#include "mapblock.h" +#include "server.h" +#include "mapgen/mapgen.h" +#include "voxelalgorithms.h" + +// garbage collector +int LuaVoxelManip::gc_object(lua_State *L) +{ + LuaVoxelManip *o = *(LuaVoxelManip **)(lua_touserdata(L, 1)); + delete o; + + return 0; +} + +int LuaVoxelManip::l_read_from_map(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkobject(L, 1); + MMVManip *vm = o->vm; + + v3s16 bp1 = getNodeBlockPos(check_v3s16(L, 2)); + v3s16 bp2 = getNodeBlockPos(check_v3s16(L, 3)); + sortBoxVerticies(bp1, bp2); + + vm->initialEmerge(bp1, bp2); + + push_v3s16(L, vm->m_area.MinEdge); + push_v3s16(L, vm->m_area.MaxEdge); + + return 2; +} + +int LuaVoxelManip::l_get_data(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkobject(L, 1); + bool use_buffer = lua_istable(L, 2); + + MMVManip *vm = o->vm; + + u32 volume = vm->m_area.getVolume(); + + if (use_buffer) + lua_pushvalue(L, 2); + else + lua_createtable(L, volume, 0); + + for (u32 i = 0; i != volume; i++) { + lua_Integer cid = vm->m_data[i].getContent(); + lua_pushinteger(L, cid); + lua_rawseti(L, -2, i + 1); + } + + return 1; +} + +int LuaVoxelManip::l_set_data(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkobject(L, 1); + MMVManip *vm = o->vm; + + if (!lua_istable(L, 2)) + throw LuaError("VoxelManip:set_data called with missing parameter"); + + u32 volume = vm->m_area.getVolume(); + for (u32 i = 0; i != volume; i++) { + lua_rawgeti(L, 2, i + 1); + content_t c = lua_tointeger(L, -1); + + vm->m_data[i].setContent(c); + + lua_pop(L, 1); + } + + return 0; +} + +int LuaVoxelManip::l_write_to_map(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkobject(L, 1); + bool update_light = !lua_isboolean(L, 2) || readParam(L, 2); + GET_ENV_PTR; + ServerMap *map = &(env->getServerMap()); + if (o->is_mapgen_vm || !update_light) { + o->vm->blitBackAll(&(o->modified_blocks)); + } else { + voxalgo::blit_back_with_light(map, o->vm, + &(o->modified_blocks)); + } + + MapEditEvent event; + event.type = MEET_OTHER; + for (const auto &modified_block : o->modified_blocks) + event.modified_blocks.insert(modified_block.first); + + map->dispatchEvent(event); + + o->modified_blocks.clear(); + return 0; +} + +int LuaVoxelManip::l_get_node_at(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + const NodeDefManager *ndef = getServer(L)->getNodeDefManager(); + + LuaVoxelManip *o = checkobject(L, 1); + v3s16 pos = check_v3s16(L, 2); + + pushnode(L, o->vm->getNodeNoExNoEmerge(pos), ndef); + return 1; +} + +int LuaVoxelManip::l_set_node_at(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + const NodeDefManager *ndef = getServer(L)->getNodeDefManager(); + + LuaVoxelManip *o = checkobject(L, 1); + v3s16 pos = check_v3s16(L, 2); + MapNode n = readnode(L, 3, ndef); + + o->vm->setNodeNoEmerge(pos, n); + + return 0; +} + +int LuaVoxelManip::l_update_liquids(lua_State *L) +{ + GET_ENV_PTR; + + LuaVoxelManip *o = checkobject(L, 1); + + Map *map = &(env->getMap()); + const NodeDefManager *ndef = getServer(L)->getNodeDefManager(); + MMVManip *vm = o->vm; + + Mapgen mg; + mg.vm = vm; + mg.ndef = ndef; + + mg.updateLiquid(&map->m_transforming_liquid, + vm->m_area.MinEdge, vm->m_area.MaxEdge); + + return 0; +} + +int LuaVoxelManip::l_calc_lighting(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkobject(L, 1); + if (!o->is_mapgen_vm) { + warningstream << "VoxelManip:calc_lighting called for a non-mapgen " + "VoxelManip object" << std::endl; + return 0; + } + + const NodeDefManager *ndef = getServer(L)->getNodeDefManager(); + EmergeManager *emerge = getServer(L)->getEmergeManager(); + MMVManip *vm = o->vm; + + v3s16 yblock = v3s16(0, 1, 0) * MAP_BLOCKSIZE; + v3s16 fpmin = vm->m_area.MinEdge; + v3s16 fpmax = vm->m_area.MaxEdge; + v3s16 pmin = lua_istable(L, 2) ? check_v3s16(L, 2) : fpmin + yblock; + v3s16 pmax = lua_istable(L, 3) ? check_v3s16(L, 3) : fpmax - yblock; + bool propagate_shadow = !lua_isboolean(L, 4) || readParam(L, 4); + + sortBoxVerticies(pmin, pmax); + if (!vm->m_area.contains(VoxelArea(pmin, pmax))) + throw LuaError("Specified voxel area out of VoxelManipulator bounds"); + + Mapgen mg; + mg.vm = vm; + mg.ndef = ndef; + mg.water_level = emerge->mgparams->water_level; + + mg.calcLighting(pmin, pmax, fpmin, fpmax, propagate_shadow); + + return 0; +} + +int LuaVoxelManip::l_set_lighting(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkobject(L, 1); + if (!o->is_mapgen_vm) { + warningstream << "VoxelManip:set_lighting called for a non-mapgen " + "VoxelManip object" << std::endl; + return 0; + } + + if (!lua_istable(L, 2)) + throw LuaError("VoxelManip:set_lighting called with missing parameter"); + + u8 light; + light = (getintfield_default(L, 2, "day", 0) & 0x0F); + light |= (getintfield_default(L, 2, "night", 0) & 0x0F) << 4; + + MMVManip *vm = o->vm; + + v3s16 yblock = v3s16(0, 1, 0) * MAP_BLOCKSIZE; + v3s16 pmin = lua_istable(L, 3) ? check_v3s16(L, 3) : vm->m_area.MinEdge + yblock; + v3s16 pmax = lua_istable(L, 4) ? check_v3s16(L, 4) : vm->m_area.MaxEdge - yblock; + + sortBoxVerticies(pmin, pmax); + if (!vm->m_area.contains(VoxelArea(pmin, pmax))) + throw LuaError("Specified voxel area out of VoxelManipulator bounds"); + + Mapgen mg; + mg.vm = vm; + + mg.setLighting(light, pmin, pmax); + + return 0; +} + +int LuaVoxelManip::l_get_light_data(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkobject(L, 1); + MMVManip *vm = o->vm; + + u32 volume = vm->m_area.getVolume(); + + lua_createtable(L, volume, 0); + for (u32 i = 0; i != volume; i++) { + lua_Integer light = vm->m_data[i].param1; + lua_pushinteger(L, light); + lua_rawseti(L, -2, i + 1); + } + + return 1; +} + +int LuaVoxelManip::l_set_light_data(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkobject(L, 1); + MMVManip *vm = o->vm; + + if (!lua_istable(L, 2)) + throw LuaError("VoxelManip:set_light_data called with missing " + "parameter"); + + u32 volume = vm->m_area.getVolume(); + for (u32 i = 0; i != volume; i++) { + lua_rawgeti(L, 2, i + 1); + u8 light = lua_tointeger(L, -1); + + vm->m_data[i].param1 = light; + + lua_pop(L, 1); + } + + return 0; +} + +int LuaVoxelManip::l_get_param2_data(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkobject(L, 1); + bool use_buffer = lua_istable(L, 2); + + MMVManip *vm = o->vm; + + u32 volume = vm->m_area.getVolume(); + + if (use_buffer) + lua_pushvalue(L, 2); + else + lua_createtable(L, volume, 0); + + for (u32 i = 0; i != volume; i++) { + lua_Integer param2 = vm->m_data[i].param2; + lua_pushinteger(L, param2); + lua_rawseti(L, -2, i + 1); + } + + return 1; +} + +int LuaVoxelManip::l_set_param2_data(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkobject(L, 1); + MMVManip *vm = o->vm; + + if (!lua_istable(L, 2)) + throw LuaError("VoxelManip:set_param2_data called with missing " + "parameter"); + + u32 volume = vm->m_area.getVolume(); + for (u32 i = 0; i != volume; i++) { + lua_rawgeti(L, 2, i + 1); + u8 param2 = lua_tointeger(L, -1); + + vm->m_data[i].param2 = param2; + + lua_pop(L, 1); + } + + return 0; +} + +int LuaVoxelManip::l_update_map(lua_State *L) +{ + return 0; +} + +int LuaVoxelManip::l_was_modified(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkobject(L, 1); + MMVManip *vm = o->vm; + + lua_pushboolean(L, vm->m_is_dirty); + + return 1; +} + +int LuaVoxelManip::l_get_emerged_area(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkobject(L, 1); + + push_v3s16(L, o->vm->m_area.MinEdge); + push_v3s16(L, o->vm->m_area.MaxEdge); + + return 2; +} + +LuaVoxelManip::LuaVoxelManip(MMVManip *mmvm, bool is_mg_vm) : + is_mapgen_vm(is_mg_vm), + vm(mmvm) +{ +} + +LuaVoxelManip::LuaVoxelManip(Map *map) : vm(new MMVManip(map)) +{ +} + +LuaVoxelManip::LuaVoxelManip(Map *map, v3s16 p1, v3s16 p2) +{ + vm = new MMVManip(map); + + v3s16 bp1 = getNodeBlockPos(p1); + v3s16 bp2 = getNodeBlockPos(p2); + sortBoxVerticies(bp1, bp2); + vm->initialEmerge(bp1, bp2); +} + +LuaVoxelManip::~LuaVoxelManip() +{ + if (!is_mapgen_vm) + delete vm; +} + +// LuaVoxelManip() +// Creates an LuaVoxelManip and leaves it on top of stack +int LuaVoxelManip::create_object(lua_State *L) +{ + GET_ENV_PTR; + + Map *map = &(env->getMap()); + LuaVoxelManip *o = (lua_istable(L, 1) && lua_istable(L, 2)) ? + new LuaVoxelManip(map, check_v3s16(L, 1), check_v3s16(L, 2)) : + new LuaVoxelManip(map); + + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + +LuaVoxelManip *LuaVoxelManip::checkobject(lua_State *L, int narg) +{ + NO_MAP_LOCK_REQUIRED; + + luaL_checktype(L, narg, LUA_TUSERDATA); + + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + + return *(LuaVoxelManip **)ud; // unbox pointer +} + +void LuaVoxelManip::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Can be created from Lua (VoxelManip()) + lua_register(L, className, create_object); +} + +const char LuaVoxelManip::className[] = "VoxelManip"; +const luaL_Reg LuaVoxelManip::methods[] = { + luamethod(LuaVoxelManip, read_from_map), + luamethod(LuaVoxelManip, get_data), + luamethod(LuaVoxelManip, set_data), + luamethod(LuaVoxelManip, get_node_at), + luamethod(LuaVoxelManip, set_node_at), + luamethod(LuaVoxelManip, write_to_map), + luamethod(LuaVoxelManip, update_map), + luamethod(LuaVoxelManip, update_liquids), + luamethod(LuaVoxelManip, calc_lighting), + luamethod(LuaVoxelManip, set_lighting), + luamethod(LuaVoxelManip, get_light_data), + luamethod(LuaVoxelManip, set_light_data), + luamethod(LuaVoxelManip, get_param2_data), + luamethod(LuaVoxelManip, set_param2_data), + luamethod(LuaVoxelManip, was_modified), + luamethod(LuaVoxelManip, get_emerged_area), + {0,0} +}; diff --git a/src/script/lua_api/l_vmanip.h b/src/script/lua_api/l_vmanip.h new file mode 100644 index 000000000..6e1aafa34 --- /dev/null +++ b/src/script/lua_api/l_vmanip.h @@ -0,0 +1,81 @@ +/* +Minetest +Copyright (C) 2013 kwolekr, Ryan Kwolek + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include "irr_v3d.h" +#include "lua_api/l_base.h" + +class Map; +class MapBlock; +class MMVManip; + +/* + VoxelManip + */ +class LuaVoxelManip : public ModApiBase +{ +private: + std::map modified_blocks; + bool is_mapgen_vm = false; + + static const char className[]; + static const luaL_Reg methods[]; + + static int gc_object(lua_State *L); + + static int l_read_from_map(lua_State *L); + static int l_get_data(lua_State *L); + static int l_set_data(lua_State *L); + static int l_write_to_map(lua_State *L); + + static int l_get_node_at(lua_State *L); + static int l_set_node_at(lua_State *L); + + static int l_update_map(lua_State *L); + static int l_update_liquids(lua_State *L); + + static int l_calc_lighting(lua_State *L); + static int l_set_lighting(lua_State *L); + static int l_get_light_data(lua_State *L); + static int l_set_light_data(lua_State *L); + + static int l_get_param2_data(lua_State *L); + static int l_set_param2_data(lua_State *L); + + static int l_was_modified(lua_State *L); + static int l_get_emerged_area(lua_State *L); + +public: + MMVManip *vm = nullptr; + + LuaVoxelManip(MMVManip *mmvm, bool is_mapgen_vm); + LuaVoxelManip(Map *map, v3s16 p1, v3s16 p2); + LuaVoxelManip(Map *map); + ~LuaVoxelManip(); + + // LuaVoxelManip() + // Creates a LuaVoxelManip and leaves it on top of stack + static int create_object(lua_State *L); + + static LuaVoxelManip *checkobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; diff --git a/src/script/scripting_client.cpp b/src/script/scripting_client.cpp new file mode 100644 index 000000000..206530ac9 --- /dev/null +++ b/src/script/scripting_client.cpp @@ -0,0 +1,111 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "scripting_client.h" +#include "client/client.h" +#include "client/game.h" +#include "cpp_api/s_internal.h" +#include "lua_api/l_client.h" +#include "lua_api/l_clientobject.h" +#include "lua_api/l_env.h" +#include "lua_api/l_inventoryaction.h" +#include "lua_api/l_item.h" +#include "lua_api/l_itemstackmeta.h" +#include "lua_api/l_minimap.h" +#include "lua_api/l_modchannels.h" +#include "lua_api/l_particles_local.h" +#include "lua_api/l_storage.h" +#include "lua_api/l_sound.h" +#include "lua_api/l_util.h" +#include "lua_api/l_item.h" +#include "lua_api/l_nodemeta.h" +#include "lua_api/l_localplayer.h" +#include "lua_api/l_camera.h" +#include "lua_api/l_settings.h" +#include "lua_api/l_http.h" +#include "lua_api/l_clientobject.h" +#include "lua_api/l_inventoryaction.h" + +ClientScripting::ClientScripting(Client *client): + ScriptApiBase(ScriptingType::Client) +{ + setGameDef(client); + setGame(g_game); + + SCRIPTAPI_PRECHECKHEADER + + // Security is mandatory client side + initializeSecurityClient(); + + lua_getglobal(L, "core"); + int top = lua_gettop(L); + + lua_newtable(L); + lua_setfield(L, -2, "ui"); + + InitializeModApi(L, top); + lua_pop(L, 1); + + // Push builtin initialization type + lua_pushstring(L, "client"); + lua_setglobal(L, "INIT"); + + infostream << "SCRIPTAPI: Initialized client game modules" << std::endl; +} + +void ClientScripting::InitializeModApi(lua_State *L, int top) +{ + LuaItemStack::Register(L); + ItemStackMetaRef::Register(L); + LuaRaycast::Register(L); + StorageRef::Register(L); + LuaMinimap::Register(L); + NodeMetaRef::RegisterClient(L); + LuaLocalPlayer::Register(L); + LuaCamera::Register(L); + ModChannelRef::Register(L); + LuaSettings::Register(L); + ClientObjectRef::Register(L); + LuaInventoryAction::Register(L); + + ModApiItemMod::Initialize(L, top); + ModApiUtil::InitializeClient(L, top); + ModApiHttp::Initialize(L, top); + ModApiClient::Initialize(L, top); + ModApiStorage::Initialize(L, top); + ModApiEnvMod::InitializeClient(L, top); + ModApiChannels::Initialize(L, top); + ModApiParticlesLocal::Initialize(L, top); +} + +void ClientScripting::on_client_ready(LocalPlayer *localplayer) +{ + LuaLocalPlayer::create(getStack(), localplayer); +} + +void ClientScripting::on_camera_ready(Camera *camera) +{ + LuaCamera::create(getStack(), camera); +} + +void ClientScripting::on_minimap_ready(Minimap *minimap) +{ + LuaMinimap::create(getStack(), minimap); +} diff --git a/src/script/scripting_client.h b/src/script/scripting_client.h new file mode 100644 index 000000000..c2752a966 --- /dev/null +++ b/src/script/scripting_client.h @@ -0,0 +1,49 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "cpp_api/s_base.h" +#include "cpp_api/s_client.h" +#include "cpp_api/s_cheats.h" +#include "cpp_api/s_modchannels.h" +#include "cpp_api/s_security.h" + +class Client; +class LocalPlayer; +class Camera; +class Minimap; + +class ClientScripting: + virtual public ScriptApiBase, + public ScriptApiSecurity, + public ScriptApiClient, + public ScriptApiModChannels, + public ScriptApiCheats +{ +public: + ClientScripting(Client *client); + void on_client_ready(LocalPlayer *localplayer); + void on_camera_ready(Camera *camera); + void on_minimap_ready(Minimap *minimap); + +private: + virtual void InitializeModApi(lua_State *L, int top); +}; diff --git a/src/script/scripting_mainmenu.cpp b/src/script/scripting_mainmenu.cpp new file mode 100644 index 000000000..6a7b20fd9 --- /dev/null +++ b/src/script/scripting_mainmenu.cpp @@ -0,0 +1,101 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "scripting_mainmenu.h" +#include "content/mods.h" +#include "cpp_api/s_internal.h" +#include "lua_api/l_base.h" +#include "lua_api/l_http.h" +#include "lua_api/l_mainmenu.h" +#include "lua_api/l_sound.h" +#include "lua_api/l_util.h" +#include "lua_api/l_settings.h" +#include "log.h" + +extern "C" { +#include "lualib.h" +} + +#define MAINMENU_NUM_ASYNC_THREADS 4 + + +MainMenuScripting::MainMenuScripting(GUIEngine* guiengine): + ScriptApiBase(ScriptingType::MainMenu) +{ + setGuiEngine(guiengine); + + SCRIPTAPI_PRECHECKHEADER + + lua_getglobal(L, "core"); + int top = lua_gettop(L); + + lua_newtable(L); + lua_setglobal(L, "gamedata"); + + // Initialize our lua_api modules + initializeModApi(L, top); + lua_pop(L, 1); + + // Push builtin initialization type + lua_pushstring(L, "mainmenu"); + lua_setglobal(L, "INIT"); + + infostream << "SCRIPTAPI: Initialized main menu modules" << std::endl; +} + +/******************************************************************************/ +void MainMenuScripting::initializeModApi(lua_State *L, int top) +{ + registerLuaClasses(L, top); + + // Initialize mod API modules + ModApiMainMenu::Initialize(L, top); + ModApiUtil::Initialize(L, top); + ModApiSound::Initialize(L, top); + ModApiHttp::Initialize(L, top); + + asyncEngine.registerStateInitializer(registerLuaClasses); + asyncEngine.registerStateInitializer(ModApiMainMenu::InitializeAsync); + asyncEngine.registerStateInitializer(ModApiUtil::InitializeAsync); + asyncEngine.registerStateInitializer(ModApiHttp::InitializeAsync); + + // Initialize async environment + //TODO possibly make number of async threads configurable + asyncEngine.initialize(MAINMENU_NUM_ASYNC_THREADS); +} + +/******************************************************************************/ +void MainMenuScripting::registerLuaClasses(lua_State *L, int top) +{ + LuaSettings::Register(L); +} + +/******************************************************************************/ +void MainMenuScripting::step() +{ + asyncEngine.step(getStack()); +} + +/******************************************************************************/ +unsigned int MainMenuScripting::queueAsync(const std::string &serialized_func, + const std::string &serialized_param) +{ + return asyncEngine.queueAsyncJob(serialized_func, serialized_param); +} + diff --git a/src/script/scripting_mainmenu.h b/src/script/scripting_mainmenu.h new file mode 100644 index 000000000..c6a5ad1b5 --- /dev/null +++ b/src/script/scripting_mainmenu.h @@ -0,0 +1,48 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "cpp_api/s_base.h" +#include "cpp_api/s_mainmenu.h" +#include "cpp_api/s_async.h" + +/*****************************************************************************/ +/* Scripting <-> Main Menu Interface */ +/*****************************************************************************/ + +class MainMenuScripting + : virtual public ScriptApiBase, + public ScriptApiMainMenu +{ +public: + MainMenuScripting(GUIEngine* guiengine); + + // Global step handler to pass back async events + void step(); + + // Pass async events from engine to async threads + unsigned int queueAsync(const std::string &serialized_func, + const std::string &serialized_params); +private: + void initializeModApi(lua_State *L, int top); + static void registerLuaClasses(lua_State *L, int top); + + AsyncEngine asyncEngine; +}; diff --git a/src/script/scripting_server.cpp b/src/script/scripting_server.cpp new file mode 100644 index 000000000..d83c44392 --- /dev/null +++ b/src/script/scripting_server.cpp @@ -0,0 +1,127 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "scripting_server.h" +#include "server.h" +#include "log.h" +#include "settings.h" +#include "cpp_api/s_internal.h" +#include "lua_api/l_areastore.h" +#include "lua_api/l_auth.h" +#include "lua_api/l_base.h" +#include "lua_api/l_craft.h" +#include "lua_api/l_env.h" +#include "lua_api/l_inventory.h" +#include "lua_api/l_item.h" +#include "lua_api/l_itemstackmeta.h" +#include "lua_api/l_mapgen.h" +#include "lua_api/l_modchannels.h" +#include "lua_api/l_nodemeta.h" +#include "lua_api/l_nodetimer.h" +#include "lua_api/l_noise.h" +#include "lua_api/l_object.h" +#include "lua_api/l_playermeta.h" +#include "lua_api/l_particles.h" +#include "lua_api/l_rollback.h" +#include "lua_api/l_server.h" +#include "lua_api/l_util.h" +#include "lua_api/l_vmanip.h" +#include "lua_api/l_settings.h" +#include "lua_api/l_http.h" +#include "lua_api/l_storage.h" + +extern "C" { +#include "lualib.h" +} + +ServerScripting::ServerScripting(Server* server): + ScriptApiBase(ScriptingType::Server) +{ + setGameDef(server); + + // setEnv(env) is called by ScriptApiEnv::initializeEnvironment() + // once the environment has been created + + SCRIPTAPI_PRECHECKHEADER + + if (g_settings->getBool("secure.enable_security")) { + initializeSecurity(); + } else { + warningstream << "\\!/ Mod security should never be disabled, as it allows any mod to " + << "access the host machine." + << "Mods should use minetest.request_insecure_environment() instead \\!/" << std::endl; + } + + lua_getglobal(L, "core"); + int top = lua_gettop(L); + + lua_newtable(L); + lua_setfield(L, -2, "object_refs"); + + lua_newtable(L); + lua_setfield(L, -2, "luaentities"); + + // Initialize our lua_api modules + InitializeModApi(L, top); + lua_pop(L, 1); + + // Push builtin initialization type + lua_pushstring(L, "game"); + lua_setglobal(L, "INIT"); + + infostream << "SCRIPTAPI: Initialized game modules" << std::endl; +} + +void ServerScripting::InitializeModApi(lua_State *L, int top) +{ + // Register reference classes (userdata) + InvRef::Register(L); + ItemStackMetaRef::Register(L); + LuaAreaStore::Register(L); + LuaItemStack::Register(L); + LuaPerlinNoise::Register(L); + LuaPerlinNoiseMap::Register(L); + LuaPseudoRandom::Register(L); + LuaPcgRandom::Register(L); + LuaRaycast::Register(L); + LuaSecureRandom::Register(L); + LuaVoxelManip::Register(L); + NodeMetaRef::Register(L); + NodeTimerRef::Register(L); + ObjectRef::Register(L); + PlayerMetaRef::Register(L); + LuaSettings::Register(L); + StorageRef::Register(L); + ModChannelRef::Register(L); + + // Initialize mod api modules + ModApiAuth::Initialize(L, top); + ModApiCraft::Initialize(L, top); + ModApiEnvMod::Initialize(L, top); + ModApiInventory::Initialize(L, top); + ModApiItemMod::Initialize(L, top); + ModApiMapgen::Initialize(L, top); + ModApiParticles::Initialize(L, top); + ModApiRollback::Initialize(L, top); + ModApiServer::Initialize(L, top); + ModApiUtil::Initialize(L, top); + ModApiHttp::Initialize(L, top); + ModApiStorage::Initialize(L, top); + ModApiChannels::Initialize(L, top); +} diff --git a/src/script/scripting_server.h b/src/script/scripting_server.h new file mode 100644 index 000000000..248fcf42a --- /dev/null +++ b/src/script/scripting_server.h @@ -0,0 +1,53 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once +#include "cpp_api/s_base.h" +#include "cpp_api/s_entity.h" +#include "cpp_api/s_env.h" +#include "cpp_api/s_inventory.h" +#include "cpp_api/s_modchannels.h" +#include "cpp_api/s_node.h" +#include "cpp_api/s_player.h" +#include "cpp_api/s_server.h" +#include "cpp_api/s_security.h" + +/*****************************************************************************/ +/* Scripting <-> Server Game Interface */ +/*****************************************************************************/ + +class ServerScripting: + virtual public ScriptApiBase, + public ScriptApiDetached, + public ScriptApiEntity, + public ScriptApiEnv, + public ScriptApiModChannels, + public ScriptApiNode, + public ScriptApiPlayer, + public ScriptApiServer, + public ScriptApiSecurity +{ +public: + ServerScripting(Server* server); + + // use ScriptApiBase::loadMod() to load mods + +private: + void InitializeModApi(lua_State *L, int top); +};