diff options
Diffstat (limited to 'src/script')
53 files changed, 1745 insertions, 597 deletions
diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 3754fc2ff..06e20c2a0 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -76,7 +76,7 @@ ItemDefinition read_item_definition(lua_State* L,int index, getboolfield(L, index, "liquids_pointable", def.liquids_pointable); warn_if_field_exists(L, index, "tool_digging_properties", - "deprecated: use tool_capabilities"); + "Deprecated; use tool_capabilities"); lua_getfield(L, index, "tool_capabilities"); if(lua_istable(L, -1)){ @@ -100,6 +100,9 @@ ItemDefinition read_item_definition(lua_State* L,int index, lua_getfield(L, -1, "place"); read_soundspec(L, -1, def.sound_place); lua_pop(L, 1); + lua_getfield(L, -1, "place_failed"); + read_soundspec(L, -1, def.sound_place_failed); + lua_pop(L, 1); } lua_pop(L, 1); @@ -197,6 +200,23 @@ void read_object_properties(lua_State *L, int index, prop->automatic_face_movement_dir_offset = 0.0; } lua_pop(L, 1); + getboolfield(L, -1, "backface_culling", prop->backface_culling); + + getstringfield(L, -1, "nametag", prop->nametag); + lua_getfield(L, -1, "nametag_color"); + if (!lua_isnil(L, -1)) { + video::SColor color = prop->nametag_color; + if (read_color(L, -1, &color)) + prop->nametag_color = color; + } + lua_pop(L, 1); + + lua_getfield(L, -1, "automatic_face_movement_max_rotation_per_sec"); + if (lua_isnumber(L, -1)) { + prop->automatic_face_movement_max_rotation_per_sec = luaL_checknumber(L, -1); + } + lua_pop(L, 1); + getstringfield(L, -1, "infotext", prop->infotext); } /******************************************************************************/ @@ -255,6 +275,16 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) else lua_pushboolean(L, false); lua_setfield(L, -2, "automatic_face_movement_dir"); + lua_pushboolean(L, prop->backface_culling); + lua_setfield(L, -2, "backface_culling"); + lua_pushlstring(L, prop->nametag.c_str(), prop->nametag.size()); + lua_setfield(L, -2, "nametag"); + push_ARGB8(L, prop->nametag_color); + lua_setfield(L, -2, "nametag_color"); + lua_pushnumber(L, prop->automatic_face_movement_max_rotation_per_sec); + lua_setfield(L, -2, "automatic_face_movement_max_rotation_per_sec"); + lua_pushlstring(L, prop->infotext.c_str(), prop->infotext.size()); + lua_setfield(L, -2, "infotext"); } /******************************************************************************/ @@ -264,14 +294,31 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype) index = lua_gettop(L) + 1 + index; TileDef tiledef; - bool default_tiling = (drawtype == NDT_PLANTLIKE || drawtype == NDT_FIRELIKE) - ? false : true; + + bool default_tiling = true; + bool default_culling = true; + switch (drawtype) { + case NDT_PLANTLIKE: + case NDT_FIRELIKE: + default_tiling = false; + // "break" is omitted here intentionaly, as PLANTLIKE + // FIRELIKE drawtype both should default to having + // backface_culling to false. + case NDT_MESH: + case NDT_LIQUID: + default_culling = false; + break; + default: + break; + } + // key at index -2 and value at index if(lua_isstring(L, index)){ // "default_lava.png" tiledef.name = lua_tostring(L, index); tiledef.tileable_vertical = default_tiling; tiledef.tileable_horizontal = default_tiling; + tiledef.backface_culling = default_culling; } else if(lua_istable(L, index)) { @@ -280,7 +327,7 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype) getstringfield(L, index, "name", tiledef.name); getstringfield(L, index, "image", tiledef.name); // MaterialSpec compat. tiledef.backface_culling = getboolfield_default( - L, index, "backface_culling", true); + L, index, "backface_culling", default_culling); tiledef.tileable_horizontal = getboolfield_default( L, index, "tileable_horizontal", default_tiling); tiledef.tileable_vertical = getboolfield_default( @@ -427,17 +474,17 @@ ContentFeatures read_content_features(lua_State *L, int index) // Warn about some deprecated fields warn_if_field_exists(L, index, "wall_mounted", - "deprecated: use paramtype2 = 'wallmounted'"); + "Deprecated; use paramtype2 = 'wallmounted'"); warn_if_field_exists(L, index, "light_propagates", - "deprecated: determined from paramtype"); + "Deprecated; determined from paramtype"); warn_if_field_exists(L, index, "dug_item", - "deprecated: use 'drop' field"); + "Deprecated; use 'drop' field"); warn_if_field_exists(L, index, "extra_dug_item", - "deprecated: use 'drop' field"); + "Deprecated; use 'drop' field"); warn_if_field_exists(L, index, "extra_dug_item_rarity", - "deprecated: use 'drop' field"); + "Deprecated; use 'drop' field"); warn_if_field_exists(L, index, "metadata_name", - "deprecated: use on_add and metadata callbacks"); + "Deprecated; use on_add and metadata callbacks"); // True for all ground-like things like stone and mud, false for eg. trees getboolfield(L, index, "is_ground_content", f.is_ground_content); @@ -454,6 +501,8 @@ ContentFeatures read_content_features(lua_State *L, int index) getboolfield(L, index, "climbable", f.climbable); // Player can build on these getboolfield(L, index, "buildable_to", f.buildable_to); + // Liquids flow into and replace node + getboolfield(L, index, "floodable", f.floodable); // Whether the node is non-liquid, source liquid or flowing liquid f.liquid_type = (LiquidType)getenumfield(L, index, "liquidtype", ScriptApiNode::es_LiquidType, LIQUID_NONE); @@ -486,6 +535,46 @@ ContentFeatures read_content_features(lua_State *L, int index) f.node_box = read_nodebox(L, -1); lua_pop(L, 1); + lua_getfield(L, index, "connects_to"); + if (lua_istable(L, -1)) { + int table = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, table) != 0) { + // Value at -1 + f.connects_to.push_back(lua_tostring(L, -1)); + lua_pop(L, 1); + } + } + lua_pop(L, 1); + + lua_getfield(L, index, "connect_sides"); + if (lua_istable(L, -1)) { + int table = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, table) != 0) { + // Value at -1 + std::string side(lua_tostring(L, -1)); + // Note faces are flipped to make checking easier + if (side == "top") + f.connect_sides |= 2; + else if (side == "bottom") + f.connect_sides |= 1; + else if (side == "front") + f.connect_sides |= 16; + else if (side == "left") + f.connect_sides |= 32; + else if (side == "back") + f.connect_sides |= 4; + else if (side == "right") + f.connect_sides |= 8; + else + warningstream << "Unknown value for \"connect_sides\": " + << side << std::endl; + lua_pop(L, 1); + } + } + lua_pop(L, 1); + lua_getfield(L, index, "selection_box"); if(lua_istable(L, -1)) f.selection_box = read_nodebox(L, -1); @@ -578,25 +667,31 @@ NodeBox read_nodebox(lua_State *L, int index) nodebox.type = (NodeBoxType)getenumfield(L, index, "type", ScriptApiNode::es_NodeBoxType, NODEBOX_REGULAR); - lua_getfield(L, index, "fixed"); - if(lua_istable(L, -1)) - nodebox.fixed = read_aabb3f_vector(L, -1, BS); - lua_pop(L, 1); - - lua_getfield(L, index, "wall_top"); - if(lua_istable(L, -1)) - nodebox.wall_top = read_aabb3f(L, -1, BS); - lua_pop(L, 1); - - lua_getfield(L, index, "wall_bottom"); - if(lua_istable(L, -1)) - nodebox.wall_bottom = read_aabb3f(L, -1, BS); - lua_pop(L, 1); - - lua_getfield(L, index, "wall_side"); - if(lua_istable(L, -1)) - nodebox.wall_side = read_aabb3f(L, -1, BS); - lua_pop(L, 1); +#define NODEBOXREAD(n, s) \ + do { \ + lua_getfield(L, index, (s)); \ + if (lua_istable(L, -1)) \ + (n) = read_aabb3f(L, -1, BS); \ + lua_pop(L, 1); \ + } while (0) + +#define NODEBOXREADVEC(n, s) \ + do { \ + lua_getfield(L, index, (s)); \ + if (lua_istable(L, -1)) \ + (n) = read_aabb3f_vector(L, -1, BS); \ + lua_pop(L, 1); \ + } while (0) + NODEBOXREADVEC(nodebox.fixed, "fixed"); + NODEBOXREAD(nodebox.wall_top, "wall_top"); + NODEBOXREAD(nodebox.wall_bottom, "wall_bottom"); + NODEBOXREAD(nodebox.wall_side, "wall_side"); + NODEBOXREADVEC(nodebox.connect_top, "connect_top"); + NODEBOXREADVEC(nodebox.connect_bottom, "connect_bottom"); + NODEBOXREADVEC(nodebox.connect_front, "connect_front"); + NODEBOXREADVEC(nodebox.connect_left, "connect_left"); + NODEBOXREADVEC(nodebox.connect_back, "connect_back"); + NODEBOXREADVEC(nodebox.connect_right, "connect_right"); } return nodebox; } @@ -639,14 +734,13 @@ void pushnode(lua_State *L, const MapNode &n, INodeDefManager *ndef) /******************************************************************************/ void warn_if_field_exists(lua_State *L, int table, - const char *fieldname, const std::string &message) + const char *name, const std::string &message) { - lua_getfield(L, table, fieldname); - if(!lua_isnil(L, -1)){ -//TODO find way to access backtrace fct from here - // infostream<<script_get_backtrace(L)<<std::endl; - infostream<<"WARNING: field \""<<fieldname<<"\": " - <<message<<std::endl; + lua_getfield(L, table, name); + if (!lua_isnil(L, -1)) { + warningstream << "Field \"" << name << "\": " + << message << std::endl; + infostream << script_get_backtrace(L) << std::endl; } lua_pop(L, 1); } @@ -705,7 +799,7 @@ ItemStack read_item(lua_State* L, int index,Server* srv) } catch(SerializationError &e) { - infostream<<"WARNING: unable to create item from itemstring" + warningstream<<"unable to create item from itemstring" <<": "<<itemstring<<std::endl; return ItemStack(); } @@ -840,14 +934,14 @@ ToolCapabilities read_tool_capabilities( getintfield(L, table_groupcap, "uses", groupcap.uses); // DEPRECATED: maxwear float maxwear = 0; - if(getfloatfield(L, table_groupcap, "maxwear", maxwear)){ - if(maxwear != 0) + if (getfloatfield(L, table_groupcap, "maxwear", maxwear)){ + if (maxwear != 0) groupcap.uses = 1.0/maxwear; else groupcap.uses = 0; - infostream<<script_get_backtrace(L)<<std::endl; - infostream<<"WARNING: field \"maxwear\" is deprecated; " - <<"should replace with uses=1/maxwear"<<std::endl; + warningstream << "Field \"maxwear\" is deprecated; " + << "replace with uses=1/maxwear" << std::endl; + infostream << script_get_backtrace(L) << std::endl; } // Read "times" table lua_getfield(L, table_groupcap, "times"); @@ -1232,4 +1326,3 @@ void read_json_value(lua_State *L, Json::Value &root, int index, u8 recursion) } lua_pop(L, 1); // Pop value } - diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp index f1d3cc421..55c4a5f5a 100644 --- a/src/script/common/c_converter.cpp +++ b/src/script/common/c_converter.cpp @@ -517,6 +517,15 @@ bool getboolfield_default(lua_State *L, int table, return result; } +void setstringfield(lua_State *L, int table, + const char *fieldname, const char *value) +{ + lua_pushstring(L, value); + if(table < 0) + table -= 1; + lua_setfield(L, table, fieldname); +} + void setintfield(lua_State *L, int table, const char *fieldname, int value) { diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h index 18a045d2a..eefac0ed7 100644 --- a/src/script/common/c_converter.h +++ b/src/script/common/c_converter.h @@ -69,6 +69,8 @@ bool getfloatfield(lua_State *L, int table, std::string checkstringfield(lua_State *L, int table, const char *fieldname); +void setstringfield(lua_State *L, int table, + const char *fieldname, const char *value); void setintfield(lua_State *L, int table, const char *fieldname, int value); void setfloatfield(lua_State *L, int table, diff --git a/src/script/common/c_internal.cpp b/src/script/common/c_internal.cpp index 2a10ce0f2..b349f9dd1 100644 --- a/src/script/common/c_internal.cpp +++ b/src/script/common/c_internal.cpp @@ -136,61 +136,56 @@ void script_run_callbacks_f(lua_State *L, int nargs, FATAL_ERROR_IF(lua_gettop(L) < nargs + 1, "Not enough arguments"); // Insert error handler - lua_pushcfunction(L, script_error_handler); - int errorhandler = lua_gettop(L) - nargs - 1; - lua_insert(L, errorhandler); + PUSH_ERROR_HANDLER(L); + int error_handler = lua_gettop(L) - nargs - 1; + lua_insert(L, error_handler); // Insert run_callbacks between error handler and table lua_getglobal(L, "core"); lua_getfield(L, -1, "run_callbacks"); lua_remove(L, -2); - lua_insert(L, errorhandler + 1); + lua_insert(L, error_handler + 1); // Insert mode after table lua_pushnumber(L, (int) mode); - lua_insert(L, errorhandler + 3); + lua_insert(L, error_handler + 3); // Stack now looks like this: // ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n> - int result = lua_pcall(L, nargs + 2, 1, errorhandler); + int result = lua_pcall(L, nargs + 2, 1, error_handler); if (result != 0) script_error(L, result, NULL, fxn); - lua_remove(L, -2); // Remove error handler + lua_remove(L, error_handler); } void log_deprecated(lua_State *L, const std::string &message) { static bool configured = false; - static bool dolog = false; - static bool doerror = false; + static bool do_log = false; + static bool do_error = false; - // performance optimization to not have to read and compare setting for every logline + // Only read settings on first call if (!configured) { std::string value = g_settings->get("deprecated_lua_api_handling"); if (value == "log") { - dolog = true; + do_log = true; } else if (value == "error") { - dolog = true; - doerror = true; + do_log = true; + do_error = true; } } - if (doerror) { - if (L != NULL) { - script_error(L, LUA_ERRRUN, NULL, NULL); - } else { - FATAL_ERROR("Can't do a scripterror for this deprecated message, " - "so exit completely!"); - } - } - - if (dolog) { - /* abusing actionstream because of lack of file-only-logged loglevel */ - actionstream << message << std::endl; - if (L != NULL) { - actionstream << script_get_backtrace(L) << std::endl; + if (do_log) { + warningstream << message << std::endl; + // L can be NULL if we get called by log_deprecated(const std::string &msg) + // from scripting_game.cpp. + if (L) { + if (do_error) + script_error(L, LUA_ERRRUN, NULL, NULL); + else + infostream << script_get_backtrace(L) << std::endl; } } } diff --git a/src/script/common/c_internal.h b/src/script/common/c_internal.h index ecb514c8f..fc59b0e2e 100644 --- a/src/script/common/c_internal.h +++ b/src/script/common/c_internal.h @@ -34,6 +34,31 @@ extern "C" { #include "common/c_types.h" + +/* + Define our custom indices into the Lua registry table. + + Lua 5.2 and above define the LUA_RIDX_LAST macro. Only numbers above that + may be used for custom indices, anything else is reserved. + + Lua 5.1 / LuaJIT do not use any numeric indices (only string indices), + so we can use numeric indices freely. +*/ +#ifdef LUA_RIDX_LAST +#define CUSTOM_RIDX_BASE ((LUA_RIDX_LAST)+1) +#else +#define CUSTOM_RIDX_BASE 1 +#endif + +#define CUSTOM_RIDX_SCRIPTAPI (CUSTOM_RIDX_BASE) +#define CUSTOM_RIDX_GLOBALS_BACKUP (CUSTOM_RIDX_BASE + 1) +#define CUSTOM_RIDX_CURRENT_MOD_NAME (CUSTOM_RIDX_BASE + 2) +#define CUSTOM_RIDX_ERROR_HANDLER (CUSTOM_RIDX_BASE + 3) + +// Pushes the error handler onto the stack and returns its index +#define PUSH_ERROR_HANDLER(L) \ + (lua_rawgeti((L), LUA_REGISTRYINDEX, CUSTOM_RIDX_ERROR_HANDLER), lua_gettop((L))) + #define PCALL_RESL(L, RES) do { \ int result_ = (RES); \ if (result_ != 0) { \ diff --git a/src/script/common/c_types.h b/src/script/common/c_types.h index 706470737..056f30251 100644 --- a/src/script/common/c_types.h +++ b/src/script/common/c_types.h @@ -52,10 +52,10 @@ public: } }; -class LuaError : public ServerError +class LuaError : public ModError { public: - LuaError(const std::string &s) : ServerError(s) {} + LuaError(const std::string &s) : ModError(s) {} }; diff --git a/src/script/cpp_api/s_async.cpp b/src/script/cpp_api/s_async.cpp index c00b22f98..9bf3fcf49 100644 --- a/src/script/cpp_api/s_async.cpp +++ b/src/script/cpp_api/s_async.cpp @@ -47,32 +47,31 @@ AsyncEngine::~AsyncEngine() // Request all threads to stop for (std::vector<AsyncWorkerThread *>::iterator it = workerThreads.begin(); it != workerThreads.end(); it++) { - (*it)->Stop(); + (*it)->stop(); } // Wake up all threads for (std::vector<AsyncWorkerThread *>::iterator it = workerThreads.begin(); it != workerThreads.end(); it++) { - jobQueueCounter.Post(); + jobQueueCounter.post(); } // Wait for threads to finish for (std::vector<AsyncWorkerThread *>::iterator it = workerThreads.begin(); it != workerThreads.end(); it++) { - (*it)->Wait(); + (*it)->wait(); } // Force kill all threads for (std::vector<AsyncWorkerThread *>::iterator it = workerThreads.begin(); it != workerThreads.end(); it++) { - (*it)->Kill(); delete *it; } - jobQueueMutex.Lock(); + jobQueueMutex.lock(); jobQueue.clear(); - jobQueueMutex.Unlock(); + jobQueueMutex.unlock(); workerThreads.clear(); } @@ -92,16 +91,17 @@ void AsyncEngine::initialize(unsigned int numEngines) initDone = true; for (unsigned int i = 0; i < numEngines; i++) { - AsyncWorkerThread *toAdd = new AsyncWorkerThread(this, i); + AsyncWorkerThread *toAdd = new AsyncWorkerThread(this, + std::string("AsyncWorker-") + itos(i)); workerThreads.push_back(toAdd); - toAdd->Start(); + toAdd->start(); } } /******************************************************************************/ unsigned int AsyncEngine::queueAsyncJob(std::string func, std::string params) { - jobQueueMutex.Lock(); + jobQueueMutex.lock(); LuaJobInfo toAdd; toAdd.id = jobIdCounter++; toAdd.serializedFunction = func; @@ -109,9 +109,9 @@ unsigned int AsyncEngine::queueAsyncJob(std::string func, std::string params) jobQueue.push_back(toAdd); - jobQueueCounter.Post(); + jobQueueCounter.post(); - jobQueueMutex.Unlock(); + jobQueueMutex.unlock(); return toAdd.id; } @@ -119,8 +119,8 @@ unsigned int AsyncEngine::queueAsyncJob(std::string func, std::string params) /******************************************************************************/ LuaJobInfo AsyncEngine::getJob() { - jobQueueCounter.Wait(); - jobQueueMutex.Lock(); + jobQueueCounter.wait(); + jobQueueMutex.lock(); LuaJobInfo retval; retval.valid = false; @@ -130,7 +130,7 @@ LuaJobInfo AsyncEngine::getJob() jobQueue.pop_front(); retval.valid = true; } - jobQueueMutex.Unlock(); + jobQueueMutex.unlock(); return retval; } @@ -138,16 +138,17 @@ LuaJobInfo AsyncEngine::getJob() /******************************************************************************/ void AsyncEngine::putJobResult(LuaJobInfo result) { - resultQueueMutex.Lock(); + resultQueueMutex.lock(); resultQueue.push_back(result); - resultQueueMutex.Unlock(); + resultQueueMutex.unlock(); } /******************************************************************************/ -void AsyncEngine::step(lua_State *L, int errorhandler) +void AsyncEngine::step(lua_State *L) { + int error_handler = PUSH_ERROR_HANDLER(L); lua_getglobal(L, "core"); - resultQueueMutex.Lock(); + resultQueueMutex.lock(); while (!resultQueue.empty()) { LuaJobInfo jobDone = resultQueue.front(); resultQueue.pop_front(); @@ -164,16 +165,16 @@ void AsyncEngine::step(lua_State *L, int errorhandler) lua_pushlstring(L, jobDone.serializedResult.data(), jobDone.serializedResult.size()); - PCALL_RESL(L, lua_pcall(L, 2, 0, errorhandler)); + PCALL_RESL(L, lua_pcall(L, 2, 0, error_handler)); } - resultQueueMutex.Unlock(); - lua_pop(L, 1); // Pop core + resultQueueMutex.unlock(); + lua_pop(L, 2); // Pop core and error handler } /******************************************************************************/ void AsyncEngine::pushFinishedJobs(lua_State* L) { // Result Table - resultQueueMutex.Lock(); + MutexAutoLock l(resultQueueMutex); unsigned int index = 1; lua_createtable(L, resultQueue.size(), 0); @@ -197,8 +198,6 @@ void AsyncEngine::pushFinishedJobs(lua_State* L) { lua_rawseti(L, top, index++); } - - resultQueueMutex.Unlock(); } /******************************************************************************/ @@ -214,10 +213,10 @@ void AsyncEngine::prepareEnvironment(lua_State* L, int top) /******************************************************************************/ AsyncWorkerThread::AsyncWorkerThread(AsyncEngine* jobDispatcher, - unsigned int threadNum) : + const std::string &name) : + Thread(name), ScriptApiBase(), - jobDispatcher(jobDispatcher), - threadnum(threadNum) + jobDispatcher(jobDispatcher) { lua_State *L = getStack(); @@ -235,50 +234,42 @@ AsyncWorkerThread::AsyncWorkerThread(AsyncEngine* jobDispatcher, /******************************************************************************/ AsyncWorkerThread::~AsyncWorkerThread() { - sanity_check(IsRunning() == false); + sanity_check(!isRunning()); } /******************************************************************************/ -void* AsyncWorkerThread::Thread() +void* AsyncWorkerThread::run() { - ThreadStarted(); - - // Register thread for error logging - char number[21]; - snprintf(number, sizeof(number), "%u", threadnum); - log_register_thread(std::string("AsyncWorkerThread_") + number); - - porting::setThreadName((std::string("AsyncWorkTh_") + number).c_str()); - lua_State *L = getStack(); std::string script = getServer()->getBuiltinLuaPath() + DIR_DELIM + "init.lua"; - if (!loadScript(script)) { - errorstream - << "AsyncWorkerThread execution of async base environment failed!" - << std::endl; - abort(); + try { + loadScript(script); + } catch (const ModError &e) { + errorstream << "Execution of async base environment failed: " + << e.what() << std::endl; + FATAL_ERROR("Execution of async base environment failed"); } + int error_handler = PUSH_ERROR_HANDLER(L); + lua_getglobal(L, "core"); if (lua_isnil(L, -1)) { - errorstream << "Unable to find core within async environment!"; - abort(); + FATAL_ERROR("Unable to find core within async environment!"); } // Main loop - while (!StopRequested()) { + while (!stopRequested()) { // Wait for job LuaJobInfo toProcess = jobDispatcher->getJob(); - if (toProcess.valid == false || StopRequested()) { + if (toProcess.valid == false || stopRequested()) { continue; } lua_getfield(L, -1, "job_processor"); if (lua_isnil(L, -1)) { - errorstream << "Unable to get async job processor!" << std::endl; - abort(); + FATAL_ERROR("Unable to get async job processor!"); } luaL_checktype(L, -1, LUA_TFUNCTION); @@ -291,7 +282,7 @@ void* AsyncWorkerThread::Thread() toProcess.serializedParams.data(), toProcess.serializedParams.size()); - int result = lua_pcall(L, 2, 1, m_errorhandler); + int result = lua_pcall(L, 2, 1, error_handler); if (result) { PCALL_RES(result); toProcess.serializedResult = ""; @@ -308,9 +299,7 @@ void* AsyncWorkerThread::Thread() jobDispatcher->putJobResult(toProcess); } - lua_pop(L, 1); // Pop core - - log_deregister_thread(); + lua_pop(L, 2); // Pop core and error handler return 0; } diff --git a/src/script/cpp_api/s_async.h b/src/script/cpp_api/s_async.h index a6459c18d..8d612d58c 100644 --- a/src/script/cpp_api/s_async.h +++ b/src/script/cpp_api/s_async.h @@ -24,9 +24,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <deque> #include <map> -#include "jthread/jthread.h" -#include "jthread/jmutex.h" -#include "jthread/jsemaphore.h" +#include "threading/thread.h" +#include "threading/mutex.h" +#include "threading/semaphore.h" #include "debug.h" #include "lua.h" #include "cpp_api/s_base.h" @@ -52,24 +52,15 @@ struct LuaJobInfo { }; // Asynchronous working environment -class AsyncWorkerThread : public JThread, public ScriptApiBase { +class AsyncWorkerThread : public Thread, public ScriptApiBase { public: - /** - * default constructor - * @param pointer to job dispatcher - */ - AsyncWorkerThread(AsyncEngine* jobDispatcher, unsigned int threadNum); - + AsyncWorkerThread(AsyncEngine* jobDispatcher, const std::string &name); virtual ~AsyncWorkerThread(); - void *Thread(); + void *run(); private: AsyncEngine *jobDispatcher; - - // Thread number. Used for debug output - unsigned int threadnum; - }; // Asynchornous thread and job management @@ -104,9 +95,8 @@ public: * Engine step to process finished jobs * the engine step is one way to pass events back, PushFinishedJobs another * @param L The Lua stack - * @param errorhandler Stack index of the Lua error handler */ - void step(lua_State *L, int errorhandler); + void step(lua_State *L); /** * Push a list of finished jobs onto the stack @@ -148,13 +138,13 @@ private: unsigned int jobIdCounter; // Mutex to protect job queue - JMutex jobQueueMutex; + Mutex jobQueueMutex; // Job queue std::deque<LuaJobInfo> jobQueue; // Mutex to protect result queue - JMutex resultQueueMutex; + Mutex resultQueueMutex; // Result queue std::deque<LuaJobInfo> resultQueue; @@ -162,7 +152,7 @@ private: std::vector<AsyncWorkerThread*> workerThreads; // Counter semaphore for job dispatching - JSemaphore jobQueueCounter; + Semaphore jobQueueCounter; }; #endif // CPP_API_ASYNC_EVENTS_HEADER diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index dcfbac4bf..679a517ee 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -52,13 +52,13 @@ public: { // Store current mod name in registry lua_pushstring(L, mod_name.c_str()); - lua_setfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); + lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); } ~ModNameStorer() { // Clear current mod name from registry lua_pushnil(L); - lua_setfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); + lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); } }; @@ -67,10 +67,11 @@ public: ScriptApiBase */ -ScriptApiBase::ScriptApiBase() +ScriptApiBase::ScriptApiBase() : + m_luastackmutex() { #ifdef SCRIPTAPI_LOCK_DEBUG - m_locked = false; + m_lock_recursion_count = 0; #endif m_luastack = luaL_newstate(); @@ -78,13 +79,13 @@ ScriptApiBase::ScriptApiBase() luaL_openlibs(m_luastack); - // Add and save an error handler - lua_pushcfunction(m_luastack, script_error_handler); - m_errorhandler = lua_gettop(m_luastack); - // Make the ScriptApiBase* accessible to ModApiBase lua_pushlightuserdata(m_luastack, this); - lua_setfield(m_luastack, LUA_REGISTRYINDEX, "scriptapi"); + lua_rawseti(m_luastack, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI); + + // Add and save an error handler + lua_pushcfunction(m_luastack, script_error_handler); + lua_rawseti(m_luastack, LUA_REGISTRYINDEX, CUSTOM_RIDX_ERROR_HANDLER); // If we are using LuaJIT add a C++ wrapper function to catch // exceptions thrown in Lua -> C++ calls @@ -119,40 +120,36 @@ ScriptApiBase::~ScriptApiBase() lua_close(m_luastack); } -bool ScriptApiBase::loadMod(const std::string &script_path, - const std::string &mod_name, std::string *error) +void ScriptApiBase::loadMod(const std::string &script_path, + const std::string &mod_name) { ModNameStorer mod_name_storer(getStack(), mod_name); - return loadScript(script_path, error); + loadScript(script_path); } -bool ScriptApiBase::loadScript(const std::string &script_path, std::string *error) +void ScriptApiBase::loadScript(const std::string &script_path) { verbosestream << "Loading and running script from " << script_path << std::endl; lua_State *L = getStack(); + int error_handler = PUSH_ERROR_HANDLER(L); + bool ok; if (m_secure) { ok = ScriptApiSecurity::safeLoadFile(L, script_path.c_str()); } else { ok = !luaL_loadfile(L, script_path.c_str()); } - ok = ok && !lua_pcall(L, 0, 0, m_errorhandler); + ok = ok && !lua_pcall(L, 0, 0, error_handler); if (!ok) { std::string error_msg = lua_tostring(L, -1); - if (error) - *error = error_msg; - errorstream << "========== ERROR FROM LUA ===========" << std::endl - << "Failed to load and run script from " << std::endl - << script_path << ":" << std::endl << std::endl - << error_msg << std::endl << std::endl - << "======= END OF ERROR FROM LUA ========" << std::endl; - lua_pop(L, 1); // Pop error message from stack - return false; + lua_pop(L, 2); // Pop error message and error handler + throw ModError("Failed to load and run script from " + + script_path + ":\n" + error_msg); } - return true; + lua_pop(L, 1); // Pop error handler } // Push the list of callbacks (a lua table). @@ -161,35 +158,40 @@ bool ScriptApiBase::loadScript(const std::string &script_path, std::string *erro // - runs the callbacks // - replaces the table and arguments with the return value, // computed depending on mode +// This function must only be called with scriptlock held (i.e. inside of a +// code block with SCRIPTAPI_PRECHECKHEADER declared) void ScriptApiBase::runCallbacksRaw(int nargs, RunCallbacksMode mode, const char *fxn) { +#ifdef SCRIPTAPI_LOCK_DEBUG + assert(m_lock_recursion_count > 0); +#endif lua_State *L = getStack(); FATAL_ERROR_IF(lua_gettop(L) < nargs + 1, "Not enough arguments"); // Insert error handler - lua_pushcfunction(L, script_error_handler); - int errorhandler = lua_gettop(L) - nargs - 1; - lua_insert(L, errorhandler); + PUSH_ERROR_HANDLER(L); + int error_handler = lua_gettop(L) - nargs - 1; + lua_insert(L, error_handler); // Insert run_callbacks between error handler and table lua_getglobal(L, "core"); lua_getfield(L, -1, "run_callbacks"); lua_remove(L, -2); - lua_insert(L, errorhandler + 1); + lua_insert(L, error_handler + 1); // Insert mode after table lua_pushnumber(L, (int)mode); - lua_insert(L, errorhandler + 3); + lua_insert(L, error_handler + 3); // Stack now looks like this: // ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n> - int result = lua_pcall(L, nargs + 2, 1, errorhandler); + int result = lua_pcall(L, nargs + 2, 1, error_handler); if (result != 0) scriptError(result, fxn); - lua_remove(L, -2); // Remove error handler + lua_remove(L, error_handler); } void ScriptApiBase::realityCheck() diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h index d653b5bac..f52474f00 100644 --- a/src/script/cpp_api/s_base.h +++ b/src/script/cpp_api/s_base.h @@ -28,15 +28,15 @@ extern "C" { } #include "irrlichttypes.h" -#include "jthread/jmutex.h" -#include "jthread/jmutexautolock.h" +#include "threads.h" +#include "threading/mutex.h" +#include "threading/mutex_auto_lock.h" #include "common/c_types.h" #include "common/c_internal.h" #define SCRIPTAPI_LOCK_DEBUG #define SCRIPTAPI_DEBUG -#define SCRIPT_MOD_NAME_FIELD "current_mod_name" // MUST be an invalid mod name so that mods can't // use that name to bypass security! #define BUILTIN_MOD_NAME "*builtin*" @@ -64,9 +64,9 @@ public: ScriptApiBase(); virtual ~ScriptApiBase(); - bool loadMod(const std::string &script_path, const std::string &mod_name, - std::string *error=NULL); - bool loadScript(const std::string &script_path, std::string *error=NULL); + // These throw a ModError on failure + void loadMod(const std::string &script_path, const std::string &mod_name); + void loadScript(const std::string &script_path); void runCallbacksRaw(int nargs, RunCallbacksMode mode, const char *fxn); @@ -83,6 +83,7 @@ public: protected: friend class LuaABM; + friend class LuaLBM; friend class InvRef; friend class ObjectRef; friend class NodeMetaRef; @@ -108,13 +109,12 @@ protected: void objectrefGetOrCreate(lua_State *L, ServerActiveObject *cobj); void objectrefGet(lua_State *L, u16 id); - JMutex m_luastackmutex; + RecursiveMutex m_luastackmutex; std::string m_last_run_mod; - // Stack index of Lua error handler - int m_errorhandler; bool m_secure; #ifdef SCRIPTAPI_LOCK_DEBUG - bool m_locked; + int m_lock_recursion_count; + threadid_t m_owning_thread; #endif private: diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp index 0d159846a..378a6bf09 100644 --- a/src/script/cpp_api/s_entity.cpp +++ b/src/script/cpp_api/s_entity.cpp @@ -80,6 +80,8 @@ void ScriptApiEntity::luaentity_Activate(u16 id, verbosestream << "scriptapi_luaentity_activate: id=" << id << std::endl; + int error_handler = PUSH_ERROR_HANDLER(L); + // Get core.luaentities[id] luaentity_get(L, id); int object = lua_gettop(L); @@ -93,11 +95,11 @@ void ScriptApiEntity::luaentity_Activate(u16 id, lua_pushinteger(L, dtime_s); setOriginFromTable(object); - PCALL_RES(lua_pcall(L, 3, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 3, 0, error_handler)); } else { lua_pop(L, 1); } - lua_pop(L, 1); // Pop object + lua_pop(L, 2); // Pop object and error handler } void ScriptApiEntity::luaentity_Remove(u16 id) @@ -126,6 +128,8 @@ std::string ScriptApiEntity::luaentity_GetStaticdata(u16 id) //infostream<<"scriptapi_luaentity_get_staticdata: id="<<id<<std::endl; + int error_handler = PUSH_ERROR_HANDLER(L); + // Get core.luaentities[id] luaentity_get(L, id); int object = lua_gettop(L); @@ -140,9 +144,10 @@ std::string ScriptApiEntity::luaentity_GetStaticdata(u16 id) lua_pushvalue(L, object); // self setOriginFromTable(object); - PCALL_RES(lua_pcall(L, 1, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 1, 1, error_handler)); - lua_remove(L, object); // Remove object + lua_remove(L, object); + lua_remove(L, error_handler); size_t len = 0; const char *s = lua_tolstring(L, -1, &len); @@ -196,6 +201,8 @@ void ScriptApiEntity::luaentity_Step(u16 id, float dtime) //infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl; + int error_handler = PUSH_ERROR_HANDLER(L); + // Get core.luaentities[id] luaentity_get(L, id); int object = lua_gettop(L); @@ -211,9 +218,9 @@ void ScriptApiEntity::luaentity_Step(u16 id, float dtime) lua_pushnumber(L, dtime); // dtime setOriginFromTable(object); - PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 2, 0, error_handler)); - lua_pop(L, 1); // Pop object + lua_pop(L, 2); // Pop object and error handler } // Calls entity:on_punch(ObjectRef puncher, time_from_last_punch, @@ -226,6 +233,8 @@ void ScriptApiEntity::luaentity_Punch(u16 id, //infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl; + int error_handler = PUSH_ERROR_HANDLER(L); + // Get core.luaentities[id] luaentity_get(L,id); int object = lua_gettop(L); @@ -244,9 +253,9 @@ void ScriptApiEntity::luaentity_Punch(u16 id, push_v3f(L, dir); setOriginFromTable(object); - PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 5, 0, error_handler)); - lua_pop(L, 1); // Pop object + lua_pop(L, 2); // Pop object and error handler } // Calls entity:on_rightclick(ObjectRef clicker) @@ -257,6 +266,8 @@ void ScriptApiEntity::luaentity_Rightclick(u16 id, //infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl; + int error_handler = PUSH_ERROR_HANDLER(L); + // Get core.luaentities[id] luaentity_get(L, id); int object = lua_gettop(L); @@ -272,8 +283,8 @@ void ScriptApiEntity::luaentity_Rightclick(u16 id, objectrefGetOrCreate(L, clicker); // Clicker reference setOriginFromTable(object); - PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 2, 0, error_handler)); - lua_pop(L, 1); // Pop object + lua_pop(L, 2); // Pop object and error handler } diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp index 9c733773a..82d0d4f0e 100644 --- a/src/script/cpp_api/s_env.cpp +++ b/src/script/cpp_api/s_env.cpp @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp, - u32 blockseed) + u32 blockseed) { SCRIPTAPI_PRECHECKHEADER @@ -44,7 +44,7 @@ void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp, void ScriptApiEnv::environment_Step(float dtime) { SCRIPTAPI_PRECHECKHEADER - //infostream<<"scriptapi_environment_step"<<std::endl; + //infostream << "scriptapi_environment_step" << std::endl; // Get core.registered_globalsteps lua_getglobal(L, "core"); @@ -58,7 +58,7 @@ void ScriptApiEnv::environment_Step(float dtime) } } -void ScriptApiEnv::player_event(ServerActiveObject* player, std::string type) +void ScriptApiEnv::player_event(ServerActiveObject *player, const std::string &type) { SCRIPTAPI_PRECHECKHEADER @@ -82,75 +82,166 @@ void ScriptApiEnv::player_event(ServerActiveObject* player, std::string type) void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env) { SCRIPTAPI_PRECHECKHEADER - verbosestream<<"scriptapi_add_environment"<<std::endl; + verbosestream << "scriptapi_add_environment" << std::endl; setEnv(env); /* - Add ActiveBlockModifiers to environment + Add {Loading,Active}BlockModifiers to environment */ // Get core.registered_abms lua_getglobal(L, "core"); lua_getfield(L, -1, "registered_abms"); - luaL_checktype(L, -1, LUA_TTABLE); int registered_abms = lua_gettop(L); - if(lua_istable(L, registered_abms)){ - int table = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - int id = lua_tonumber(L, -2); - int current_abm = lua_gettop(L); - - std::set<std::string> trigger_contents; - lua_getfield(L, current_abm, "nodenames"); - if(lua_istable(L, -1)){ - int table = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TSTRING); - trigger_contents.insert(lua_tostring(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } else if(lua_isstring(L, -1)){ + if (!lua_istable(L, registered_abms)) { + lua_pop(L, 1); + throw LuaError("core.registered_abms was not a lua table, as expected."); + } + lua_pushnil(L); + while (lua_next(L, registered_abms)) { + // key at index -2 and value at index -1 + int id = lua_tonumber(L, -2); + int current_abm = lua_gettop(L); + + std::set<std::string> trigger_contents; + lua_getfield(L, current_abm, "nodenames"); + if (lua_istable(L, -1)) { + int table = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, table)) { + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TSTRING); trigger_contents.insert(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); } - lua_pop(L, 1); - - std::set<std::string> required_neighbors; - lua_getfield(L, current_abm, "neighbors"); - if(lua_istable(L, -1)){ - int table = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TSTRING); - required_neighbors.insert(lua_tostring(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } else if(lua_isstring(L, -1)){ + } else if (lua_isstring(L, -1)) { + trigger_contents.insert(lua_tostring(L, -1)); + } + lua_pop(L, 1); + + std::set<std::string> required_neighbors; + lua_getfield(L, current_abm, "neighbors"); + if (lua_istable(L, -1)) { + int table = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, table)) { + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TSTRING); required_neighbors.insert(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); } - lua_pop(L, 1); + } else if (lua_isstring(L, -1)) { + required_neighbors.insert(lua_tostring(L, -1)); + } + lua_pop(L, 1); + + float trigger_interval = 10.0; + getfloatfield(L, current_abm, "interval", trigger_interval); + + int trigger_chance = 50; + getintfield(L, current_abm, "chance", trigger_chance); - float trigger_interval = 10.0; - getfloatfield(L, current_abm, "interval", trigger_interval); + bool simple_catch_up = true; + getboolfield(L, current_abm, "catch_up", simple_catch_up); - int trigger_chance = 50; - getintfield(L, current_abm, "chance", trigger_chance); + LuaABM *abm = new LuaABM(L, id, trigger_contents, required_neighbors, + trigger_interval, trigger_chance, simple_catch_up); + + env->addActiveBlockModifier(abm); + + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + lua_pop(L, 1); - LuaABM *abm = new LuaABM(L, id, trigger_contents, - required_neighbors, trigger_interval, trigger_chance); + // Get core.registered_lbms + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_lbms"); + int registered_lbms = lua_gettop(L); - env->addActiveBlockModifier(abm); + if (!lua_istable(L, registered_lbms)) { + lua_pop(L, 1); + throw LuaError("core.registered_lbms was not a lua table, as expected."); + } - // removes value, keeps key for next iteration - lua_pop(L, 1); + lua_pushnil(L); + while (lua_next(L, registered_lbms)) { + // key at index -2 and value at index -1 + int id = lua_tonumber(L, -2); + int current_lbm = lua_gettop(L); + + std::set<std::string> trigger_contents; + lua_getfield(L, current_lbm, "nodenames"); + if (lua_istable(L, -1)) { + int table = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, table)) { + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TSTRING); + trigger_contents.insert(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } else if (lua_isstring(L, -1)) { + trigger_contents.insert(lua_tostring(L, -1)); } + lua_pop(L, 1); + + std::string name; + getstringfield(L, current_lbm, "name", name); + + bool run_at_every_load = getboolfield_default(L, current_lbm, + "run_at_every_load", false); + + LuaLBM *lbm = new LuaLBM(L, id, trigger_contents, name, + run_at_every_load); + + env->addLoadingBlockModifierDef(lbm); + + // removes value, keeps key for next iteration + lua_pop(L, 1); } lua_pop(L, 1); } + +void ScriptApiEnv::on_emerge_area_completion( + v3s16 blockpos, int action, ScriptCallbackState *state) +{ + Server *server = getServer(); + + // Note that the order of these locks is important! Envlock must *ALWAYS* + // be acquired before attempting to acquire scriptlock, or else ServerThread + // will try to acquire scriptlock after it already owns envlock, thus + // deadlocking EmergeThread and ServerThread + MutexAutoLock envlock(server->m_env_mutex); + + SCRIPTAPI_PRECHECKHEADER + + int error_handler = PUSH_ERROR_HANDLER(L); + + lua_rawgeti(L, LUA_REGISTRYINDEX, state->callback_ref); + luaL_checktype(L, -1, LUA_TFUNCTION); + + push_v3s16(L, blockpos); + lua_pushinteger(L, action); + lua_pushinteger(L, state->refcount); + lua_rawgeti(L, LUA_REGISTRYINDEX, state->args_ref); + + setOriginDirect(state->origin.c_str()); + + try { + PCALL_RES(lua_pcall(L, 4, 0, error_handler)); + } catch (LuaError &e) { + server->setAsyncFatalError(e.what()); + } + + lua_pop(L, 1); // Pop error handler + + if (state->refcount == 0) { + luaL_unref(L, LUA_REGISTRYINDEX, state->callback_ref); + luaL_unref(L, LUA_REGISTRYINDEX, state->args_ref); + } +} diff --git a/src/script/cpp_api/s_env.h b/src/script/cpp_api/s_env.h index 180cfabd0..e07024565 100644 --- a/src/script/cpp_api/s_env.h +++ b/src/script/cpp_api/s_env.h @@ -24,19 +24,23 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_v3d.h" class ServerEnvironment; -struct MapgenParams; +struct ScriptCallbackState; -class ScriptApiEnv - : virtual public ScriptApiBase +class ScriptApiEnv : virtual public ScriptApiBase { public: - // On environment step + // Called on environment step void environment_Step(float dtime); - // After generating a piece of map - void environment_OnGenerated(v3s16 minp, v3s16 maxp,u32 blockseed); - //called on player event - void player_event(ServerActiveObject* player, std::string type); + // Called after generating a piece of map + void environment_OnGenerated(v3s16 minp, v3s16 maxp, u32 blockseed); + + // Called on player event + void player_event(ServerActiveObject *player, const std::string &type); + + // Called after emerge of a block queued from core.emerge_area() + void on_emerge_area_completion(v3s16 blockpos, int action, + ScriptCallbackState *state); void initializeEnvironment(ServerEnvironment *env); }; diff --git a/src/script/cpp_api/s_internal.h b/src/script/cpp_api/s_internal.h index 9999a584a..37473c497 100644 --- a/src/script/cpp_api/s_internal.h +++ b/src/script/cpp_api/s_internal.h @@ -32,28 +32,50 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifdef SCRIPTAPI_LOCK_DEBUG #include "debug.h" // assert() + class LockChecker { public: - LockChecker(bool* variable) { - assert(*variable == false); + LockChecker(int *recursion_counter, threadid_t *owning_thread) + { + m_lock_recursion_counter = recursion_counter; + m_owning_thread = owning_thread; + m_original_level = *recursion_counter; + + if (*m_lock_recursion_counter > 0) + assert(thr_is_current_thread(*m_owning_thread)); + else + *m_owning_thread = thr_get_current_thread_id(); - m_variable = variable; - *m_variable = true; + (*m_lock_recursion_counter)++; } - ~LockChecker() { - *m_variable = false; + + ~LockChecker() + { + assert(thr_is_current_thread(*m_owning_thread)); + assert(*m_lock_recursion_counter > 0); + + (*m_lock_recursion_counter)--; + + assert(*m_lock_recursion_counter == m_original_level); } + private: -bool* m_variable; + int *m_lock_recursion_counter; + int m_original_level; + threadid_t *m_owning_thread; }; -#define SCRIPTAPI_LOCK_CHECK LockChecker(&(this->m_locked)) +#define SCRIPTAPI_LOCK_CHECK \ + LockChecker scriptlock_checker( \ + &this->m_lock_recursion_count, \ + &this->m_owning_thread) + #else -#define SCRIPTAPI_LOCK_CHECK while(0) + #define SCRIPTAPI_LOCK_CHECK while(0) #endif #define SCRIPTAPI_PRECHECKHEADER \ - JMutexAutoLock(this->m_luastackmutex); \ + RecursiveMutexAutoLock scriptlock(this->m_luastackmutex); \ SCRIPTAPI_LOCK_CHECK; \ realityCheck(); \ lua_State *L = getStack(); \ diff --git a/src/script/cpp_api/s_inventory.cpp b/src/script/cpp_api/s_inventory.cpp index 019d1ccc0..c90c7d4e2 100644 --- a/src/script/cpp_api/s_inventory.cpp +++ b/src/script/cpp_api/s_inventory.cpp @@ -33,6 +33,8 @@ int ScriptApiDetached::detached_inventory_AllowMove( { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + // Push callback function on stack if (!getDetachedInventoryCallback(name, "allow_move")) return count; @@ -48,11 +50,11 @@ int ScriptApiDetached::detached_inventory_AllowMove( lua_pushinteger(L, to_index + 1); // to_index lua_pushinteger(L, count); // count objectrefGetOrCreate(L, player); // player - PCALL_RES(lua_pcall(L, 7, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 7, 1, error_handler)); if(!lua_isnumber(L, -1)) throw LuaError("allow_move should return a number. name=" + name); int ret = luaL_checkinteger(L, -1); - lua_pop(L, 1); // Pop integer + lua_pop(L, 2); // Pop integer and error handler return ret; } @@ -64,6 +66,8 @@ int ScriptApiDetached::detached_inventory_AllowPut( { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + // Push callback function on stack if (!getDetachedInventoryCallback(name, "allow_put")) return stack.count; // All will be accepted @@ -76,11 +80,11 @@ int ScriptApiDetached::detached_inventory_AllowPut( lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 5, 1, error_handler)); if (!lua_isnumber(L, -1)) throw LuaError("allow_put should return a number. name=" + name); int ret = luaL_checkinteger(L, -1); - lua_pop(L, 1); // Pop integer + lua_pop(L, 2); // Pop integer and error handler return ret; } @@ -92,6 +96,8 @@ int ScriptApiDetached::detached_inventory_AllowTake( { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + // Push callback function on stack if (!getDetachedInventoryCallback(name, "allow_take")) return stack.count; // All will be accepted @@ -104,11 +110,11 @@ int ScriptApiDetached::detached_inventory_AllowTake( lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 5, 1, error_handler)); if (!lua_isnumber(L, -1)) throw LuaError("allow_take should return a number. name=" + name); int ret = luaL_checkinteger(L, -1); - lua_pop(L, 1); // Pop integer + lua_pop(L, 2); // Pop integer and error handler return ret; } @@ -121,6 +127,8 @@ void ScriptApiDetached::detached_inventory_OnMove( { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + // Push callback function on stack if (!getDetachedInventoryCallback(name, "on_move")) return; @@ -136,7 +144,8 @@ void ScriptApiDetached::detached_inventory_OnMove( lua_pushinteger(L, to_index + 1); // to_index lua_pushinteger(L, count); // count objectrefGetOrCreate(L, player); // player - PCALL_RES(lua_pcall(L, 7, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 7, 0, error_handler)); + lua_pop(L, 1); // Pop error handler } // Report put items @@ -147,6 +156,8 @@ void ScriptApiDetached::detached_inventory_OnPut( { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + // Push callback function on stack if (!getDetachedInventoryCallback(name, "on_put")) return; @@ -160,7 +171,8 @@ void ScriptApiDetached::detached_inventory_OnPut( lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 5, 0, error_handler)); + lua_pop(L, 1); // Pop error handler } // Report taken items @@ -171,6 +183,8 @@ void ScriptApiDetached::detached_inventory_OnTake( { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + // Push callback function on stack if (!getDetachedInventoryCallback(name, "on_take")) return; @@ -184,7 +198,8 @@ void ScriptApiDetached::detached_inventory_OnTake( lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 5, 0, error_handler)); + lua_pop(L, 1); // Pop error handler } // Retrieves core.detached_inventories[name][callbackname] diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp index 4d4d416ec..3c84fb8cf 100644 --- a/src/script/cpp_api/s_item.cpp +++ b/src/script/cpp_api/s_item.cpp @@ -34,6 +34,8 @@ bool ScriptApiItem::item_OnDrop(ItemStack &item, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + // Push callback function on stack if (!getItemCallback(item.name.c_str(), "on_drop")) return false; @@ -42,7 +44,7 @@ bool ScriptApiItem::item_OnDrop(ItemStack &item, LuaItemStack::create(L, item); objectrefGetOrCreate(L, dropper); pushFloatPos(L, pos); - PCALL_RES(lua_pcall(L, 3, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 3, 1, error_handler)); if (!lua_isnil(L, -1)) { try { item = read_item(L,-1, getServer()); @@ -50,7 +52,7 @@ bool ScriptApiItem::item_OnDrop(ItemStack &item, throw LuaError(std::string(e.what()) + ". item=" + item.name); } } - lua_pop(L, 1); // Pop item + lua_pop(L, 2); // Pop item and error handler return true; } @@ -59,6 +61,8 @@ bool ScriptApiItem::item_OnPlace(ItemStack &item, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + // Push callback function on stack if (!getItemCallback(item.name.c_str(), "on_place")) return false; @@ -67,7 +71,7 @@ bool ScriptApiItem::item_OnPlace(ItemStack &item, LuaItemStack::create(L, item); objectrefGetOrCreate(L, placer); pushPointedThing(pointed); - PCALL_RES(lua_pcall(L, 3, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 3, 1, error_handler)); if (!lua_isnil(L, -1)) { try { item = read_item(L,-1, getServer()); @@ -75,7 +79,7 @@ bool ScriptApiItem::item_OnPlace(ItemStack &item, throw LuaError(std::string(e.what()) + ". item=" + item.name); } } - lua_pop(L, 1); // Pop item + lua_pop(L, 2); // Pop item and error handler return true; } @@ -84,6 +88,8 @@ bool ScriptApiItem::item_OnUse(ItemStack &item, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + // Push callback function on stack if (!getItemCallback(item.name.c_str(), "on_use")) return false; @@ -92,7 +98,7 @@ bool ScriptApiItem::item_OnUse(ItemStack &item, LuaItemStack::create(L, item); objectrefGetOrCreate(L, user); pushPointedThing(pointed); - PCALL_RES(lua_pcall(L, 3, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 3, 1, error_handler)); if(!lua_isnil(L, -1)) { try { item = read_item(L,-1, getServer()); @@ -100,7 +106,33 @@ bool ScriptApiItem::item_OnUse(ItemStack &item, throw LuaError(std::string(e.what()) + ". item=" + item.name); } } - lua_pop(L, 1); // Pop item + lua_pop(L, 2); // Pop item and error handler + return true; +} + +bool ScriptApiItem::item_OnSecondaryUse(ItemStack &item, ServerActiveObject *user) +{ + SCRIPTAPI_PRECHECKHEADER + + int error_handler = PUSH_ERROR_HANDLER(L); + + if (!getItemCallback(item.name.c_str(), "on_secondary_use")) + return false; + + LuaItemStack::create(L, item); + objectrefGetOrCreate(L, user); + PointedThing pointed; + pointed.type = POINTEDTHING_NOTHING; + pushPointedThing(pointed); + PCALL_RES(lua_pcall(L, 3, 1, error_handler)); + if (!lua_isnil(L, -1)) { + try { + item = read_item(L, -1, getServer()); + } catch (LuaError &e) { + throw LuaError(std::string(e.what()) + ". item=" + item.name); + } + } + lua_pop(L, 2); // Pop item and error handler return true; } @@ -109,6 +141,8 @@ bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + lua_getglobal(L, "core"); lua_getfield(L, -1, "on_craft"); LuaItemStack::create(L, item); @@ -122,7 +156,7 @@ bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user, push_items(L, items); InvRef::create(L, craft_inv); - PCALL_RES(lua_pcall(L, 4, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 4, 1, error_handler)); if (!lua_isnil(L, -1)) { try { item = read_item(L,-1, getServer()); @@ -130,7 +164,7 @@ bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user, throw LuaError(std::string(e.what()) + ". item=" + item.name); } } - lua_pop(L, 1); // Pop item + lua_pop(L, 2); // Pop item and error handler return true; } @@ -139,6 +173,8 @@ bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + lua_getglobal(L, "core"); lua_getfield(L, -1, "craft_predict"); LuaItemStack::create(L, item); @@ -152,7 +188,7 @@ bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user, push_items(L, items); InvRef::create(L, craft_inv); - PCALL_RES(lua_pcall(L, 4, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 4, 1, error_handler)); if (!lua_isnil(L, -1)) { try { item = read_item(L,-1, getServer()); @@ -160,7 +196,7 @@ bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user, throw LuaError(std::string(e.what()) + ". item=" + item.name); } } - lua_pop(L, 1); // Pop item + lua_pop(L, 2); // Pop item and error handler return true; } diff --git a/src/script/cpp_api/s_item.h b/src/script/cpp_api/s_item.h index 88cc1909d..7350a71c5 100644 --- a/src/script/cpp_api/s_item.h +++ b/src/script/cpp_api/s_item.h @@ -42,6 +42,8 @@ public: ServerActiveObject *placer, const PointedThing &pointed); bool item_OnUse(ItemStack &item, ServerActiveObject *user, const PointedThing &pointed); + bool item_OnSecondaryUse(ItemStack &item, + ServerActiveObject *user); bool item_OnCraft(ItemStack &item, ServerActiveObject *user, const InventoryList *old_craft_grid, const InventoryLocation &craft_inv); bool item_CraftPredict(ItemStack &item, ServerActiveObject *user, diff --git a/src/script/cpp_api/s_mainmenu.cpp b/src/script/cpp_api/s_mainmenu.cpp index 17ceff082..e9a7a13b9 100644 --- a/src/script/cpp_api/s_mainmenu.cpp +++ b/src/script/cpp_api/s_mainmenu.cpp @@ -43,6 +43,8 @@ void ScriptApiMainMenu::handleMainMenuEvent(std::string text) { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + // Get handler function lua_getglobal(L, "core"); lua_getfield(L, -1, "event_handler"); @@ -55,13 +57,16 @@ void ScriptApiMainMenu::handleMainMenuEvent(std::string text) // Call it lua_pushstring(L, text.c_str()); - PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 1, 0, error_handler)); + lua_pop(L, 1); // Pop error handler } void ScriptApiMainMenu::handleMainMenuButtons(const StringMap &fields) { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + // Get handler function lua_getglobal(L, "core"); lua_getfield(L, -1, "button_handler"); @@ -84,6 +89,7 @@ void ScriptApiMainMenu::handleMainMenuButtons(const StringMap &fields) } // Call it - PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 1, 0, error_handler)); + lua_pop(L, 1); // Pop error handler } diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp index dac058b13..17f0f0dac 100644 --- a/src/script/cpp_api/s_node.cpp +++ b/src/script/cpp_api/s_node.cpp @@ -57,6 +57,7 @@ struct EnumString ScriptApiNode::es_ContentParamType2[] = {CPT2_FACEDIR, "facedir"}, {CPT2_WALLMOUNTED, "wallmounted"}, {CPT2_LEVELED, "leveled"}, + {CPT2_DEGROTATE, "degrotate"}, {0, NULL}, }; @@ -81,6 +82,7 @@ struct EnumString ScriptApiNode::es_NodeBoxType[] = {NODEBOX_FIXED, "fixed"}, {NODEBOX_WALLMOUNTED, "wallmounted"}, {NODEBOX_LEVELED, "leveled"}, + {NODEBOX_CONNECTED, "connected"}, {0, NULL}, }; @@ -95,6 +97,8 @@ bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + INodeDefManager *ndef = getServer()->ndef(); // Push callback function on stack @@ -106,7 +110,8 @@ bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node, pushnode(L, node, ndef); objectrefGetOrCreate(L, puncher); pushPointedThing(pointed); - PCALL_RES(lua_pcall(L, 4, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 4, 0, error_handler)); + lua_pop(L, 1); // Pop error handler return true; } @@ -115,6 +120,8 @@ bool ScriptApiNode::node_on_dig(v3s16 p, MapNode node, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + INodeDefManager *ndef = getServer()->ndef(); // Push callback function on stack @@ -125,7 +132,8 @@ bool ScriptApiNode::node_on_dig(v3s16 p, MapNode node, push_v3s16(L, p); pushnode(L, node, ndef); objectrefGetOrCreate(L, digger); - PCALL_RES(lua_pcall(L, 3, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 3, 0, error_handler)); + lua_pop(L, 1); // Pop error handler return true; } @@ -133,6 +141,8 @@ void ScriptApiNode::node_on_construct(v3s16 p, MapNode node) { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + INodeDefManager *ndef = getServer()->ndef(); // Push callback function on stack @@ -141,13 +151,16 @@ void ScriptApiNode::node_on_construct(v3s16 p, MapNode node) // Call function push_v3s16(L, p); - PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 1, 0, error_handler)); + lua_pop(L, 1); // Pop error handler } void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node) { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + INodeDefManager *ndef = getServer()->ndef(); // Push callback function on stack @@ -156,13 +169,16 @@ void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node) // Call function push_v3s16(L, p); - PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 1, 0, error_handler)); + lua_pop(L, 1); // Pop error handler } void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node) { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + INodeDefManager *ndef = getServer()->ndef(); // Push callback function on stack @@ -172,13 +188,16 @@ void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node) // Call function push_v3s16(L, p); pushnode(L, node, ndef); - PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 2, 0, error_handler)); + lua_pop(L, 1); // Pop error handler } bool ScriptApiNode::node_on_timer(v3s16 p, MapNode node, f32 dtime) { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + INodeDefManager *ndef = getServer()->ndef(); // Push callback function on stack @@ -188,7 +207,8 @@ bool ScriptApiNode::node_on_timer(v3s16 p, MapNode node, f32 dtime) // Call function push_v3s16(L, p); lua_pushnumber(L,dtime); - PCALL_RES(lua_pcall(L, 2, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 2, 1, error_handler)); + lua_remove(L, error_handler); return (bool) lua_isboolean(L, -1) && (bool) lua_toboolean(L, -1) == true; } @@ -199,6 +219,8 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + INodeDefManager *ndef = getServer()->ndef(); // If node doesn't exist, we don't know what callback to call @@ -223,23 +245,30 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p, lua_settable(L, -3); } objectrefGetOrCreate(L, sender); // player - PCALL_RES(lua_pcall(L, 4, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 4, 0, error_handler)); + lua_pop(L, 1); // Pop error handler } void ScriptApiNode::node_falling_update(v3s16 p) { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + lua_getglobal(L, "nodeupdate"); push_v3s16(L, p); - PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 1, 0, error_handler)); + lua_pop(L, 1); // Pop error handler } void ScriptApiNode::node_falling_update_single(v3s16 p) { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + lua_getglobal(L, "nodeupdate_single"); push_v3s16(L, p); - PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 1, 0, error_handler)); + lua_pop(L, 1); // Pop error handler } diff --git a/src/script/cpp_api/s_nodemeta.cpp b/src/script/cpp_api/s_nodemeta.cpp index 638750b0e..d050c0bc9 100644 --- a/src/script/cpp_api/s_nodemeta.cpp +++ b/src/script/cpp_api/s_nodemeta.cpp @@ -34,6 +34,8 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowMove(v3s16 p, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + INodeDefManager *ndef = getServer()->ndef(); // If node doesn't exist, we don't know what callback to call @@ -54,12 +56,12 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowMove(v3s16 p, lua_pushinteger(L, to_index + 1); // to_index lua_pushinteger(L, count); // count objectrefGetOrCreate(L, player); // player - PCALL_RES(lua_pcall(L, 7, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 7, 1, error_handler)); if (!lua_isnumber(L, -1)) throw LuaError("allow_metadata_inventory_move should" " return a number, guilty node: " + nodename); int num = luaL_checkinteger(L, -1); - lua_pop(L, 1); // Pop integer + lua_pop(L, 2); // Pop integer and error handler return num; } @@ -70,6 +72,8 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowPut(v3s16 p, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + INodeDefManager *ndef = getServer()->ndef(); // If node doesn't exist, we don't know what callback to call @@ -88,12 +92,12 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowPut(v3s16 p, lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 5, 1, error_handler)); if(!lua_isnumber(L, -1)) throw LuaError("allow_metadata_inventory_put should" " return a number, guilty node: " + nodename); int num = luaL_checkinteger(L, -1); - lua_pop(L, 1); // Pop integer + lua_pop(L, 2); // Pop integer and error handler return num; } @@ -104,6 +108,8 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowTake(v3s16 p, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + INodeDefManager *ndef = getServer()->ndef(); // If node doesn't exist, we don't know what callback to call @@ -122,12 +128,12 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowTake(v3s16 p, lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 5, 1, error_handler)); if (!lua_isnumber(L, -1)) throw LuaError("allow_metadata_inventory_take should" " return a number, guilty node: " + nodename); int num = luaL_checkinteger(L, -1); - lua_pop(L, 1); // Pop integer + lua_pop(L, 2); // Pop integer and error handler return num; } @@ -139,6 +145,8 @@ void ScriptApiNodemeta::nodemeta_inventory_OnMove(v3s16 p, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + INodeDefManager *ndef = getServer()->ndef(); // If node doesn't exist, we don't know what callback to call @@ -159,7 +167,8 @@ void ScriptApiNodemeta::nodemeta_inventory_OnMove(v3s16 p, lua_pushinteger(L, to_index + 1); // to_index lua_pushinteger(L, count); // count objectrefGetOrCreate(L, player); // player - PCALL_RES(lua_pcall(L, 7, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 7, 0, error_handler)); + lua_pop(L, 1); // Pop error handler } // Report put items @@ -169,6 +178,8 @@ void ScriptApiNodemeta::nodemeta_inventory_OnPut(v3s16 p, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + INodeDefManager *ndef = getServer()->ndef(); // If node doesn't exist, we don't know what callback to call @@ -187,7 +198,8 @@ void ScriptApiNodemeta::nodemeta_inventory_OnPut(v3s16 p, lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 5, 0, error_handler)); + lua_pop(L, 1); // Pop error handler } // Report taken items @@ -197,6 +209,8 @@ void ScriptApiNodemeta::nodemeta_inventory_OnTake(v3s16 p, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + INodeDefManager *ndef = getServer()->ndef(); // If node doesn't exist, we don't know what callback to call @@ -215,7 +229,8 @@ void ScriptApiNodemeta::nodemeta_inventory_OnTake(v3s16 p, lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 5, 0, error_handler)); + lua_pop(L, 1); // Pop error handler } ScriptApiNodemeta::ScriptApiNodemeta() diff --git a/src/script/cpp_api/s_player.cpp b/src/script/cpp_api/s_player.cpp index ef3c31cfd..807430678 100644 --- a/src/script/cpp_api/s_player.cpp +++ b/src/script/cpp_api/s_player.cpp @@ -74,6 +74,8 @@ s16 ScriptApiPlayer::on_player_hpchange(ServerActiveObject *player, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); + // Get core.registered_on_player_hpchange lua_getglobal(L, "core"); lua_getfield(L, -1, "registered_on_player_hpchange"); @@ -81,9 +83,9 @@ s16 ScriptApiPlayer::on_player_hpchange(ServerActiveObject *player, objectrefGetOrCreate(L, player); lua_pushnumber(L, hp_change); - PCALL_RES(lua_pcall(L, 2, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 2, 1, error_handler)); hp_change = lua_tointeger(L, -1); - lua_pop(L, -1); + lua_pop(L, 2); // Pop result and error handler return hp_change; } diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index 6a6d40307..730235c7b 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -47,7 +47,7 @@ static inline void copy_safe(lua_State *L, const char *list[], unsigned len, int // Pushes the original version of a library function on the stack, from the old version static inline void push_original(lua_State *L, const char *lib, const char *func) { - lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup"); + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP); lua_getfield(L, -1, lib); lua_remove(L, -2); // Remove globals_backup lua_getfield(L, -1, func); @@ -116,7 +116,6 @@ void ScriptApiSecurity::initializeSecurity() "upvaluejoin", "sethook", "debug", - "getupvalue", "setlocal", }; static const char *package_whitelist[] = { @@ -143,7 +142,7 @@ void ScriptApiSecurity::initializeSecurity() // Backup globals to the registry lua_getglobal(L, "_G"); - lua_setfield(L, LUA_REGISTRYINDEX, "globals_backup"); + lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP); // Replace the global environment with an empty one #if LUA_VERSION_NUM <= 501 @@ -165,7 +164,7 @@ void ScriptApiSecurity::initializeSecurity() #endif // Get old globals - lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup"); + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP); int old_globals = lua_gettop(L); @@ -241,7 +240,7 @@ void ScriptApiSecurity::initializeSecurity() bool ScriptApiSecurity::isSecure(lua_State *L) { - lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup"); + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP); bool secure = !lua_isnil(L, -1); lua_pop(L, 1); return secure; @@ -356,7 +355,7 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path) if (!removed.empty()) abs_path += DIR_DELIM + removed; // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "scriptapi"); + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI); ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1); lua_pop(L, 1); const Server *server = script->getServer(); @@ -364,7 +363,7 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path) if (!server) return false; // Get mod name - lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); if (lua_isstring(L, -1)) { std::string mod_name = lua_tostring(L, -1); diff --git a/src/script/cpp_api/s_security.h b/src/script/cpp_api/s_security.h index 4a4389cf5..97bc5c067 100644 --- a/src/script/cpp_api/s_security.h +++ b/src/script/cpp_api/s_security.h @@ -25,9 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #define CHECK_SECURE_PATH(L, path) \ if (!ScriptApiSecurity::checkPath(L, path)) { \ - lua_pushstring(L, (std::string("Attempt to access external file ") + \ - path + " with mod security on.").c_str()); \ - lua_error(L); \ + throw LuaError(std::string("Attempt to access external file ") + \ + path + " with mod security on."); \ } #define CHECK_SECURE_PATH_OPTIONAL(L, path) \ if (ScriptApiSecurity::isSecure(L)) { \ diff --git a/src/script/cpp_api/s_server.cpp b/src/script/cpp_api/s_server.cpp index ec2f9c0af..38bd41f87 100644 --- a/src/script/cpp_api/s_server.cpp +++ b/src/script/cpp_api/s_server.cpp @@ -27,13 +27,15 @@ bool ScriptApiServer::getAuth(const std::string &playername, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); getAuthHandler(); lua_getfield(L, -1, "get_auth"); if (lua_type(L, -1) != LUA_TFUNCTION) throw LuaError("Authentication handler missing get_auth"); lua_pushstring(L, playername.c_str()); - PCALL_RES(lua_pcall(L, 1, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 1, 1, error_handler)); lua_remove(L, -2); // Remove auth handler + lua_remove(L, error_handler); // nil = login not allowed if (lua_isnil(L, -1)) @@ -99,6 +101,7 @@ void ScriptApiServer::createAuth(const std::string &playername, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); getAuthHandler(); lua_getfield(L, -1, "create_auth"); lua_remove(L, -2); // Remove auth handler @@ -106,7 +109,8 @@ void ScriptApiServer::createAuth(const std::string &playername, throw LuaError("Authentication handler missing create_auth"); lua_pushstring(L, playername.c_str()); lua_pushstring(L, password.c_str()); - PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler)); + PCALL_RES(lua_pcall(L, 2, 0, error_handler)); + lua_pop(L, 1); // Pop error handler } bool ScriptApiServer::setPassword(const std::string &playername, @@ -114,6 +118,7 @@ bool ScriptApiServer::setPassword(const std::string &playername, { SCRIPTAPI_PRECHECKHEADER + int error_handler = PUSH_ERROR_HANDLER(L); getAuthHandler(); lua_getfield(L, -1, "set_password"); lua_remove(L, -2); // Remove auth handler @@ -121,7 +126,8 @@ bool ScriptApiServer::setPassword(const std::string &playername, throw LuaError("Authentication handler missing set_password"); lua_pushstring(L, playername.c_str()); lua_pushstring(L, password.c_str()); - PCALL_RES(lua_pcall(L, 2, 1, m_errorhandler)); + PCALL_RES(lua_pcall(L, 2, 1, error_handler)); + lua_remove(L, error_handler); return lua_toboolean(L, -1); } diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt index 2501ce6d6..d507dcf70 100644 --- a/src/script/lua_api/CMakeLists.txt +++ b/src/script/lua_api/CMakeLists.txt @@ -16,6 +16,7 @@ set(common_SCRIPT_LUA_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/l_util.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_vmanip.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_settings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_http.cpp PARENT_SCOPE) set(client_SCRIPT_LUA_API_SRCS diff --git a/src/script/lua_api/l_areastore.cpp b/src/script/lua_api/l_areastore.cpp index 1e9075119..20e7875c7 100644 --- a/src/script/lua_api/l_areastore.cpp +++ b/src/script/lua_api/l_areastore.cpp @@ -22,11 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_internal.h" #include "common/c_converter.h" #include "cpp_api/s_security.h" -#include "areastore.h" +#include "irr_v3d.h" +#include "util/areastore.h" #include "filesys.h" -#ifndef ANDROID - #include "cmake_config.h" -#endif #include <fstream> static inline void get_data_and_border_flags(lua_State *L, u8 start_i, @@ -45,6 +43,7 @@ static void push_area(lua_State *L, const Area *a, { if (!include_borders && !include_data) { lua_pushboolean(L, true); + return; } lua_newtable(L); if (include_borders) { @@ -71,6 +70,22 @@ static inline void push_areas(lua_State *L, const std::vector<Area *> &areas, } } +// 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) { @@ -149,7 +164,7 @@ int LuaAreaStore::l_get_areas_in_area(lua_State *L) return 1; } -// insert_area(edge1, edge2, data) +// insert_area(edge1, edge2, data, id) int LuaAreaStore::l_insert_area(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -157,26 +172,18 @@ int LuaAreaStore::l_insert_area(lua_State *L) LuaAreaStore *o = checkobject(L, 1); AreaStore *ast = o->as; - Area a; - - a.minedge = check_v3s16(L, 2); - a.maxedge = check_v3s16(L, 3); - - a.extremifyEdges(); - a.id = ast->getFreeId(a.minedge, a.maxedge); - - if (a.id == AREA_ID_INVALID) { - // couldn't get free id - lua_pushnil(L); - return 1; - } + 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); - ast->insertArea(a); + if (lua_isnumber(L, 5)) + a.id = lua_tonumber(L, 5); + + if (!ast->insertArea(&a)) + return 0; lua_pushnumber(L, a.id); return 1; @@ -229,17 +236,15 @@ int LuaAreaStore::l_set_cache_params(lua_State *L) return 0; } -#if 0 // to_string() int LuaAreaStore::l_to_string(lua_State *L) { NO_MAP_LOCK_REQUIRED; LuaAreaStore *o = checkobject(L, 1); - AreaStore *ast = o->as; std::ostringstream os(std::ios_base::binary); - ast->serialize(os); + o->as->serialize(os); std::string str = os.str(); lua_pushlstring(L, str.c_str(), str.length()); @@ -270,16 +275,12 @@ int LuaAreaStore::l_from_string(lua_State *L) NO_MAP_LOCK_REQUIRED; LuaAreaStore *o = checkobject(L, 1); - AreaStore *ast = o->as; size_t len; const char *str = luaL_checklstring(L, 2, &len); std::istringstream is(std::string(str, len), std::ios::binary); - bool success = ast->deserialize(is); - - lua_pushboolean(L, success); - return 1; + return deserialization_helper(L, o->as, is); } // from_file(filename) @@ -288,26 +289,17 @@ int LuaAreaStore::l_from_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_OPTIONAL(L, filename); std::ifstream is(filename, std::ios::binary); - bool success = ast->deserialize(is); - - lua_pushboolean(L, success); - return 1; + return deserialization_helper(L, o->as, is); } -#endif LuaAreaStore::LuaAreaStore() { -#if USE_SPATIAL - this->as = new SpatialAreaStore(); -#else - this->as = new VectorAreaStore(); -#endif + this->as = AreaStore::getOptimalImplementation(); } LuaAreaStore::LuaAreaStore(const std::string &type) @@ -393,9 +385,9 @@ const luaL_reg LuaAreaStore::methods[] = { luamethod(LuaAreaStore, reserve), luamethod(LuaAreaStore, remove_area), luamethod(LuaAreaStore, set_cache_params), - /* luamethod(LuaAreaStore, to_string), + luamethod(LuaAreaStore, to_string), luamethod(LuaAreaStore, to_file), luamethod(LuaAreaStore, from_string), - luamethod(LuaAreaStore, from_file),*/ + luamethod(LuaAreaStore, from_file), {0,0} }; diff --git a/src/script/lua_api/l_areastore.h b/src/script/lua_api/l_areastore.h index a25529627..4bd94cebe 100644 --- a/src/script/lua_api/l_areastore.h +++ b/src/script/lua_api/l_areastore.h @@ -17,16 +17,14 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef L_AREASTORE_H_ -#define L_AREASTORE_H_ +#ifndef L_AREA_STORE_H_ +#define L_AREA_STORE_H_ #include "lua_api/l_base.h" -#include "irr_v3d.h" -#include "areastore.h" -/* - AreaStore - */ + +class AreaStore; + class LuaAreaStore : public ModApiBase { private: @@ -45,11 +43,11 @@ private: static int l_set_cache_params(lua_State *L); - /* static int l_to_string(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); */ + static int l_from_file(lua_State *L); public: AreaStore *as; @@ -67,4 +65,4 @@ public: static void Register(lua_State *L); }; -#endif /* L_AREASTORE_H_ */ +#endif // L_AREA_STORE_H_ diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp index 6ad3e4ba2..515a7d933 100644 --- a/src/script/lua_api/l_base.cpp +++ b/src/script/lua_api/l_base.cpp @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., ScriptApiBase *ModApiBase::getScriptApiBase(lua_State *L) { // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "scriptapi"); + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI); ScriptApiBase *sapi_ptr = (ScriptApiBase*) lua_touserdata(L, -1); lua_pop(L, 1); return sapi_ptr; @@ -49,7 +49,7 @@ GUIEngine *ModApiBase::getGuiEngine(lua_State *L) std::string ModApiBase::getCurrentModPath(lua_State *L) { - lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); const char *current_mod_name = lua_tostring(L, -1); if (!current_mod_name) return "."; diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 28afdd071..8284c3fcb 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -33,11 +33,15 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/pointedthing.h" #include "content_sao.h" #include "treegen.h" +#include "emerge.h" #include "pathfinder.h" -#define GET_ENV_PTR ServerEnvironment* env = \ - dynamic_cast<ServerEnvironment*>(getEnv(L)); \ - if (env == NULL) return 0 +struct EnumString ModApiEnvMod::es_ClearObjectsMode[] = +{ + {CLEAR_OBJECTS_MODE_FULL, "full"}, + {CLEAR_OBJECTS_MODE_QUICK, "quick"}, + {0, NULL}, +}; /////////////////////////////////////////////////////////////////////////////// @@ -52,8 +56,7 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, sanity_check(lua_checkstack(L, 20)); StackUnroller stack_unroller(L); - lua_pushcfunction(L, script_error_handler); - int errorhandler = lua_gettop(L); + int error_handler = PUSH_ERROR_HANDLER(L); // Get registered_abms lua_getglobal(L, "core"); @@ -80,13 +83,68 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, lua_pushnumber(L, active_object_count); lua_pushnumber(L, active_object_count_wider); - int result = lua_pcall(L, 4, 0, errorhandler); + 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) +{ + GameScripting *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_pushnumber(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 +} + +void LuaEmergeAreaCallback(v3s16 blockpos, EmergeAction action, void *param) +{ + ScriptCallbackState *state = (ScriptCallbackState *)param; + assert(state != NULL); + assert(state->script != NULL); + assert(state->refcount > 0); + + state->refcount--; + + state->script->on_emerge_area_completion(blockpos, action, state); + + if (state->refcount == 0) + delete state; +} + // Exported functions // set_node(pos, node) @@ -412,8 +470,7 @@ int ModApiEnvMod::l_add_item(lua_State *L) if(item.empty() || !item.isKnown(getServer(L)->idef())) return 0; - lua_pushcfunction(L, script_error_handler); - int errorhandler = lua_gettop(L); + int error_handler = PUSH_ERROR_HANDLER(L); // Use spawn_item to spawn a __builtin:item lua_getglobal(L, "core"); @@ -424,9 +481,9 @@ int ModApiEnvMod::l_add_item(lua_State *L) lua_pushvalue(L, 1); lua_pushstring(L, item.getItemString().c_str()); - PCALL_RESL(L, lua_pcall(L, 2, 1, errorhandler)); + PCALL_RESL(L, lua_pcall(L, 2, 1, error_handler)); - lua_remove(L, errorhandler); // Remove error handler + lua_remove(L, error_handler); return 1; } @@ -504,6 +561,15 @@ int ModApiEnvMod::l_get_timeofday(lua_State *L) return 1; } +// get_day_count() -> int +int ModApiEnvMod::l_get_day_count(lua_State *L) +{ + GET_ENV_PTR; + + lua_pushnumber(L, env->getDayCount()); + return 1; +} + // get_gametime() int ModApiEnvMod::l_get_gametime(lua_State *L) { @@ -659,7 +725,7 @@ int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L) // returns world-specific PerlinNoise int ModApiEnvMod::l_get_perlin(lua_State *L) { - GET_ENV_PTR; + GET_ENV_PTR_NO_MAP_LOCK; NoiseParams params; @@ -685,7 +751,7 @@ int ModApiEnvMod::l_get_perlin(lua_State *L) // returns world-specific PerlinNoiseMap int ModApiEnvMod::l_get_perlin_map(lua_State *L) { - GET_ENV_PTR; + GET_ENV_PTR_NO_MAP_LOCK; NoiseParams np; if (!read_noiseparams(L, 1, &np)) @@ -717,13 +783,20 @@ int ModApiEnvMod::l_get_voxel_manip(lua_State *L) return 1; } -// clear_objects() +// 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; - env->clearAllObjects(); + ClearObjectsMode mode = CLEAR_OBJECTS_MODE_FULL; + if (lua_istable(L, 1)) { + mode = (ClearObjectsMode)getenumfield(L, 1, "mode", + ModApiEnvMod::es_ClearObjectsMode, mode); + } + + env->clearObjects(mode); return 0; } @@ -753,6 +826,51 @@ int ModApiEnvMod::l_line_of_sight(lua_State *L) return 1; } +// 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) @@ -797,19 +915,19 @@ int ModApiEnvMod::l_find_path(lua_State *L) unsigned int searchdistance = luaL_checkint(L, 3); unsigned int max_jump = luaL_checkint(L, 4); unsigned int max_drop = luaL_checkint(L, 5); - algorithm algo = A_PLAIN_NP; + PathAlgorithm algo = PA_PLAIN_NP; if (!lua_isnil(L, 6)) { std::string algorithm = luaL_checkstring(L,6); if (algorithm == "A*") - algo = A_PLAIN; + algo = PA_PLAIN; if (algorithm == "Dijkstra") - algo = DIJKSTRA; + algo = PA_DIJKSTRA; } - std::vector<v3s16> path = - get_Path(env,pos1,pos2,searchdistance,max_jump,max_drop,algo); + std::vector<v3s16> path = get_path(env, pos1, pos2, + searchdistance, max_jump, max_drop, algo); if (path.size() > 0) { @@ -920,13 +1038,6 @@ int ModApiEnvMod::l_forceload_free_block(lua_State *L) return 0; } -// get_us_time() -int ModApiEnvMod::l_get_us_time(lua_State *L) -{ - lua_pushnumber(L, porting::getTimeUs()); - return 1; -} - void ModApiEnvMod::Initialize(lua_State *L, int top) { API_FCT(set_node); @@ -953,9 +1064,11 @@ void ModApiEnvMod::Initialize(lua_State *L, int top) 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(emerge_area); API_FCT(delete_area); API_FCT(get_perlin); API_FCT(get_perlin_map); @@ -967,5 +1080,4 @@ void ModApiEnvMod::Initialize(lua_State *L, int top) API_FCT(transforming_liquid_add); API_FCT(forceload_block); API_FCT(forceload_free_block); - API_FCT(get_us_time); } diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 0d4ca788e..89dd7978f 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -113,6 +113,9 @@ private: // 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) -> pos or nil // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" static int l_find_node_near(lua_State *L); @@ -125,6 +128,9 @@ private: // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" static int l_find_nodes_in_area_under_air(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); @@ -165,15 +171,13 @@ private: // stops forceloading a position static int l_forceload_free_block(lua_State *L); - // get us precision time - static int l_get_us_time(lua_State *L); - public: static void Initialize(lua_State *L, int top); + + static struct EnumString es_ClearObjectsMode[]; }; -class LuaABM : public ActiveBlockModifier -{ +class LuaABM : public ActiveBlockModifier { private: int m_id; @@ -181,16 +185,18 @@ private: std::set<std::string> 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::set<std::string> &trigger_contents, const std::set<std::string> &required_neighbors, - float trigger_interval, u32 trigger_chance): + 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_trigger_chance(trigger_chance), + m_simple_catch_up(simple_catch_up) { } virtual std::set<std::string> getTriggerContents() @@ -209,8 +215,38 @@ public: { 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<std::string> &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); +}; + +struct ScriptCallbackState { + GameScripting *script; + int callback_ref; + int args_ref; + unsigned int refcount; + std::string origin; +}; + #endif /* L_ENV_H_ */ diff --git a/src/script/lua_api/l_http.cpp b/src/script/lua_api/l_http.cpp new file mode 100644 index 000000000..8bd39b6ed --- /dev/null +++ b/src/script/lua_api/l_http.cpp @@ -0,0 +1,193 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +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 <algorithm> +#include <iomanip> +#include <cctype> + +#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; + + // post_data: if table, post form data, otherwise raw data + lua_getfield(L, 1, "post_data"); + if (lua_istable(L, 2)) { + lua_pushnil(L); + while (lua_next(L, 2) != 0) + { + req.post_fields[luaL_checkstring(L, -2)] = luaL_checkstring(L, -1); + lua_pop(L, 1); + } + } else if (lua_isstring(L, 2)) { + req.post_data = lua_tostring(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) + { + const char *header = luaL_checkstring(L, -1); + req.extra_headers.push_back(header); + 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.c_str()); +} + +// 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); + + actionstream << "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; + } + + const char *mod_name = lua_tostring(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<std::string> 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<std::string> 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: + // <core.http_add_fetch> <table with fetch_async, fetch_async_get> + // Now call core.http_add_fetch to append .fetch(request, callback) to table + lua_call(L, 1, 1); + + return 1; +} +#endif + +void ModApiHttp::Initialize(lua_State *L, int top) +{ +#if USE_CURL + API_FCT(request_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..077ade691 --- /dev/null +++ b/src/script/lua_api/l_http.h @@ -0,0 +1,50 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +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. +*/ + +#ifndef L_HTTP_H_ +#define L_HTTP_H_ + +#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_async({url=, timeout=, post_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); +#endif + +public: + static void Initialize(lua_State *L, int top); +}; + +#endif /* L_HTTP_H_ */ diff --git a/src/script/lua_api/l_internal.h b/src/script/lua_api/l_internal.h index 1e40c5c4a..456c8fcce 100644 --- a/src/script/lua_api/l_internal.h +++ b/src/script/lua_api/l_internal.h @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define API_FCT(name) registerFunction(L, #name, l_##name,top) #define ASYNC_API_FCT(name) engine.registerFunction(#name, l_##name) +#define MAP_LOCK_REQUIRED #define NO_MAP_LOCK_REQUIRED /* @@ -45,4 +46,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif */ +#define GET_ENV_PTR_NO_MAP_LOCK \ + ServerEnvironment *env = (ServerEnvironment *)getEnv(L); \ + if (env == NULL) \ + return 0 + +#define GET_ENV_PTR \ + MAP_LOCK_REQUIRED; \ + GET_ENV_PTR_NO_MAP_LOCK + #endif /* L_INTERNAL_H_ */ diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp index f48f6083b..de9f9374a 100644 --- a/src/script/lua_api/l_inventory.cpp +++ b/src/script/lua_api/l_inventory.cpp @@ -491,6 +491,7 @@ int ModApiInventory::l_get_inventory(lua_State *L) std::string type = checkstringfield(L, 1, "type"); if(type == "node"){ + MAP_LOCK_REQUIRED; lua_getfield(L, 1, "pos"); v3s16 pos = check_v3s16(L, -1); loc.setNodeMeta(pos); @@ -514,7 +515,7 @@ int ModApiInventory::l_get_inventory(lua_State *L) InvRef::create(L, loc); else lua_pushnil(L); - return 1; + return 1; // END NO_MAP_LOCK_REQUIRED; } } diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index 842b15709..5381cba76 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -94,7 +94,7 @@ int LuaItemStack::l_set_count(lua_State *L) bool status; lua_Integer count = luaL_checkinteger(L, 2); - if (count <= 65535) { + if (count > 0 && count <= 65535) { item.count = count; status = true; } else { diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 92311d6fc..7b29db159 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -706,16 +706,13 @@ int ModApiMainMenu::l_set_topleft_text(lua_State *L) /******************************************************************************/ int ModApiMainMenu::l_get_mapgen_names(lua_State *L) { - lua_newtable(L); - - std::list<const char *> names; - EmergeManager::getMapgenNames(names); + std::vector<const char *> names; + EmergeManager::getMapgenNames(&names, lua_toboolean(L, 1)); - int i = 1; - for (std::list<const char *>::const_iterator - it = names.begin(); it != names.end(); ++it) { - lua_pushstring(L, *it); - lua_rawseti(L, -2, i++); + 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; @@ -725,8 +722,8 @@ int ModApiMainMenu::l_get_mapgen_names(lua_State *L) /******************************************************************************/ int ModApiMainMenu::l_get_modpath(lua_State *L) { - std::string modpath - = fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "mods" + DIR_DELIM); + std::string modpath = fs::RemoveRelativePathComponents( + porting::path_user + DIR_DELIM + "mods" + DIR_DELIM); lua_pushstring(L, modpath.c_str()); return 1; } @@ -734,8 +731,8 @@ int ModApiMainMenu::l_get_modpath(lua_State *L) /******************************************************************************/ int ModApiMainMenu::l_get_gamepath(lua_State *L) { - std::string gamepath - = fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "games" + DIR_DELIM); + std::string gamepath = fs::RemoveRelativePathComponents( + porting::path_user + DIR_DELIM + "games" + DIR_DELIM); lua_pushstring(L, gamepath.c_str()); return 1; } @@ -743,44 +740,46 @@ int ModApiMainMenu::l_get_gamepath(lua_State *L) /******************************************************************************/ int ModApiMainMenu::l_get_texturepath(lua_State *L) { - std::string gamepath - = fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "textures"); + 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"); + std::string gamepath = fs::RemoveRelativePathComponents( + porting::path_share + DIR_DELIM + "textures"); lua_pushstring(L, gamepath.c_str()); return 1; } /******************************************************************************/ int ModApiMainMenu::l_create_dir(lua_State *L) { - const char *path = luaL_checkstring(L, 1); + const char *path = luaL_checkstring(L, 1); if (ModApiMainMenu::isMinetestPath(path)) { - lua_pushboolean(L,fs::CreateAllDirs(path)); + lua_pushboolean(L, fs::CreateAllDirs(path)); return 1; } - lua_pushboolean(L,false); + + lua_pushboolean(L, false); return 1; } /******************************************************************************/ int ModApiMainMenu::l_delete_dir(lua_State *L) { - const char *path = luaL_checkstring(L, 1); + const char *path = luaL_checkstring(L, 1); std::string absolute_path = fs::RemoveRelativePathComponents(path); if (ModApiMainMenu::isMinetestPath(absolute_path)) { - lua_pushboolean(L,fs::RecursiveDelete(absolute_path)); + lua_pushboolean(L, fs::RecursiveDelete(absolute_path)); return 1; } - lua_pushboolean(L,false); + + lua_pushboolean(L, false); return 1; } @@ -1060,8 +1059,8 @@ int ModApiMainMenu::l_get_video_modes(lua_State *L) /******************************************************************************/ int ModApiMainMenu::l_gettext(lua_State *L) { - std::wstring wtext = wstrgettext((std::string) luaL_checkstring(L, 1)); - lua_pushstring(L, wide_to_utf8(wtext).c_str()); + std::string text = strgettext(std::string(luaL_checkstring(L, 1))); + lua_pushstring(L, text.c_str()); return 1; } @@ -1096,7 +1095,9 @@ int ModApiMainMenu::l_get_screen_info(lua_State *L) /******************************************************************************/ int ModApiMainMenu::l_get_min_supp_proto(lua_State *L) { - lua_pushinteger(L, CLIENT_PROTOCOL_VERSION_MIN); + u16 proto_version_min = g_settings->getFlag("send_pre_v25_init") ? + CLIENT_PROTOCOL_VERSION_MIN_LEGACY : CLIENT_PROTOCOL_VERSION_MIN; + lua_pushinteger(L, proto_version_min); return 1; } diff --git a/src/script/lua_api/l_mainmenu.h b/src/script/lua_api/l_mainmenu.h index 9c1fed272..405af25e8 100644 --- a/src/script/lua_api/l_mainmenu.h +++ b/src/script/lua_api/l_mainmenu.h @@ -119,8 +119,6 @@ private: static int l_get_texturepath_share(lua_State *L); - static int l_get_dirlist(lua_State *L); - static int l_create_dir(lua_State *L); static int l_delete_dir(lua_State *L); diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index d30b68054..fb839176b 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -70,6 +70,7 @@ struct EnumString ModApiMapgen::es_OreType[] = { {ORE_SCATTER, "scatter"}, {ORE_SHEET, "sheet"}, + {ORE_PUFF, "puff"}, {ORE_BLOB, "blob"}, {ORE_VEIN, "vein"}, {0, NULL}, @@ -449,10 +450,38 @@ size_t get_biome_list(lua_State *L, int index, /////////////////////////////////////////////////////////////////////////////// +// get_biome_id(biomename) +// returns the biome id used in biomemap +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; + + BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; + + if (!bmgr) + return 0; + + Biome *biome = (Biome *)bmgr->getByName(biome_str); + + if (!biome || biome->index == OBJDEF_INVALID_INDEX) + return 0; + + lua_pushinteger(L, biome->index); + + 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; @@ -464,7 +493,7 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L) EmergeManager *emerge = getServer(L)->getEmergeManager(); Mapgen *mg = emerge->getCurrentMapgen(); if (!mg) - return 0; + throw LuaError("Must only be called in a mapgen thread!"); size_t maplen = mg->csize.X * mg->csize.Z; @@ -563,6 +592,8 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L) int ModApiMapgen::l_get_mapgen_params(lua_State *L) { + NO_MAP_LOCK_REQUIRED; + MapgenParams *params = &getServer(L)->getEmergeManager()->params; lua_newtable(L); @@ -579,7 +610,7 @@ int ModApiMapgen::l_get_mapgen_params(lua_State *L) lua_pushinteger(L, params->chunksize); lua_setfield(L, -2, "chunksize"); - std::string flagstr = writeFlagString(params->flags, flagdesc_mapgen, (u32)-1); + std::string flagstr = writeFlagString(params->flags, flagdesc_mapgen, U32_MAX); lua_pushstring(L, flagstr.c_str()); lua_setfield(L, -2, "flags"); @@ -591,10 +622,16 @@ int ModApiMapgen::l_get_mapgen_params(lua_State *L) // set mapgen parameters int ModApiMapgen::l_set_mapgen_params(lua_State *L) { + NO_MAP_LOCK_REQUIRED; + if (!lua_istable(L, 1)) return 0; - MapgenParams *params = &getServer(L)->getEmergeManager()->params; + EmergeManager *emerge = getServer(L)->getEmergeManager(); + if (emerge->isRunning()) + throw LuaError("Cannot set parameters while mapgen is running"); + + MapgenParams *params = &emerge->params; u32 flags = 0, flagmask = 0; lua_getfield(L, 1, "mgname"); @@ -612,6 +649,10 @@ int ModApiMapgen::l_set_mapgen_params(lua_State *L) if (lua_isnumber(L, -1)) params->water_level = lua_tointeger(L, -1); + lua_getfield(L, 1, "chunksize"); + if (lua_isnumber(L, -1)) + params->chunksize = lua_tointeger(L, -1); + warn_if_field_exists(L, 1, "flagmask", "Deprecated: flags field now includes unset flags."); lua_getfield(L, 1, "flagmask"); @@ -631,6 +672,8 @@ int ModApiMapgen::l_set_mapgen_params(lua_State *L) // 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; @@ -648,6 +691,8 @@ int ModApiMapgen::l_set_noiseparams(lua_State *L) // get_noiseparams(name) int ModApiMapgen::l_get_noiseparams(lua_State *L) { + NO_MAP_LOCK_REQUIRED; + std::string name = luaL_checkstring(L, 1); NoiseParams np; @@ -662,6 +707,8 @@ int ModApiMapgen::l_get_noiseparams(lua_State *L) // 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(); @@ -686,6 +733,8 @@ int ModApiMapgen::l_set_gen_notify(lua_State *L) // 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); @@ -705,6 +754,8 @@ int ModApiMapgen::l_get_gen_notify(lua_State *L) // 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); @@ -729,6 +780,8 @@ int ModApiMapgen::l_register_biome(lua_State *L) // 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); @@ -869,6 +922,8 @@ bool read_deco_schematic(lua_State *L, SchematicManager *schemmgr, DecoSchematic // 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); @@ -880,7 +935,7 @@ int ModApiMapgen::l_register_ore(lua_State *L) "ore_type", es_OreType, ORE_SCATTER); Ore *ore = oremgr->create(oretype); if (!ore) { - errorstream << "register_ore: ore_type " << oretype << " not implemented"; + errorstream << "register_ore: ore_type " << oretype << " not implemented\n"; return 0; } @@ -889,10 +944,19 @@ int ModApiMapgen::l_register_ore(lua_State *L) 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->nthresh = getfloatfield_default(L, index, "noise_threshhold", 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\"."); @@ -937,10 +1001,43 @@ int ModApiMapgen::l_register_ore(lua_State *L) } lua_pop(L, 1); - if (oretype == ORE_VEIN) { - OreVein *orevein = (OreVein *)ore; - orevein->random_factor = getfloatfield_default(L, index, - "random_factor", 1.f); + //// 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; + } + default: + break; } ObjDefHandle handle = oremgr->add(ore); @@ -964,6 +1061,8 @@ int ModApiMapgen::l_register_ore(lua_State *L) // register_schematic({schematic}, replacements={}) int ModApiMapgen::l_register_schematic(lua_State *L) { + NO_MAP_LOCK_REQUIRED; + SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; StringMap replace_names; @@ -989,6 +1088,8 @@ int ModApiMapgen::l_register_schematic(lua_State *L) // clear_registered_biomes() int ModApiMapgen::l_clear_registered_biomes(lua_State *L) { + NO_MAP_LOCK_REQUIRED; + BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; bmgr->clear(); return 0; @@ -998,6 +1099,8 @@ int ModApiMapgen::l_clear_registered_biomes(lua_State *L) // clear_registered_decorations() int ModApiMapgen::l_clear_registered_decorations(lua_State *L) { + NO_MAP_LOCK_REQUIRED; + DecorationManager *dmgr = getServer(L)->getEmergeManager()->decomgr; dmgr->clear(); return 0; @@ -1007,6 +1110,8 @@ int ModApiMapgen::l_clear_registered_decorations(lua_State *L) // clear_registered_ores() int ModApiMapgen::l_clear_registered_ores(lua_State *L) { + NO_MAP_LOCK_REQUIRED; + OreManager *omgr = getServer(L)->getEmergeManager()->oremgr; omgr->clear(); return 0; @@ -1016,6 +1121,8 @@ int ModApiMapgen::l_clear_registered_ores(lua_State *L) // clear_registered_schematics() int ModApiMapgen::l_clear_registered_schematics(lua_State *L) { + NO_MAP_LOCK_REQUIRED; + SchematicManager *smgr = getServer(L)->getEmergeManager()->schemmgr; smgr->clear(); return 0; @@ -1025,6 +1132,8 @@ int ModApiMapgen::l_clear_registered_schematics(lua_State *L) // 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; @@ -1049,6 +1158,8 @@ int ModApiMapgen::l_generate_ores(lua_State *L) // 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; @@ -1073,6 +1184,8 @@ int ModApiMapgen::l_generate_decorations(lua_State *L) // create_schematic(p1, p2, probability_list, filename, y_slice_prob_list) int ModApiMapgen::l_create_schematic(lua_State *L) { + MAP_LOCK_REQUIRED; + INodeDefManager *ndef = getServer(L)->getNodeDefManager(); const char *filename = luaL_checkstring(L, 4); @@ -1136,6 +1249,8 @@ int ModApiMapgen::l_create_schematic(lua_State *L) // place_schematic(p, schematic, rotation, replacement) int ModApiMapgen::l_place_schematic(lua_State *L) { + MAP_LOCK_REQUIRED; + Map *map = &(getEnv(L)->getMap()); SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; @@ -1165,15 +1280,59 @@ int ModApiMapgen::l_place_schematic(lua_State *L) return 0; } - schem->placeStructure(map, p, 0, (Rotation)rot, force_placement); + schem->placeOnMap(map, p, 0, (Rotation)rot, force_placement); lua_pushboolean(L, true); return 1; } +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; + const char *enumstr = lua_tostring(L, 4); + if (enumstr) + string_to_enum(es_Rotation, rot, std::string(enumstr)); + + //// Read force placement + bool force_placement = true; + if (lua_isboolean(L, 6)) + force_placement = lua_toboolean(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; + } + + bool schematic_did_fit = schem->placeOnVManip( + vm, p, 0, (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; + SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; //// Read options @@ -1223,6 +1382,7 @@ int ModApiMapgen::l_serialize_schematic(lua_State *L) void ModApiMapgen::Initialize(lua_State *L, int top) { + API_FCT(get_biome_id); API_FCT(get_mapgen_object); API_FCT(get_mapgen_params); @@ -1246,5 +1406,6 @@ void ModApiMapgen::Initialize(lua_State *L, int top) API_FCT(generate_decorations); API_FCT(create_schematic); API_FCT(place_schematic); + API_FCT(place_schematic_on_vmanip); API_FCT(serialize_schematic); } diff --git a/src/script/lua_api/l_mapgen.h b/src/script/lua_api/l_mapgen.h index 7440d1285..9751c0db6 100644 --- a/src/script/lua_api/l_mapgen.h +++ b/src/script/lua_api/l_mapgen.h @@ -24,6 +24,10 @@ with this program; if not, write to the Free Software Foundation, Inc., class ModApiMapgen : public ModApiBase { private: + // get_biome_id(biomename) + // returns the biome id used in biomemap + static int l_get_biome_id(lua_State *L); + // get_mapgen_object(objectname) // returns the requested object used during map generation static int l_get_mapgen_object(lua_State *L); @@ -81,9 +85,13 @@ private: // create_schematic(p1, p2, probability_list, filename) static int l_create_schematic(lua_State *L); - // place_schematic(p, schematic, rotation, replacement) + // place_schematic(p, schematic, rotation, replacements, force_placement) static int l_place_schematic(lua_State *L); + // place_schematic_on_vmanip(vm, p, schematic, + // rotation, replacements, force_placement) + static int l_place_schematic_on_vmanip(lua_State *L); + // serialize_schematic(schematic, format, options={...}) static int l_serialize_schematic(lua_State *L); diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 6cdbe5c68..c8bc7d558 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_content.h" #include "environment.h" #include "map.h" +#include "gamedef.h" #include "nodemetadata.h" @@ -43,7 +44,7 @@ NodeMetadata* NodeMetaRef::getmeta(NodeMetaRef *ref, bool auto_create) { NodeMetadata *meta = ref->m_env->getMap().getNodeMetadata(ref->m_p); if(meta == NULL && auto_create) { - meta = new NodeMetadata(ref->m_env->getGameDef()); + meta = new NodeMetadata(ref->m_env->getGameDef()->idef()); if(!ref->m_env->getMap().setNodeMetadata(ref->m_p, meta)) { delete meta; return NULL; @@ -81,6 +82,8 @@ int NodeMetaRef::gc_object(lua_State *L) { // get_string(self, name) int NodeMetaRef::l_get_string(lua_State *L) { + MAP_LOCK_REQUIRED; + NodeMetaRef *ref = checkobject(L, 1); std::string name = luaL_checkstring(L, 2); @@ -97,6 +100,8 @@ int NodeMetaRef::l_get_string(lua_State *L) // set_string(self, name, var) int NodeMetaRef::l_set_string(lua_State *L) { + MAP_LOCK_REQUIRED; + NodeMetaRef *ref = checkobject(L, 1); std::string name = luaL_checkstring(L, 2); size_t len = 0; @@ -114,6 +119,8 @@ int NodeMetaRef::l_set_string(lua_State *L) // get_int(self, name) int NodeMetaRef::l_get_int(lua_State *L) { + MAP_LOCK_REQUIRED; + NodeMetaRef *ref = checkobject(L, 1); std::string name = lua_tostring(L, 2); @@ -130,6 +137,8 @@ int NodeMetaRef::l_get_int(lua_State *L) // set_int(self, name, var) int NodeMetaRef::l_set_int(lua_State *L) { + MAP_LOCK_REQUIRED; + NodeMetaRef *ref = checkobject(L, 1); std::string name = lua_tostring(L, 2); int a = lua_tointeger(L, 3); @@ -146,6 +155,8 @@ int NodeMetaRef::l_set_int(lua_State *L) // get_float(self, name) int NodeMetaRef::l_get_float(lua_State *L) { + MAP_LOCK_REQUIRED; + NodeMetaRef *ref = checkobject(L, 1); std::string name = lua_tostring(L, 2); @@ -162,6 +173,8 @@ int NodeMetaRef::l_get_float(lua_State *L) // set_float(self, name, var) int NodeMetaRef::l_set_float(lua_State *L) { + MAP_LOCK_REQUIRED; + NodeMetaRef *ref = checkobject(L, 1); std::string name = lua_tostring(L, 2); float a = lua_tonumber(L, 3); @@ -178,6 +191,8 @@ int NodeMetaRef::l_set_float(lua_State *L) // get_inventory(self) int NodeMetaRef::l_get_inventory(lua_State *L) { + MAP_LOCK_REQUIRED; + NodeMetaRef *ref = checkobject(L, 1); getmeta(ref, true); // try to ensure the metadata exists InvRef::createNodeMeta(L, ref->m_p); @@ -187,6 +202,8 @@ int NodeMetaRef::l_get_inventory(lua_State *L) // to_table(self) int NodeMetaRef::l_to_table(lua_State *L) { + MAP_LOCK_REQUIRED; + NodeMetaRef *ref = checkobject(L, 1); NodeMetadata *meta = getmeta(ref, true); @@ -229,6 +246,8 @@ int NodeMetaRef::l_to_table(lua_State *L) // from_table(self, table) int NodeMetaRef::l_from_table(lua_State *L) { + MAP_LOCK_REQUIRED; + NodeMetaRef *ref = checkobject(L, 1); int base = 2; diff --git a/src/script/lua_api/l_nodetimer.cpp b/src/script/lua_api/l_nodetimer.cpp index c81a7ebc9..601113516 100644 --- a/src/script/lua_api/l_nodetimer.cpp +++ b/src/script/lua_api/l_nodetimer.cpp @@ -39,6 +39,7 @@ NodeTimerRef* NodeTimerRef::checkobject(lua_State *L, int narg) int NodeTimerRef::l_set(lua_State *L) { + MAP_LOCK_REQUIRED; NodeTimerRef *o = checkobject(L, 1); ServerEnvironment *env = o->m_env; if(env == NULL) return 0; @@ -50,6 +51,7 @@ int NodeTimerRef::l_set(lua_State *L) int NodeTimerRef::l_start(lua_State *L) { + MAP_LOCK_REQUIRED; NodeTimerRef *o = checkobject(L, 1); ServerEnvironment *env = o->m_env; if(env == NULL) return 0; @@ -60,6 +62,7 @@ int NodeTimerRef::l_start(lua_State *L) int NodeTimerRef::l_stop(lua_State *L) { + MAP_LOCK_REQUIRED; NodeTimerRef *o = checkobject(L, 1); ServerEnvironment *env = o->m_env; if(env == NULL) return 0; @@ -69,6 +72,7 @@ int NodeTimerRef::l_stop(lua_State *L) int NodeTimerRef::l_is_started(lua_State *L) { + MAP_LOCK_REQUIRED; NodeTimerRef *o = checkobject(L, 1); ServerEnvironment *env = o->m_env; if(env == NULL) return 0; @@ -80,6 +84,7 @@ int NodeTimerRef::l_is_started(lua_State *L) int NodeTimerRef::l_get_timeout(lua_State *L) { + MAP_LOCK_REQUIRED; NodeTimerRef *o = checkobject(L, 1); ServerEnvironment *env = o->m_env; if(env == NULL) return 0; @@ -91,6 +96,7 @@ int NodeTimerRef::l_get_timeout(lua_State *L) int NodeTimerRef::l_get_elapsed(lua_State *L) { + MAP_LOCK_REQUIRED; NodeTimerRef *o = checkobject(L, 1); ServerEnvironment *env = o->m_env; if(env == NULL) return 0; diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp index c8dc2d2dc..04dc6048f 100644 --- a/src/script/lua_api/l_noise.cpp +++ b/src/script/lua_api/l_noise.cpp @@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "common/c_content.h" #include "log.h" +#include "porting.h" +#include "util/numeric.h" /////////////////////////////////////// /* @@ -410,6 +412,7 @@ const luaL_reg LuaPerlinNoiseMap::methods[] = { int LuaPseudoRandom::l_next(lua_State *L) { NO_MAP_LOCK_REQUIRED; + LuaPseudoRandom *o = checkobject(L, 1); int min = 0; int max = 32767; @@ -437,7 +440,9 @@ int LuaPseudoRandom::l_next(lua_State *L) int LuaPseudoRandom::create_object(lua_State *L) { - int seed = luaL_checknumber(L, 1); + 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); @@ -532,8 +537,10 @@ int LuaPcgRandom::l_rand_normal_dist(lua_State *L) int LuaPcgRandom::create_object(lua_State *L) { - lua_Integer seed = luaL_checknumber(L, 1); - LuaPcgRandom *o = lua_isnumber(L, 2) ? + 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; @@ -595,3 +602,116 @@ const luaL_reg LuaPcgRandom::methods[] = { 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 index e958c5a23..492eb7550 100644 --- a/src/script/lua_api/l_noise.h +++ b/src/script/lua_api/l_noise.h @@ -160,4 +160,37 @@ public: 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); +}; + #endif /* L_NOISE_H_ */ diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 3ac8eeefb..6d6614e7d 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -31,10 +31,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "hud.h" #include "scripting_game.h" -#define GET_ENV_PTR ServerEnvironment* env = \ - dynamic_cast<ServerEnvironment*>(getEnv(L)); \ - if (env == NULL) return 0 - struct EnumString es_HudElementType[] = { {HUD_ELEM_IMAGE, "image"}, @@ -132,7 +128,6 @@ int ObjectRef::gc_object(lua_State *L) { // remove(self) int ObjectRef::l_remove(lua_State *L) { - NO_MAP_LOCK_REQUIRED; GET_ENV_PTR; ObjectRef *ref = checkobject(L, 1); @@ -409,6 +404,7 @@ int ObjectRef::l_get_armor_groups(lua_State *L) // physics_override_gravity, sneak, sneak_glitch) int ObjectRef::l_set_physics_override(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); PlayerSAO *co = (PlayerSAO *) getobject(ref); if (co == NULL) return 0; @@ -441,6 +437,7 @@ int ObjectRef::l_set_physics_override(lua_State *L) // get_physics_override(self) int ObjectRef::l_get_physics_override(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); PlayerSAO *co = (PlayerSAO *)getobject(ref); if (co == NULL) @@ -509,7 +506,7 @@ int ObjectRef::l_get_animation(lua_State *L) // set_local_animation(self, {stand/idle}, {walk}, {dig}, {walk+dig}, frame_speed) int ObjectRef::l_set_local_animation(lua_State *L) { - //NO_MAP_LOCK_REQUIRED; + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -534,7 +531,7 @@ int ObjectRef::l_set_local_animation(lua_State *L) // get_local_animation(self) int ObjectRef::l_get_local_animation(lua_State *L) { - //NO_MAP_LOCK_REQUIRED + NO_MAP_LOCK_REQUIRED ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -555,7 +552,7 @@ int ObjectRef::l_get_local_animation(lua_State *L) // set_eye_offset(self, v3f first pv, v3f third pv) int ObjectRef::l_set_eye_offset(lua_State *L) { - //NO_MAP_LOCK_REQUIRED; + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -585,7 +582,7 @@ int ObjectRef::l_set_eye_offset(lua_State *L) // get_eye_offset(self) int ObjectRef::l_get_eye_offset(lua_State *L) { - //NO_MAP_LOCK_REQUIRED; + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -642,7 +639,6 @@ int ObjectRef::l_get_bone_position(lua_State *L) // set_attach(self, parent, bone, position, rotation) int ObjectRef::l_set_attach(lua_State *L) { - NO_MAP_LOCK_REQUIRED; GET_ENV_PTR; ObjectRef *ref = checkobject(L, 1); @@ -681,7 +677,6 @@ int ObjectRef::l_set_attach(lua_State *L) // get_attach(self) int ObjectRef::l_get_attach(lua_State *L) { - NO_MAP_LOCK_REQUIRED; GET_ENV_PTR; ObjectRef *ref = checkobject(L, 1); @@ -709,7 +704,6 @@ int ObjectRef::l_get_attach(lua_State *L) // set_detach(self) int ObjectRef::l_set_detach(lua_State *L) { - NO_MAP_LOCK_REQUIRED; GET_ENV_PTR; ObjectRef *ref = checkobject(L, 1); @@ -773,6 +767,59 @@ int ObjectRef::l_is_player(lua_State *L) 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 *co = getobject(ref); + + if (co == NULL) + return 0; + ObjectProperties *prop = co->accessObjectProperties(); + if (!prop) + 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", ""); + if (nametag != "") + prop->nametag = nametag; + + co->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 *co = getobject(ref); + + if (co == NULL) + return 0; + ObjectProperties *prop = co->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 */ // setvelocity(self, {x=num, y=num, z=num}) @@ -1137,6 +1184,7 @@ int ObjectRef::l_get_player_control_bits(lua_State *L) // hud_add(self, form) int ObjectRef::l_hud_add(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -1187,7 +1235,7 @@ int ObjectRef::l_hud_add(lua_State *L) } u32 id = getServer(L)->hudAdd(player, elem); - if (id == (u32)-1) { + if (id == U32_MAX) { delete elem; return 0; } @@ -1199,6 +1247,7 @@ int ObjectRef::l_hud_add(lua_State *L) // hud_remove(self, id) int ObjectRef::l_hud_remove(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -1218,6 +1267,7 @@ int ObjectRef::l_hud_remove(lua_State *L) // hud_change(self, id, stat, data) int ObjectRef::l_hud_change(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -1294,6 +1344,7 @@ int ObjectRef::l_hud_change(lua_State *L) // hud_get(self, id) int ObjectRef::l_hud_get(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -1344,6 +1395,7 @@ int ObjectRef::l_hud_get(lua_State *L) // hud_set_flags(self, flags) int ObjectRef::l_hud_set_flags(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -1369,6 +1421,7 @@ int ObjectRef::l_hud_set_flags(lua_State *L) int ObjectRef::l_hud_get_flags(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -1394,6 +1447,7 @@ int ObjectRef::l_hud_get_flags(lua_State *L) // 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); Player *player = getplayer(ref); if (player == NULL) @@ -1411,6 +1465,7 @@ int ObjectRef::l_hud_set_hotbar_itemcount(lua_State *L) // hud_get_hotbar_itemcount(self) int ObjectRef::l_hud_get_hotbar_itemcount(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -1425,6 +1480,7 @@ int ObjectRef::l_hud_get_hotbar_itemcount(lua_State *L) // 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); Player *player = getplayer(ref); if (player == NULL) @@ -1439,6 +1495,7 @@ int ObjectRef::l_hud_set_hotbar_image(lua_State *L) // hud_get_hotbar_image(self) int ObjectRef::l_hud_get_hotbar_image(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -1452,6 +1509,7 @@ int ObjectRef::l_hud_get_hotbar_image(lua_State *L) // 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); Player *player = getplayer(ref); if (player == NULL) @@ -1466,6 +1524,7 @@ int ObjectRef::l_hud_set_hotbar_selected_image(lua_State *L) // 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); Player *player = getplayer(ref); if (player == NULL) @@ -1479,6 +1538,7 @@ int ObjectRef::l_hud_get_hotbar_selected_image(lua_State *L) // set_sky(self, bgcolor, type, list) int ObjectRef::l_set_sky(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -1517,6 +1577,7 @@ int ObjectRef::l_set_sky(lua_State *L) // get_sky(self) int ObjectRef::l_get_sky(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -1544,6 +1605,7 @@ int ObjectRef::l_get_sky(lua_State *L) // override_day_night_ratio(self, brightness=0...1) int ObjectRef::l_override_day_night_ratio(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -1566,6 +1628,7 @@ int ObjectRef::l_override_day_night_ratio(lua_State *L) // get_day_night_ratio(self) int ObjectRef::l_get_day_night_ratio(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) @@ -1583,45 +1646,6 @@ int ObjectRef::l_get_day_night_ratio(lua_State *L) 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); - PlayerSAO *playersao = getplayersao(ref); - if (playersao == NULL) - return 0; - - lua_getfield(L, 2, "color"); - if (!lua_isnil(L, -1)) { - video::SColor color = playersao->getNametagColor(); - if (!read_color(L, -1, &color)) - return 0; - playersao->setNametagColor(color); - } - - 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); - PlayerSAO *playersao = getplayersao(ref); - if (playersao == NULL) - return 0; - - video::SColor color = playersao->getNametagColor(); - - lua_newtable(L); - push_ARGB8(L, color); - lua_setfield(L, -2, "color"); - - return 1; -} - ObjectRef::ObjectRef(ServerActiveObject *object): m_object(object) { @@ -1709,6 +1733,8 @@ const luaL_reg ObjectRef::methods[] = { luamethod(ObjectRef, set_detach), luamethod(ObjectRef, set_properties), luamethod(ObjectRef, get_properties), + luamethod(ObjectRef, set_nametag_attributes), + luamethod(ObjectRef, get_nametag_attributes), // LuaEntitySAO-only luamethod(ObjectRef, setvelocity), luamethod(ObjectRef, getvelocity), @@ -1758,7 +1784,5 @@ const luaL_reg ObjectRef::methods[] = { luamethod(ObjectRef, get_local_animation), luamethod(ObjectRef, set_eye_offset), luamethod(ObjectRef, get_eye_offset), - luamethod(ObjectRef, set_nametag_attributes), - luamethod(ObjectRef, get_nametag_attributes), {0,0} }; diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp index 2532b2b08..f6c1725de 100644 --- a/src/script/lua_api/l_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -32,6 +32,8 @@ with this program; if not, write to the Free Software Foundation, Inc., // texture = e.g."default_wood.png" int ModApiParticles::l_add_particle(lua_State *L) { + MAP_LOCK_REQUIRED; + // Get parameters v3f pos, vel, acc; pos = vel = acc = v3f(0, 0, 0); @@ -119,6 +121,8 @@ int ModApiParticles::l_add_particle(lua_State *L) // texture = e.g."default_wood.png" int ModApiParticles::l_add_particlespawner(lua_State *L) { + MAP_LOCK_REQUIRED; + // Get parameters u16 amount = 1; v3f minpos, maxpos, minvel, maxvel, minacc, maxacc; @@ -208,6 +212,8 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) // player (string) is optional int ModApiParticles::l_delete_particlespawner(lua_State *L) { + MAP_LOCK_REQUIRED; + // Get parameters u32 id = luaL_checknumber(L, 1); std::string playername = ""; diff --git a/src/script/lua_api/l_rollback.cpp b/src/script/lua_api/l_rollback.cpp index 5744e6813..482b0cbf5 100644 --- a/src/script/lua_api/l_rollback.cpp +++ b/src/script/lua_api/l_rollback.cpp @@ -38,6 +38,8 @@ void push_RollbackNode(lua_State *L, RollbackNode &node) // 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); @@ -79,6 +81,8 @@ int ModApiRollback::l_rollback_get_node_actions(lua_State *L) // 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); diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 73eca9d60..59d3f5c70 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., // request_shutdown() int ModApiServer::l_request_shutdown(lua_State *L) { + NO_MAP_LOCK_REQUIRED; const char *msg = lua_tolstring(L, 1, NULL); bool reconnect = lua_toboolean(L, 2); getServer(L)->requestShutdown(msg ? msg : "", reconnect); @@ -44,6 +45,16 @@ int ModApiServer::l_get_server_status(lua_State *L) 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) { @@ -110,7 +121,7 @@ int ModApiServer::l_get_player_ip(lua_State *L) } catch(con::PeerNotFoundException) // unlikely { - dstream << __FUNCTION_NAME << ": peer was not found" << std::endl; + dstream << FUNCTION_NAME << ": peer was not found" << std::endl; lua_pushnil(L); // error return 1; } @@ -136,7 +147,7 @@ int ModApiServer::l_get_player_information(lua_State *L) } catch(con::PeerNotFoundException) // unlikely { - dstream << __FUNCTION_NAME << ": peer was not found" << std::endl; + dstream << FUNCTION_NAME << ": peer was not found" << std::endl; lua_pushnil(L); // error return 1; } @@ -150,7 +161,7 @@ int ModApiServer::l_get_player_information(lua_State *L) #define ERET(code) \ if (!(code)) { \ - dstream << __FUNCTION_NAME << ": peer was not found" << std::endl; \ + dstream << FUNCTION_NAME << ": peer was not found" << std::endl; \ lua_pushnil(L); /* error */ \ return 1; \ } @@ -281,7 +292,7 @@ int ModApiServer::l_ban_player(lua_State *L) } catch(con::PeerNotFoundException) // unlikely { - dstream << __FUNCTION_NAME << ": peer was not found" << std::endl; + dstream << FUNCTION_NAME << ": peer was not found" << std::endl; lua_pushboolean(L, false); // error return 1; } @@ -345,7 +356,7 @@ int ModApiServer::l_show_formspec(lua_State *L) int ModApiServer::l_get_current_modname(lua_State *L) { NO_MAP_LOCK_REQUIRED; - lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); return 1; } @@ -442,7 +453,7 @@ int ModApiServer::l_notify_authentication_modified(lua_State *L) int ModApiServer::l_get_last_run_mod(lua_State *L) { NO_MAP_LOCK_REQUIRED; - lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); const char *current_mod = lua_tostring(L, -1); if (current_mod == NULL || current_mod[0] == '\0') { lua_pop(L, 1); @@ -504,6 +515,8 @@ void ModApiServer::Initialize(lua_State *L, int top) 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); diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h index df31f325f..06a5ddc24 100644 --- a/src/script/lua_api/l_server.h +++ b/src/script/lua_api/l_server.h @@ -46,6 +46,9 @@ private: // 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); diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index 12146e80a..c3e6c8964 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -25,9 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serialization.h" #include "json/json.h" #include "cpp_api/s_security.h" -#include "areastore.h" -#include "debug.h" #include "porting.h" +#include "debug.h" #include "log.h" #include "tool.h" #include "filesys.h" @@ -35,70 +34,49 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/auth.h" #include <algorithm> -// debug(...) -// Writes a line to dstream -int ModApiUtil::l_debug(lua_State *L) -{ - NO_MAP_LOCK_REQUIRED; - // Handle multiple parameters to behave like standard lua print() - int n = lua_gettop(L); - lua_getglobal(L, "tostring"); - for (int i = 1; i <= n; i++) { - /* - Call tostring(i-th argument). - This is what print() does, and it behaves a bit - differently from directly calling lua_tostring. - */ - lua_pushvalue(L, -1); /* function to be called */ - lua_pushvalue(L, i); /* value to print */ - lua_call(L, 1, 1); - size_t len; - const char *s = lua_tolstring(L, -1, &len); - if (i > 1) - dstream << "\t"; - if (s) - dstream << std::string(s, len); - lua_pop(L, 1); - } - dstream << std::endl; - return 0; -} - // log([level,] text) // Writes a line to the logger. // The one-argument version logs to infostream. -// The two-argument version accept a log level: error, action, info, or verbose. +// 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; - LogMessageLevel level = LMT_INFO; + LogLevel level = LL_NONE; if (lua_isnone(L, 2)) { - text = lua_tostring(L, 1); - } - else { - std::string levelname = luaL_checkstring(L, 1); + text = luaL_checkstring(L, 1); + } else { + std::string name = luaL_checkstring(L, 1); text = luaL_checkstring(L, 2); - if(levelname == "error") - level = LMT_ERROR; - else if(levelname == "action") - level = LMT_ACTION; - else if(levelname == "verbose") - level = LMT_VERBOSE; - else if (levelname == "deprecated") { - log_deprecated(L,text); + if (name == "deprecated") { + log_deprecated(L, text); 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; + } } - log_printline(level, text); + 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; +} + #define CHECK_SECURE_SETTING(L, name) \ - if (name.compare(0, 7, "secure.") == 0) {\ - lua_pushliteral(L, "Attempt to set secure setting.");\ - lua_error(L);\ + if (ScriptApiSecurity::isSecure(L) && \ + name.compare(0, 7, "secure.") == 0) { \ + throw LuaError("Attempt to set secure setting."); \ } // setting_set(name, value) @@ -183,8 +161,14 @@ int ModApiUtil::l_parse_json(lua_State *L) if (!reader.parse(stream, root)) { errorstream << "Failed to parse json data " << reader.getFormattedErrorMessages(); - errorstream << "data: \"" << jsonstr << "\"" - << 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; } @@ -267,7 +251,7 @@ 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 = translatePassword(name, raw_password); + std::string hash = translate_password(name, raw_password); lua_pushstring(L, hash.c_str()); return 1; } @@ -290,6 +274,8 @@ int ModApiUtil::l_is_yes(lua_State *L) int ModApiUtil::l_get_builtin_path(lua_State *L) { + NO_MAP_LOCK_REQUIRED; + std::string path = porting::path_share + DIR_DELIM + "builtin"; lua_pushstring(L, path.c_str()); return 1; @@ -298,6 +284,8 @@ int ModApiUtil::l_get_builtin_path(lua_State *L) // 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); @@ -317,6 +305,8 @@ int ModApiUtil::l_compress(lua_State *L) // 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); @@ -367,32 +357,57 @@ int ModApiUtil::l_get_dir_list(lua_State *L) 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; } - lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); + + // 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)) { - lua_pushnil(L); - return 1; + return 0; } + + // Check secure.trusted_mods const char *mod_name = lua_tostring(L, -1); 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<std::string> mod_list = str_split(trusted_mods, ','); - if (std::find(mod_list.begin(), mod_list.end(), mod_name) == mod_list.end()) { - lua_pushnil(L); - return 1; + if (std::find(mod_list.begin(), mod_list.end(), mod_name) == + mod_list.end()) { + return 0; } - lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup"); + + // Push insecure environment + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP); return 1; } void ModApiUtil::Initialize(lua_State *L, int top) { - API_FCT(debug); API_FCT(log); + API_FCT(get_us_time); + API_FCT(setting_set); API_FCT(setting_get); API_FCT(setting_setbool); @@ -422,9 +437,10 @@ void ModApiUtil::Initialize(lua_State *L, int top) void ModApiUtil::InitializeAsync(AsyncEngine& engine) { - ASYNC_API_FCT(debug); ASYNC_API_FCT(log); + ASYNC_API_FCT(get_us_time); + //ASYNC_API_FCT(setting_set); ASYNC_API_FCT(setting_get); //ASYNC_API_FCT(setting_setbool); diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h index e75aa28cb..6fac7e7eb 100644 --- a/src/script/lua_api/l_util.h +++ b/src/script/lua_api/l_util.h @@ -35,16 +35,15 @@ private: GUIEngine instance should be in here. */ - // debug(text) - // Writes a line to dstream - static int l_debug(lua_State *L); - // log([level,] text) // Writes a line to the logger. // The one-argument version logs to infostream. - // The two-argument version accept a log level: error, action, info, or verbose. + // 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); + // setting_set(name, value) static int l_setting_set(lua_State *L); diff --git a/src/script/lua_api/l_vmanip.cpp b/src/script/lua_api/l_vmanip.cpp index ac6c10303..f13866408 100644 --- a/src/script/lua_api/l_vmanip.cpp +++ b/src/script/lua_api/l_vmanip.cpp @@ -28,10 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "mapgen.h" -#define GET_ENV_PTR ServerEnvironment* env = \ - dynamic_cast<ServerEnvironment*>(getEnv(L)); \ - if (env == NULL) return 0 - // garbage collector int LuaVoxelManip::gc_object(lua_State *L) { @@ -43,6 +39,8 @@ int LuaVoxelManip::gc_object(lua_State *L) int LuaVoxelManip::l_read_from_map(lua_State *L) { + MAP_LOCK_REQUIRED; + LuaVoxelManip *o = checkobject(L, 1); MMVManip *vm = o->vm; @@ -108,6 +106,8 @@ int LuaVoxelManip::l_set_data(lua_State *L) int LuaVoxelManip::l_write_to_map(lua_State *L) { + MAP_LOCK_REQUIRED; + LuaVoxelManip *o = checkobject(L, 1); MMVManip *vm = o->vm; @@ -119,23 +119,25 @@ int LuaVoxelManip::l_write_to_map(lua_State *L) int LuaVoxelManip::l_get_node_at(lua_State *L) { NO_MAP_LOCK_REQUIRED; - GET_ENV_PTR; + + INodeDefManager *ndef = getServer(L)->getNodeDefManager(); LuaVoxelManip *o = checkobject(L, 1); v3s16 pos = check_v3s16(L, 2); - pushnode(L, o->vm->getNodeNoExNoEmerge(pos), env->getGameDef()->ndef()); + pushnode(L, o->vm->getNodeNoExNoEmerge(pos), ndef); return 1; } int LuaVoxelManip::l_set_node_at(lua_State *L) { NO_MAP_LOCK_REQUIRED; - GET_ENV_PTR; + + INodeDefManager *ndef = getServer(L)->getNodeDefManager(); LuaVoxelManip *o = checkobject(L, 1); v3s16 pos = check_v3s16(L, 2); - MapNode n = readnode(L, 3, env->getGameDef()->ndef()); + MapNode n = readnode(L, 3, ndef); o->vm->setNodeNoEmerge(pos, n); @@ -179,6 +181,7 @@ int LuaVoxelManip::l_calc_lighting(lua_State *L) 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) ? lua_toboolean(L, 4) : true; sortBoxVerticies(pmin, pmax); if (!vm->m_area.contains(VoxelArea(pmin, pmax))) @@ -189,7 +192,7 @@ int LuaVoxelManip::l_calc_lighting(lua_State *L) mg.ndef = ndef; mg.water_level = emerge->params.water_level; - mg.calcLighting(pmin, pmax, fpmin, fpmax); + mg.calcLighting(pmin, pmax, fpmin, fpmax, propagate_shadow); return 0; } @@ -313,14 +316,12 @@ int LuaVoxelManip::l_set_param2_data(lua_State *L) int LuaVoxelManip::l_update_map(lua_State *L) { + GET_ENV_PTR; + LuaVoxelManip *o = checkobject(L, 1); if (o->is_mapgen_vm) return 0; - Environment *env = getEnv(L); - if (!env) - return 0; - Map *map = &(env->getMap()); // TODO: Optimize this by using Mapgen::calcLighting() instead @@ -359,6 +360,8 @@ int LuaVoxelManip::l_was_modified(lua_State *L) 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); @@ -400,11 +403,7 @@ LuaVoxelManip::~LuaVoxelManip() // Creates an LuaVoxelManip and leaves it on top of stack int LuaVoxelManip::create_object(lua_State *L) { - NO_MAP_LOCK_REQUIRED; - - Environment *env = getEnv(L); - if (!env) - return 0; + GET_ENV_PTR; Map *map = &(env->getMap()); LuaVoxelManip *o = (lua_istable(L, 1) && lua_istable(L, 2)) ? diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp index 4f0350d41..e313d55f8 100644 --- a/src/script/scripting_game.cpp +++ b/src/script/scripting_game.cpp @@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_util.h" #include "lua_api/l_vmanip.h" #include "lua_api/l_settings.h" +#include "lua_api/l_http.h" extern "C" { #include "lualib.h" @@ -89,6 +90,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top) ModApiRollback::Initialize(L, top); ModApiServer::Initialize(L, top); ModApiUtil::Initialize(L, top); + ModApiHttp::Initialize(L, top); // Register reference classes (userdata) InvRef::Register(L); @@ -98,6 +100,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top) LuaPerlinNoiseMap::Register(L); LuaPseudoRandom::Register(L); LuaPcgRandom::Register(L); + LuaSecureRandom::Register(L); LuaVoxelManip::Register(L); NodeMetaRef::Register(L); NodeTimerRef::Register(L); diff --git a/src/script/scripting_mainmenu.cpp b/src/script/scripting_mainmenu.cpp index c74c18edc..b1e50c94b 100644 --- a/src/script/scripting_mainmenu.cpp +++ b/src/script/scripting_mainmenu.cpp @@ -78,7 +78,7 @@ void MainMenuScripting::initializeModApi(lua_State *L, int top) /******************************************************************************/ void MainMenuScripting::step() { - asyncEngine.step(getStack(), m_errorhandler); + asyncEngine.step(getStack()); } /******************************************************************************/ |