summaryrefslogtreecommitdiff
path: root/src/script/cpp_api
diff options
context:
space:
mode:
Diffstat (limited to 'src/script/cpp_api')
-rw-r--r--src/script/cpp_api/s_async.cpp108
-rw-r--r--src/script/cpp_api/s_async.h39
-rw-r--r--src/script/cpp_api/s_base.cpp11
-rw-r--r--src/script/cpp_api/s_base.h17
-rw-r--r--src/script/cpp_api/s_client.cpp88
-rw-r--r--src/script/cpp_api/s_entity.cpp2
-rw-r--r--src/script/cpp_api/s_entity.h2
-rw-r--r--src/script/cpp_api/s_env.cpp63
-rw-r--r--src/script/cpp_api/s_env.h5
-rw-r--r--src/script/cpp_api/s_item.cpp36
-rw-r--r--src/script/cpp_api/s_item.h14
-rw-r--r--src/script/cpp_api/s_mainmenu.h2
-rw-r--r--src/script/cpp_api/s_node.cpp1
-rw-r--r--src/script/cpp_api/s_node.h1
-rw-r--r--src/script/cpp_api/s_nodemeta.cpp18
-rw-r--r--src/script/cpp_api/s_player.cpp2
-rw-r--r--src/script/cpp_api/s_player.h2
-rw-r--r--src/script/cpp_api/s_security.cpp131
-rw-r--r--src/script/cpp_api/s_security.h15
-rw-r--r--src/script/cpp_api/s_server.cpp64
-rw-r--r--src/script/cpp_api/s_server.h6
21 files changed, 460 insertions, 167 deletions
diff --git a/src/script/cpp_api/s_async.cpp b/src/script/cpp_api/s_async.cpp
index 0619b32c0..dacdcd75a 100644
--- a/src/script/cpp_api/s_async.cpp
+++ b/src/script/cpp_api/s_async.cpp
@@ -32,20 +32,19 @@ extern "C" {
#include "filesys.h"
#include "porting.h"
#include "common/c_internal.h"
+#include "lua_api/l_base.h"
/******************************************************************************/
AsyncEngine::~AsyncEngine()
{
-
// Request all threads to stop
for (AsyncWorkerThread *workerThread : workerThreads) {
workerThread->stop();
}
-
// Wake up all threads
- for (std::vector<AsyncWorkerThread *>::iterator it = workerThreads.begin();
- it != workerThreads.end(); ++it) {
+ for (auto it : workerThreads) {
+ (void)it;
jobQueueCounter.post();
}
@@ -68,6 +67,7 @@ AsyncEngine::~AsyncEngine()
/******************************************************************************/
void AsyncEngine::registerStateInitializer(StateInitializer func)
{
+ FATAL_ERROR_IF(initDone, "Initializer may not be registered after init");
stateInitializers.push_back(func);
}
@@ -85,36 +85,36 @@ void AsyncEngine::initialize(unsigned int numEngines)
}
/******************************************************************************/
-unsigned int AsyncEngine::queueAsyncJob(const std::string &func,
- const std::string &params)
+u32 AsyncEngine::queueAsyncJob(std::string &&func, std::string &&params,
+ const std::string &mod_origin)
{
jobQueueMutex.lock();
- LuaJobInfo toAdd;
- toAdd.id = jobIdCounter++;
- toAdd.serializedFunction = func;
- toAdd.serializedParams = params;
+ u32 jobId = jobIdCounter++;
- jobQueue.push_back(toAdd);
+ jobQueue.emplace_back();
+ auto &to_add = jobQueue.back();
+ to_add.id = jobId;
+ to_add.function = std::move(func);
+ to_add.params = std::move(params);
+ to_add.mod_origin = mod_origin;
jobQueueCounter.post();
-
jobQueueMutex.unlock();
-
- return toAdd.id;
+ return jobId;
}
/******************************************************************************/
-LuaJobInfo AsyncEngine::getJob()
+bool AsyncEngine::getJob(LuaJobInfo *job)
{
jobQueueCounter.wait();
jobQueueMutex.lock();
- LuaJobInfo retval;
+ bool retval = false;
if (!jobQueue.empty()) {
- retval = jobQueue.front();
+ *job = std::move(jobQueue.front());
jobQueue.pop_front();
- retval.valid = true;
+ retval = true;
}
jobQueueMutex.unlock();
@@ -122,10 +122,10 @@ LuaJobInfo AsyncEngine::getJob()
}
/******************************************************************************/
-void AsyncEngine::putJobResult(const LuaJobInfo &result)
+void AsyncEngine::putJobResult(LuaJobInfo &&result)
{
resultQueueMutex.lock();
- resultQueue.push_back(result);
+ resultQueue.emplace_back(std::move(result));
resultQueueMutex.unlock();
}
@@ -134,26 +134,30 @@ void AsyncEngine::step(lua_State *L)
{
int error_handler = PUSH_ERROR_HANDLER(L);
lua_getglobal(L, "core");
- resultQueueMutex.lock();
+
+ ScriptApiBase *script = ModApiBase::getScriptApiBase(L);
+
+ MutexAutoLock autolock(resultQueueMutex);
while (!resultQueue.empty()) {
- LuaJobInfo jobDone = resultQueue.front();
+ LuaJobInfo j = std::move(resultQueue.front());
resultQueue.pop_front();
lua_getfield(L, -1, "async_event_handler");
-
- if (lua_isnil(L, -1)) {
+ if (lua_isnil(L, -1))
FATAL_ERROR("Async event handler does not exist!");
- }
-
luaL_checktype(L, -1, LUA_TFUNCTION);
- lua_pushinteger(L, jobDone.id);
- lua_pushlstring(L, jobDone.serializedResult.data(),
- jobDone.serializedResult.size());
+ lua_pushinteger(L, j.id);
+ lua_pushlstring(L, j.result.data(), j.result.size());
- PCALL_RESL(L, lua_pcall(L, 2, 0, error_handler));
+ // Call handler
+ const char *origin = j.mod_origin.empty() ? nullptr : j.mod_origin.c_str();
+ script->setOriginDirect(origin);
+ int result = lua_pcall(L, 2, 0, error_handler);
+ if (result)
+ script_error(L, result, origin, "<async>");
}
- resultQueueMutex.unlock();
+
lua_pop(L, 2); // Pop core and error handler
}
@@ -168,8 +172,8 @@ void AsyncEngine::prepareEnvironment(lua_State* L, int top)
/******************************************************************************/
AsyncWorkerThread::AsyncWorkerThread(AsyncEngine* jobDispatcher,
const std::string &name) :
- Thread(name),
ScriptApiBase(ScriptingType::Async),
+ Thread(name),
jobDispatcher(jobDispatcher)
{
lua_State *L = getStack();
@@ -196,9 +200,9 @@ void* AsyncWorkerThread::run()
{
lua_State *L = getStack();
- std::string script = getServer()->getBuiltinLuaPath() + DIR_DELIM + "init.lua";
try {
- loadScript(script);
+ loadMod(getServer()->getBuiltinLuaPath() + DIR_DELIM + "init.lua",
+ BUILTIN_MOD_NAME);
} catch (const ModError &e) {
errorstream << "Execution of async base environment failed: "
<< e.what() << std::endl;
@@ -213,44 +217,44 @@ void* AsyncWorkerThread::run()
}
// Main loop
+ LuaJobInfo j;
while (!stopRequested()) {
// Wait for job
- LuaJobInfo toProcess = jobDispatcher->getJob();
-
- if (!toProcess.valid || stopRequested()) {
+ if (!jobDispatcher->getJob(&j) || stopRequested())
continue;
- }
lua_getfield(L, -1, "job_processor");
- if (lua_isnil(L, -1)) {
+ if (lua_isnil(L, -1))
FATAL_ERROR("Unable to get async job processor!");
- }
-
luaL_checktype(L, -1, LUA_TFUNCTION);
- // Call it
- lua_pushlstring(L,
- toProcess.serializedFunction.data(),
- toProcess.serializedFunction.size());
- lua_pushlstring(L,
- toProcess.serializedParams.data(),
- toProcess.serializedParams.size());
+ if (luaL_loadbuffer(L, j.function.data(), j.function.size(), "=(async)")) {
+ errorstream << "ASYNC WORKER: Unable to deserialize function" << std::endl;
+ lua_pushnil(L);
+ }
+ lua_pushlstring(L, j.params.data(), j.params.size());
+ // Call it
+ setOriginDirect(j.mod_origin.empty() ? nullptr : j.mod_origin.c_str());
int result = lua_pcall(L, 2, 1, error_handler);
if (result) {
- PCALL_RES(result);
- toProcess.serializedResult = "";
+ try {
+ scriptError(result, "<async>");
+ } catch (const ModError &e) {
+ errorstream << e.what() << std::endl;
+ }
} else {
// Fetch result
size_t length;
const char *retval = lua_tolstring(L, -1, &length);
- toProcess.serializedResult = std::string(retval, length);
+ j.result.assign(retval, length);
}
lua_pop(L, 1); // Pop retval
// Put job result
- jobDispatcher->putJobResult(toProcess);
+ if (!j.result.empty())
+ jobDispatcher->putJobResult(std::move(j));
}
lua_pop(L, 2); // Pop core and error handler
diff --git a/src/script/cpp_api/s_async.h b/src/script/cpp_api/s_async.h
index 99a4f891c..697cb0221 100644
--- a/src/script/cpp_api/s_async.h
+++ b/src/script/cpp_api/s_async.h
@@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <vector>
#include <deque>
-#include <map>
#include "threading/semaphore.h"
#include "threading/thread.h"
@@ -39,26 +38,29 @@ struct LuaJobInfo
{
LuaJobInfo() = default;
- // Function to be called in async environment
- std::string serializedFunction = "";
- // Parameter to be passed to function
- std::string serializedParams = "";
- // Result of function call
- std::string serializedResult = "";
+ // Function to be called in async environment (from string.dump)
+ std::string function;
+ // Parameter to be passed to function (serialized)
+ std::string params;
+ // Result of function call (serialized)
+ std::string result;
+ // Name of the mod who invoked this call
+ std::string mod_origin;
// JobID used to identify a job and match it to callback
- unsigned int id = 0;
-
- bool valid = false;
+ u32 id;
};
// Asynchronous working environment
-class AsyncWorkerThread : public Thread, public ScriptApiBase {
+class AsyncWorkerThread : public Thread, virtual public ScriptApiBase {
+ friend class AsyncEngine;
public:
- AsyncWorkerThread(AsyncEngine* jobDispatcher, const std::string &name);
virtual ~AsyncWorkerThread();
void *run();
+protected:
+ AsyncWorkerThread(AsyncEngine* jobDispatcher, const std::string &name);
+
private:
AsyncEngine *jobDispatcher = nullptr;
};
@@ -89,7 +91,8 @@ public:
* @param params Serialized parameters
* @return jobid The job is queued
*/
- unsigned int queueAsyncJob(const std::string &func, const std::string &params);
+ u32 queueAsyncJob(std::string &&func, std::string &&params,
+ const std::string &mod_origin = "");
/**
* Engine step to process finished jobs
@@ -102,15 +105,16 @@ protected:
/**
* Get a Job from queue to be processed
* this function blocks until a job is ready
- * @return a job to be processed
+ * @param job a job to be processed
+ * @return whether a job was available
*/
- LuaJobInfo getJob();
+ bool getJob(LuaJobInfo *job);
/**
* Put a Job result back to result queue
* @param result result of completed job
*/
- void putJobResult(const LuaJobInfo &result);
+ void putJobResult(LuaJobInfo &&result);
/**
* Initialize environment with current registred functions
@@ -129,11 +133,10 @@ private:
std::vector<StateInitializer> stateInitializers;
// Internal counter to create job IDs
- unsigned int jobIdCounter = 0;
+ u32 jobIdCounter = 0;
// Mutex to protect job queue
std::mutex jobQueueMutex;
-
// Job queue
std::deque<LuaJobInfo> jobQueue;
diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp
index f965975a3..f7b8a5102 100644
--- a/src/script/cpp_api/s_base.cpp
+++ b/src/script/cpp_api/s_base.cpp
@@ -37,6 +37,8 @@ extern "C" {
#include "lualib.h"
#if USE_LUAJIT
#include "luajit.h"
+#else
+ #include "bit.h"
#endif
}
@@ -88,6 +90,11 @@ ScriptApiBase::ScriptApiBase(ScriptingType type):
else
luaL_openlibs(m_luastack);
+ // Load bit library
+ lua_pushcfunction(m_luastack, luaopen_bit);
+ lua_pushstring(m_luastack, LUA_BITLIBNAME);
+ lua_call(m_luastack, 1, 0);
+
// Make the ScriptApiBase* accessible to ModApiBase
#if INDIRECT_SCRIPTAPI_RIDX
*(void **)(lua_newuserdata(m_luastack, sizeof(void *))) = this;
@@ -331,13 +338,9 @@ void ScriptApiBase::setOriginDirect(const char *origin)
void ScriptApiBase::setOriginFromTableRaw(int index, const char *fxn)
{
-#ifdef SCRIPTAPI_DEBUG
lua_State *L = getStack();
-
m_last_run_mod = lua_istable(L, index) ?
getstringfield_default(L, index, "mod_origin", "") : "";
- //printf(">>>> running %s for mod: %s\n", fxn, m_last_run_mod.c_str());
-#endif
}
/*
diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h
index 86f7f7bac..244d81605 100644
--- a/src/script/cpp_api/s_base.h
+++ b/src/script/cpp_api/s_base.h
@@ -39,7 +39,6 @@ extern "C" {
#include "config.h"
#define SCRIPTAPI_LOCK_DEBUG
-#define SCRIPTAPI_DEBUG
// MUST be an invalid mod name so that mods can't
// use that name to bypass security!
@@ -108,7 +107,9 @@ public:
Client* getClient();
#endif
- std::string getOrigin() { return m_last_run_mod; }
+ // IMPORTANT: these cannot be used for any security-related uses, they exist
+ // only to enrich error messages
+ const std::string &getOrigin() { return m_last_run_mod; }
void setOriginDirect(const char *origin);
void setOriginFromTableRaw(int index, const char *fxn);
@@ -124,11 +125,23 @@ protected:
friend class ModApiEnvMod;
friend class LuaVoxelManip;
+ /*
+ Subtle edge case with coroutines: If for whatever reason you have a
+ method in a subclass that's called from existing lua_CFunction
+ (any of the l_*.cpp files) then make it static and take the lua_State*
+ as an argument. This is REQUIRED because getStack() will not return the
+ correct state if called inside coroutines.
+
+ Also note that src/script/common/ is the better place for such helpers.
+ */
lua_State* getStack()
{ return m_luastack; }
+ // Checks that stack size is sane
void realityCheck();
+ // Takes an error from lua_pcall and throws it as a LuaError
void scriptError(int result, const char *fxn);
+ // Dumps stack contents for debugging
void stackDump(std::ostream &o);
void setGameDef(IGameDef* gamedef) { m_gamedef = gamedef; }
diff --git a/src/script/cpp_api/s_client.cpp b/src/script/cpp_api/s_client.cpp
index f2cc9730b..c889fffa0 100644
--- a/src/script/cpp_api/s_client.cpp
+++ b/src/script/cpp_api/s_client.cpp
@@ -33,7 +33,11 @@ void ScriptApiClient::on_mods_loaded()
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_mods_loaded");
// Call callbacks
- runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
+ try {
+ runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
+ } catch (LuaError &e) {
+ getClient()->setFatalError(e);
+ }
}
void ScriptApiClient::on_shutdown()
@@ -44,7 +48,11 @@ void ScriptApiClient::on_shutdown()
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_shutdown");
// Call callbacks
- runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
+ try {
+ runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
+ } catch (LuaError &e) {
+ getClient()->setFatalError(e);
+ }
}
bool ScriptApiClient::on_sending_message(const std::string &message)
@@ -56,7 +64,12 @@ bool ScriptApiClient::on_sending_message(const std::string &message)
lua_getfield(L, -1, "registered_on_sending_chat_message");
// Call callbacks
lua_pushstring(L, message.c_str());
- runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC);
+ try {
+ runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC);
+ } catch (LuaError &e) {
+ getClient()->setFatalError(e);
+ return true;
+ }
return readParam<bool>(L, -1);
}
@@ -69,7 +82,12 @@ bool ScriptApiClient::on_receiving_message(const std::string &message)
lua_getfield(L, -1, "registered_on_receiving_chat_message");
// Call callbacks
lua_pushstring(L, message.c_str());
- runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC);
+ try {
+ runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC);
+ } catch (LuaError &e) {
+ getClient()->setFatalError(e);
+ return true;
+ }
return readParam<bool>(L, -1);
}
@@ -82,7 +100,11 @@ void ScriptApiClient::on_damage_taken(int32_t damage_amount)
lua_getfield(L, -1, "registered_on_damage_taken");
// Call callbacks
lua_pushinteger(L, damage_amount);
- runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC);
+ try {
+ runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC);
+ } catch (LuaError &e) {
+ getClient()->setFatalError(e);
+ }
}
void ScriptApiClient::on_hp_modification(int32_t newhp)
@@ -94,7 +116,11 @@ void ScriptApiClient::on_hp_modification(int32_t newhp)
lua_getfield(L, -1, "registered_on_hp_modification");
// Call callbacks
lua_pushinteger(L, newhp);
- runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC);
+ try {
+ runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC);
+ } catch (LuaError &e) {
+ getClient()->setFatalError(e);
+ }
}
void ScriptApiClient::on_death()
@@ -105,7 +131,11 @@ void ScriptApiClient::on_death()
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_death");
// Call callbacks
- runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
+ try {
+ runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
+ } catch (LuaError &e) {
+ getClient()->setFatalError(e);
+ }
}
void ScriptApiClient::environment_step(float dtime)
@@ -120,8 +150,7 @@ void ScriptApiClient::environment_step(float dtime)
try {
runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
} catch (LuaError &e) {
- getClient()->setFatalError(std::string("Client environment_step: ") + e.what() + "\n"
- + script_get_backtrace(L));
+ getClient()->setFatalError(e);
}
}
@@ -146,7 +175,11 @@ void ScriptApiClient::on_formspec_input(const std::string &formname,
lua_pushlstring(L, value.c_str(), value.size());
lua_settable(L, -3);
}
- runCallbacks(2, RUN_CALLBACKS_MODE_OR_SC);
+ try {
+ runCallbacks(2, RUN_CALLBACKS_MODE_OR_SC);
+ } catch (LuaError &e) {
+ getClient()->setFatalError(e);
+ }
}
bool ScriptApiClient::on_dignode(v3s16 p, MapNode node)
@@ -164,7 +197,12 @@ bool ScriptApiClient::on_dignode(v3s16 p, MapNode node)
pushnode(L, node, ndef);
// Call functions
- runCallbacks(2, RUN_CALLBACKS_MODE_OR);
+ try {
+ runCallbacks(2, RUN_CALLBACKS_MODE_OR);
+ } catch (LuaError &e) {
+ getClient()->setFatalError(e);
+ return true;
+ }
return lua_toboolean(L, -1);
}
@@ -183,7 +221,12 @@ bool ScriptApiClient::on_punchnode(v3s16 p, MapNode node)
pushnode(L, node, ndef);
// Call functions
- runCallbacks(2, RUN_CALLBACKS_MODE_OR);
+ try {
+ runCallbacks(2, RUN_CALLBACKS_MODE_OR);
+ } catch (LuaError &e) {
+ getClient()->setFatalError(e);
+ return true;
+ }
return readParam<bool>(L, -1);
}
@@ -200,7 +243,12 @@ bool ScriptApiClient::on_placenode(const PointedThing &pointed, const ItemDefini
push_item_definition(L, item);
// Call functions
- runCallbacks(2, RUN_CALLBACKS_MODE_OR);
+ try {
+ runCallbacks(2, RUN_CALLBACKS_MODE_OR);
+ } catch (LuaError &e) {
+ getClient()->setFatalError(e);
+ return true;
+ }
return readParam<bool>(L, -1);
}
@@ -217,7 +265,12 @@ bool ScriptApiClient::on_item_use(const ItemStack &item, const PointedThing &poi
push_pointed_thing(L, pointed, true);
// Call functions
- runCallbacks(2, RUN_CALLBACKS_MODE_OR);
+ try {
+ runCallbacks(2, RUN_CALLBACKS_MODE_OR);
+ } catch (LuaError &e) {
+ getClient()->setFatalError(e);
+ return true;
+ }
return readParam<bool>(L, -1);
}
@@ -238,7 +291,12 @@ bool ScriptApiClient::on_inventory_open(Inventory *inventory)
lua_rawset(L, -3);
}
- runCallbacks(1, RUN_CALLBACKS_MODE_OR);
+ try {
+ runCallbacks(1, RUN_CALLBACKS_MODE_OR);
+ } catch (LuaError &e) {
+ getClient()->setFatalError(e);
+ return true;
+ }
return readParam<bool>(L, -1);
}
diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp
index 746f7013e..06337b9e8 100644
--- a/src/script/cpp_api/s_entity.cpp
+++ b/src/script/cpp_api/s_entity.cpp
@@ -240,7 +240,7 @@ void ScriptApiEntity::luaentity_Step(u16 id, float dtime,
// tool_capabilities, direction, damage)
bool ScriptApiEntity::luaentity_Punch(u16 id,
ServerActiveObject *puncher, float time_from_last_punch,
- const ToolCapabilities *toolcap, v3f dir, s16 damage)
+ const ToolCapabilities *toolcap, v3f dir, s32 damage)
{
SCRIPTAPI_PRECHECKHEADER
diff --git a/src/script/cpp_api/s_entity.h b/src/script/cpp_api/s_entity.h
index b52f6e447..7658ae922 100644
--- a/src/script/cpp_api/s_entity.h
+++ b/src/script/cpp_api/s_entity.h
@@ -42,7 +42,7 @@ public:
const collisionMoveResult *moveresult);
bool luaentity_Punch(u16 id,
ServerActiveObject *puncher, float time_from_last_punch,
- const ToolCapabilities *toolcap, v3f dir, s16 damage);
+ const ToolCapabilities *toolcap, v3f dir, s32 damage);
bool luaentity_on_death(u16 id, ServerActiveObject *killer);
void luaentity_Rightclick(u16 id, ServerActiveObject *clicker);
void luaentity_on_attach_child(u16 id, ServerActiveObject *child);
diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp
index 8da5debaa..874c37b6e 100644
--- a/src/script/cpp_api/s_env.cpp
+++ b/src/script/cpp_api/s_env.cpp
@@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapgen/mapgen.h"
#include "lua_api/l_env.h"
#include "server.h"
+#include "script/common/c_content.h"
+
void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp,
u32 blockseed)
@@ -51,13 +53,7 @@ void ScriptApiEnv::environment_Step(float dtime)
lua_getfield(L, -1, "registered_globalsteps");
// Call callbacks
lua_pushnumber(L, dtime);
- try {
- runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
- } catch (LuaError &e) {
- getServer()->setAsyncFatalError(
- std::string("environment_Step: ") + e.what() + "\n"
- + script_get_backtrace(L));
- }
+ runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiEnv::player_event(ServerActiveObject *player, const std::string &type)
@@ -74,13 +70,7 @@ void ScriptApiEnv::player_event(ServerActiveObject *player, const std::string &t
// Call callbacks
objectrefGetOrCreate(L, player); // player
lua_pushstring(L,type.c_str()); // event type
- try {
- runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
- } catch (LuaError &e) {
- getServer()->setAsyncFatalError(
- std::string("player_event: ") + e.what() + "\n"
- + script_get_backtrace(L) );
- }
+ runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env)
@@ -150,13 +140,19 @@ void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env)
bool simple_catch_up = true;
getboolfield(L, current_abm, "catch_up", simple_catch_up);
+
+ s16 min_y = INT16_MIN;
+ getintfield(L, current_abm, "min_y", min_y);
+
+ s16 max_y = INT16_MAX;
+ getintfield(L, current_abm, "max_y", max_y);
lua_getfield(L, current_abm, "action");
luaL_checktype(L, current_abm + 1, LUA_TFUNCTION);
lua_pop(L, 1);
LuaABM *abm = new LuaABM(L, id, trigger_contents, required_neighbors,
- trigger_interval, trigger_chance, simple_catch_up);
+ trigger_interval, trigger_chance, simple_catch_up, min_y, max_y);
env->addActiveBlockModifier(abm);
@@ -249,9 +245,8 @@ void ScriptApiEnv::on_emerge_area_completion(
try {
PCALL_RES(lua_pcall(L, 4, 0, error_handler));
} catch (LuaError &e) {
- server->setAsyncFatalError(
- std::string("on_emerge_area_completion: ") + e.what() + "\n"
- + script_get_backtrace(L));
+ // Note: don't throw here, we still need to run the cleanup code below
+ server->setAsyncFatalError(e);
}
lua_pop(L, 1); // Pop error handler
@@ -261,3 +256,35 @@ void ScriptApiEnv::on_emerge_area_completion(
luaL_unref(L, LUA_REGISTRYINDEX, state->args_ref);
}
}
+
+void ScriptApiEnv::on_liquid_transformed(
+ const std::vector<std::pair<v3s16, MapNode>> &list)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get core.registered_on_liquid_transformed
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "registered_on_liquid_transformed");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ lua_remove(L, -2);
+
+ // Skip converting list and calling hook if there are
+ // no registered callbacks.
+ if(lua_objlen(L, -1) < 1) return;
+
+ // Convert the list to a pos array and a node array for lua
+ int index = 1;
+ const NodeDefManager *ndef = getEnv()->getGameDef()->ndef();
+ lua_createtable(L, list.size(), 0);
+ lua_createtable(L, list.size(), 0);
+ for(std::pair<v3s16, MapNode> p : list) {
+ lua_pushnumber(L, index);
+ push_v3s16(L, p.first);
+ lua_rawset(L, -4);
+ lua_pushnumber(L, index++);
+ pushnode(L, p.second, ndef);
+ lua_rawset(L, -3);
+ }
+
+ runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
+}
diff --git a/src/script/cpp_api/s_env.h b/src/script/cpp_api/s_env.h
index 232a08aaf..090858f17 100644
--- a/src/script/cpp_api/s_env.h
+++ b/src/script/cpp_api/s_env.h
@@ -21,6 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "cpp_api/s_base.h"
#include "irr_v3d.h"
+#include "mapnode.h"
+#include <vector>
class ServerEnvironment;
struct ScriptCallbackState;
@@ -41,5 +43,8 @@ public:
void on_emerge_area_completion(v3s16 blockpos, int action,
ScriptCallbackState *state);
+ // Called after liquid transform changes
+ void on_liquid_transformed(const std::vector<std::pair<v3s16, MapNode>> &list);
+
void initializeEnvironment(ServerEnvironment *env);
};
diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp
index 24955cefc..b1916070e 100644
--- a/src/script/cpp_api/s_item.cpp
+++ b/src/script/cpp_api/s_item.cpp
@@ -29,6 +29,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "inventory.h"
#include "inventorymanager.h"
+#define WRAP_LUAERROR(e, detail) \
+ LuaError(std::string(__FUNCTION__) + ": " + (e).what() + ". " detail)
+
bool ScriptApiItem::item_OnDrop(ItemStack &item,
ServerActiveObject *dropper, v3f pos)
{
@@ -49,20 +52,21 @@ bool ScriptApiItem::item_OnDrop(ItemStack &item,
try {
item = read_item(L, -1, getServer()->idef());
} catch (LuaError &e) {
- throw LuaError(std::string(e.what()) + ". item=" + item.name);
+ throw WRAP_LUAERROR(e, "item=" + item.name);
}
}
lua_pop(L, 2); // Pop item and error handler
return true;
}
-bool ScriptApiItem::item_OnPlace(ItemStack &item,
+bool ScriptApiItem::item_OnPlace(Optional<ItemStack> &ret_item,
ServerActiveObject *placer, const PointedThing &pointed)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
+ const ItemStack &item = *ret_item;
// Push callback function on stack
if (!getItemCallback(item.name.c_str(), "on_place"))
return false;
@@ -79,22 +83,25 @@ bool ScriptApiItem::item_OnPlace(ItemStack &item,
PCALL_RES(lua_pcall(L, 3, 1, error_handler));
if (!lua_isnil(L, -1)) {
try {
- item = read_item(L, -1, getServer()->idef());
+ ret_item = read_item(L, -1, getServer()->idef());
} catch (LuaError &e) {
- throw LuaError(std::string(e.what()) + ". item=" + item.name);
+ throw WRAP_LUAERROR(e, "item=" + item.name);
}
+ } else {
+ ret_item = nullopt;
}
lua_pop(L, 2); // Pop item and error handler
return true;
}
-bool ScriptApiItem::item_OnUse(ItemStack &item,
+bool ScriptApiItem::item_OnUse(Optional<ItemStack> &ret_item,
ServerActiveObject *user, const PointedThing &pointed)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
+ const ItemStack &item = *ret_item;
// Push callback function on stack
if (!getItemCallback(item.name.c_str(), "on_use"))
return false;
@@ -106,22 +113,25 @@ bool ScriptApiItem::item_OnUse(ItemStack &item,
PCALL_RES(lua_pcall(L, 3, 1, error_handler));
if(!lua_isnil(L, -1)) {
try {
- item = read_item(L, -1, getServer()->idef());
+ ret_item = read_item(L, -1, getServer()->idef());
} catch (LuaError &e) {
- throw LuaError(std::string(e.what()) + ". item=" + item.name);
+ throw WRAP_LUAERROR(e, "item=" + item.name);
}
+ } else {
+ ret_item = nullopt;
}
lua_pop(L, 2); // Pop item and error handler
return true;
}
-bool ScriptApiItem::item_OnSecondaryUse(ItemStack &item,
+bool ScriptApiItem::item_OnSecondaryUse(Optional<ItemStack> &ret_item,
ServerActiveObject *user, const PointedThing &pointed)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
+ const ItemStack &item = *ret_item;
if (!getItemCallback(item.name.c_str(), "on_secondary_use"))
return false;
@@ -131,10 +141,12 @@ bool ScriptApiItem::item_OnSecondaryUse(ItemStack &item,
PCALL_RES(lua_pcall(L, 3, 1, error_handler));
if (!lua_isnil(L, -1)) {
try {
- item = read_item(L, -1, getServer()->idef());
+ ret_item = read_item(L, -1, getServer()->idef());
} catch (LuaError &e) {
- throw LuaError(std::string(e.what()) + ". item=" + item.name);
+ throw WRAP_LUAERROR(e, "item=" + item.name);
}
+ } else {
+ ret_item = nullopt;
}
lua_pop(L, 2); // Pop item and error handler
return true;
@@ -165,7 +177,7 @@ bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user,
try {
item = read_item(L, -1, getServer()->idef());
} catch (LuaError &e) {
- throw LuaError(std::string(e.what()) + ". item=" + item.name);
+ throw WRAP_LUAERROR(e, "item=" + item.name);
}
}
lua_pop(L, 2); // Pop item and error handler
@@ -197,7 +209,7 @@ bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user,
try {
item = read_item(L, -1, getServer()->idef());
} catch (LuaError &e) {
- throw LuaError(std::string(e.what()) + ". item=" + item.name);
+ throw WRAP_LUAERROR(e, "item=" + item.name);
}
}
lua_pop(L, 2); // Pop item and error handler
diff --git a/src/script/cpp_api/s_item.h b/src/script/cpp_api/s_item.h
index 25a3501f9..5015d8bd4 100644
--- a/src/script/cpp_api/s_item.h
+++ b/src/script/cpp_api/s_item.h
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "cpp_api/s_base.h"
#include "irr_v3d.h"
+#include "util/Optional.h"
struct PointedThing;
struct ItemStack;
@@ -35,13 +36,20 @@ class ScriptApiItem
: virtual public ScriptApiBase
{
public:
+ /*
+ * Functions with Optional<ItemStack> are for callbacks where Lua may
+ * want to prevent the engine from modifying the inventory after it's done.
+ * This has a longer backstory where on_use may need to empty the player's
+ * inventory without the engine interfering (see issue #6546).
+ */
+
bool item_OnDrop(ItemStack &item,
ServerActiveObject *dropper, v3f pos);
- bool item_OnPlace(ItemStack &item,
+ bool item_OnPlace(Optional<ItemStack> &item,
ServerActiveObject *placer, const PointedThing &pointed);
- bool item_OnUse(ItemStack &item,
+ bool item_OnUse(Optional<ItemStack> &item,
ServerActiveObject *user, const PointedThing &pointed);
- bool item_OnSecondaryUse(ItemStack &item,
+ bool item_OnSecondaryUse(Optional<ItemStack> &item,
ServerActiveObject *user, const PointedThing &pointed);
bool item_OnCraft(ItemStack &item, ServerActiveObject *user,
const InventoryList *old_craft_grid, const InventoryLocation &craft_inv);
diff --git a/src/script/cpp_api/s_mainmenu.h b/src/script/cpp_api/s_mainmenu.h
index aef36ce39..470577a29 100644
--- a/src/script/cpp_api/s_mainmenu.h
+++ b/src/script/cpp_api/s_mainmenu.h
@@ -38,7 +38,7 @@ public:
void handleMainMenuEvent(std::string text);
/**
- * process field data recieved from formspec
+ * process field data received from formspec
* @param fields data in field format
*/
void handleMainMenuButtons(const StringMap &fields);
diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp
index f23fbfbde..029cb6308 100644
--- a/src/script/cpp_api/s_node.cpp
+++ b/src/script/cpp_api/s_node.cpp
@@ -65,6 +65,7 @@ struct EnumString ScriptApiNode::es_ContentParamType2[] =
{CPT2_COLORED_FACEDIR, "colorfacedir"},
{CPT2_COLORED_WALLMOUNTED, "colorwallmounted"},
{CPT2_GLASSLIKE_LIQUID_LEVEL, "glasslikeliquidlevel"},
+ {CPT2_COLORED_DEGROTATE, "colordegrotate"},
{0, NULL},
};
diff --git a/src/script/cpp_api/s_node.h b/src/script/cpp_api/s_node.h
index 3f771c838..3c6a8445b 100644
--- a/src/script/cpp_api/s_node.h
+++ b/src/script/cpp_api/s_node.h
@@ -53,6 +53,7 @@ public:
static struct EnumString es_ContentParamType[];
static struct EnumString es_ContentParamType2[];
static struct EnumString es_LiquidType[];
+ static struct EnumString es_LiquidMoveType[];
static struct EnumString es_NodeBoxType[];
static struct EnumString es_TextureAlphaMode[];
};
diff --git a/src/script/cpp_api/s_nodemeta.cpp b/src/script/cpp_api/s_nodemeta.cpp
index c081e9fc4..7ab3757f3 100644
--- a/src/script/cpp_api/s_nodemeta.cpp
+++ b/src/script/cpp_api/s_nodemeta.cpp
@@ -43,7 +43,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowMove(
return 0;
// Push callback function on stack
- std::string nodename = ndef->get(node).name;
+ const auto &nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_move", &ma.to_inv.p))
return count;
@@ -58,7 +58,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowMove(
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);
+ " return a number. node=" + nodename);
int num = luaL_checkinteger(L, -1);
lua_pop(L, 2); // Pop integer and error handler
return num;
@@ -81,7 +81,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowPut(
return 0;
// Push callback function on stack
- std::string nodename = ndef->get(node).name;
+ const auto &nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_put", &ma.to_inv.p))
return stack.count;
@@ -94,7 +94,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowPut(
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);
+ " return a number. node=" + nodename);
int num = luaL_checkinteger(L, -1);
lua_pop(L, 2); // Pop integer and error handler
return num;
@@ -117,7 +117,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowTake(
return 0;
// Push callback function on stack
- std::string nodename = ndef->get(node).name;
+ const auto &nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_take", &ma.from_inv.p))
return stack.count;
@@ -130,7 +130,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowTake(
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);
+ " return a number. node=" + nodename);
int num = luaL_checkinteger(L, -1);
lua_pop(L, 2); // Pop integer and error handler
return num;
@@ -153,7 +153,7 @@ void ScriptApiNodemeta::nodemeta_inventory_OnMove(
return;
// Push callback function on stack
- std::string nodename = ndef->get(node).name;
+ const auto &nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_move", &ma.from_inv.p))
return;
@@ -186,7 +186,7 @@ void ScriptApiNodemeta::nodemeta_inventory_OnPut(
return;
// Push callback function on stack
- std::string nodename = ndef->get(node).name;
+ const auto &nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_put", &ma.to_inv.p))
return;
@@ -217,7 +217,7 @@ void ScriptApiNodemeta::nodemeta_inventory_OnTake(
return;
// Push callback function on stack
- std::string nodename = ndef->get(node).name;
+ const auto &nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_take", &ma.from_inv.p))
return;
diff --git a/src/script/cpp_api/s_player.cpp b/src/script/cpp_api/s_player.cpp
index d3e6138dc..22b24f363 100644
--- a/src/script/cpp_api/s_player.cpp
+++ b/src/script/cpp_api/s_player.cpp
@@ -60,7 +60,7 @@ bool ScriptApiPlayer::on_punchplayer(ServerActiveObject *player,
float time_from_last_punch,
const ToolCapabilities *toolcap,
v3f dir,
- s16 damage)
+ s32 damage)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_punchplayers
diff --git a/src/script/cpp_api/s_player.h b/src/script/cpp_api/s_player.h
index c0f141862..e866aee46 100644
--- a/src/script/cpp_api/s_player.h
+++ b/src/script/cpp_api/s_player.h
@@ -46,7 +46,7 @@ public:
void on_cheat(ServerActiveObject *player, const std::string &cheat_type);
bool on_punchplayer(ServerActiveObject *player, ServerActiveObject *hitter,
float time_from_last_punch, const ToolCapabilities *toolcap,
- v3f dir, s16 damage);
+ v3f dir, s32 damage);
void on_rightclickplayer(ServerActiveObject *player, ServerActiveObject *clicker);
s32 on_player_hpchange(ServerActiveObject *player, s32 hp_change,
const PlayerHPChangeReason &reason);
diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp
index 63058d7c3..a6c5114b2 100644
--- a/src/script/cpp_api/s_security.cpp
+++ b/src/script/cpp_api/s_security.cpp
@@ -18,7 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "cpp_api/s_security.h"
-
+#include "lua_api/l_base.h"
#include "filesys.h"
#include "porting.h"
#include "server.h"
@@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cerrno>
#include <string>
+#include <algorithm>
#include <iostream>
@@ -45,6 +46,21 @@ static inline void copy_safe(lua_State *L, const char *list[], unsigned len, int
}
}
+static void shallow_copy_table(lua_State *L, int from=-2, int to=-1)
+{
+ if (from < 0) from = lua_gettop(L) + from + 1;
+ if (to < 0) to = lua_gettop(L) + to + 1;
+ lua_pushnil(L);
+ while (lua_next(L, from) != 0) {
+ assert(lua_type(L, -1) != LUA_TTABLE);
+ // duplicate key and value for lua_rawset
+ lua_pushvalue(L, -2);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, to);
+ lua_pop(L, 1);
+ }
+}
+
// 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)
{
@@ -83,11 +99,15 @@ void ScriptApiSecurity::initializeSecurity()
"unpack",
"_VERSION",
"xpcall",
- // Completely safe libraries
+ };
+ static const char *whitelist_tables[] = {
+ // These libraries are completely safe BUT we need to duplicate their table
+ // to ensure the sandbox can't affect the insecure env
"coroutine",
"string",
"table",
"math",
+ "bit"
};
static const char *io_whitelist[] = {
"close",
@@ -101,21 +121,17 @@ void ScriptApiSecurity::initializeSecurity()
"date",
"difftime",
"getenv",
- "setlocale",
"time",
- "tmpname",
};
static const char *debug_whitelist[] = {
"gethook",
"traceback",
"getinfo",
"getmetatable",
- "setupvalue",
"setmetatable",
"upvalueid",
"sethook",
"debug",
- "setlocal",
};
static const char *package_whitelist[] = {
"config",
@@ -167,6 +183,17 @@ void ScriptApiSecurity::initializeSecurity()
lua_pop(L, 1);
+ // Copy safe libraries
+ for (const char *libname : whitelist_tables) {
+ lua_getfield(L, old_globals, libname);
+ lua_newtable(L);
+ shallow_copy_table(L);
+
+ lua_setglobal(L, libname);
+ lua_pop(L, 1);
+ }
+
+
// Copy safe IO functions
lua_getfield(L, old_globals, "io");
lua_newtable(L);
@@ -190,6 +217,7 @@ void ScriptApiSecurity::initializeSecurity()
// And replace unsafe ones
SECURE_API(os, remove);
SECURE_API(os, rename);
+ SECURE_API(os, setlocale);
lua_setglobal(L, "os");
lua_pop(L, 1); // Pop old OS
@@ -221,7 +249,25 @@ void ScriptApiSecurity::initializeSecurity()
lua_pop(L, 1); // Pop old jit
#endif
+ // Get rid of 'core' in the old globals, we don't want anyone thinking it's
+ // safe or even usable.
+ lua_pushnil(L);
+ lua_setfield(L, old_globals, "core");
+
lua_pop(L, 1); // Pop globals_backup
+
+
+ /*
+ * In addition to copying the tables in whitelist_tables, we also need to
+ * replace the string metatable. Otherwise old_globals.string would
+ * be accessible via getmetatable("").__index from inside the sandbox.
+ */
+ lua_pushliteral(L, "");
+ lua_newtable(L);
+ lua_getglobal(L, "string");
+ lua_setfield(L, -2, "__index");
+ lua_setmetatable(L, -2);
+ lua_pop(L, 1); // Pop empty string
}
void ScriptApiSecurity::initializeSecurityClient()
@@ -243,7 +289,7 @@ void ScriptApiSecurity::initializeSecurityClient()
"rawset",
"select",
"setfenv",
- // getmetatable can be used to escape the sandbox
+ // getmetatable can be used to escape the sandbox <- ???
"setmetatable",
"tonumber",
"tostring",
@@ -256,6 +302,7 @@ void ScriptApiSecurity::initializeSecurityClient()
"string",
"table",
"math",
+ "bit",
};
static const char *os_whitelist[] = {
"clock",
@@ -264,7 +311,7 @@ void ScriptApiSecurity::initializeSecurityClient()
"time"
};
static const char *debug_whitelist[] = {
- "getinfo",
+ "getinfo", // used by builtin and unset before mods load
"traceback"
};
@@ -496,15 +543,8 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path,
if (!removed.empty())
abs_path += DIR_DELIM + removed;
- // Get server from registry
- lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
- ScriptApiBase *script;
-#if INDIRECT_SCRIPTAPI_RIDX
- script = (ScriptApiBase *) *(void**)(lua_touserdata(L, -1));
-#else
- script = (ScriptApiBase *) lua_touserdata(L, -1);
-#endif
- lua_pop(L, 1);
+ // Get gamedef from registry
+ ScriptApiBase *script = ModApiBase::getScriptApiBase(L);
const IGameDef *gamedef = script->getGameDef();
if (!gamedef)
return false;
@@ -569,6 +609,38 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path,
return false;
}
+bool ScriptApiSecurity::checkWhitelisted(lua_State *L, const std::string &setting)
+{
+ assert(str_starts_with(setting, "secure."));
+
+ // 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 false;
+ 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 false;
+
+ // 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 false;
+ std::string mod_name = readParam<std::string>(L, -1);
+
+ std::string value = g_settings->get(setting);
+ value.erase(std::remove(value.begin(), value.end(), ' '), value.end());
+ auto mod_list = str_split(value, ',');
+
+ return CONTAINS(mod_list, mod_name);
+}
+
int ScriptApiSecurity::sl_g_dofile(lua_State *L)
{
@@ -627,13 +699,7 @@ int ScriptApiSecurity::sl_g_load(lua_State *L)
int ScriptApiSecurity::sl_g_loadfile(lua_State *L)
{
#ifndef SERVER
- lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
-#if INDIRECT_SCRIPTAPI_RIDX
- ScriptApiBase *script = (ScriptApiBase *) *(void**)(lua_touserdata(L, -1));
-#else
- ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1);
-#endif
- lua_pop(L, 1);
+ ScriptApiBase *script = ModApiBase::getScriptApiBase(L);
// Client implementation
if (script->getType() == ScriptingType::Client) {
@@ -806,3 +872,20 @@ int ScriptApiSecurity::sl_os_remove(lua_State *L)
return 2;
}
+
+int ScriptApiSecurity::sl_os_setlocale(lua_State *L)
+{
+ const bool cat = lua_gettop(L) > 1;
+ // Don't allow changes
+ if (!lua_isnoneornil(L, 1)) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ push_original(L, "os", "setlocale");
+ lua_pushnil(L);
+ if (cat)
+ lua_pushvalue(L, 2);
+ lua_call(L, cat ? 2 : 1, 1);
+ return 1;
+}
diff --git a/src/script/cpp_api/s_security.h b/src/script/cpp_api/s_security.h
index 73e763548..880ce1638 100644
--- a/src/script/cpp_api/s_security.h
+++ b/src/script/cpp_api/s_security.h
@@ -40,11 +40,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class ScriptApiSecurity : virtual public ScriptApiBase
{
public:
- int getThread(lua_State *L);
- // creates an empty Lua environment
- void createEmptyEnv(lua_State *L);
- // sets the enviroment to the table thats on top of the stack
- void setLuaEnv(lua_State *L, int thread);
// Sets up security on the ScriptApi's Lua state
void initializeSecurity();
void initializeSecurityClient();
@@ -57,8 +52,17 @@ public:
// Checks if mods are allowed to read (and optionally write) to the path
static bool checkPath(lua_State *L, const char *path, bool write_required,
bool *write_allowed=NULL);
+ // Check if mod is whitelisted in the given setting
+ // This additionally checks that the mod's main file scope is executing.
+ static bool checkWhitelisted(lua_State *L, const std::string &setting);
private:
+ int getThread(lua_State *L);
+ // sets the enviroment to the table thats on top of the stack
+ void setLuaEnv(lua_State *L, int thread);
+ // creates an empty Lua environment
+ void createEmptyEnv(lua_State *L);
+
// Syntax: "sl_" <Library name or 'g' (global)> '_' <Function name>
// (sl stands for Secure Lua)
@@ -75,4 +79,5 @@ private:
static int sl_os_rename(lua_State *L);
static int sl_os_remove(lua_State *L);
+ static int sl_os_setlocale(lua_State *L);
};
diff --git a/src/script/cpp_api/s_server.cpp b/src/script/cpp_api/s_server.cpp
index 96cb28b28..c255b0c71 100644
--- a/src/script/cpp_api/s_server.cpp
+++ b/src/script/cpp_api/s_server.cpp
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "cpp_api/s_server.h"
#include "cpp_api/s_internal.h"
#include "common/c_converter.h"
+#include "util/numeric.h" // myrand
bool ScriptApiServer::getAuth(const std::string &playername,
std::string *dst_password,
@@ -196,3 +197,66 @@ std::string ScriptApiServer::formatChatMessage(const std::string &name,
return ret;
}
+
+u32 ScriptApiServer::allocateDynamicMediaCallback(lua_State *L, int f_idx)
+{
+ if (f_idx < 0)
+ f_idx = lua_gettop(L) + f_idx + 1;
+
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "dynamic_media_callbacks");
+ luaL_checktype(L, -1, LUA_TTABLE);
+
+ // Find a randomly generated token that doesn't exist yet
+ int tries = 100;
+ u32 token;
+ while (1) {
+ token = myrand();
+ lua_rawgeti(L, -2, token);
+ bool is_free = lua_isnil(L, -1);
+ lua_pop(L, 1);
+ if (is_free)
+ break;
+ if (--tries < 0)
+ FATAL_ERROR("Ran out of callbacks IDs?!");
+ }
+
+ // core.dynamic_media_callbacks[token] = callback_func
+ lua_pushvalue(L, f_idx);
+ lua_rawseti(L, -2, token);
+
+ lua_pop(L, 2);
+
+ verbosestream << "allocateDynamicMediaCallback() = " << token << std::endl;
+ return token;
+}
+
+void ScriptApiServer::freeDynamicMediaCallback(u32 token)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ verbosestream << "freeDynamicMediaCallback(" << token << ")" << std::endl;
+
+ // core.dynamic_media_callbacks[token] = nil
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "dynamic_media_callbacks");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ lua_pushnil(L);
+ lua_rawseti(L, -2, token);
+ lua_pop(L, 2);
+}
+
+void ScriptApiServer::on_dynamic_media_added(u32 token, const char *playername)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ int error_handler = PUSH_ERROR_HANDLER(L);
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "dynamic_media_callbacks");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ lua_rawgeti(L, -1, token);
+ luaL_checktype(L, -1, LUA_TFUNCTION);
+
+ lua_pushstring(L, playername);
+ PCALL_RES(lua_pcall(L, 1, 0, error_handler));
+}
diff --git a/src/script/cpp_api/s_server.h b/src/script/cpp_api/s_server.h
index d8639cba7..58c8c0e48 100644
--- a/src/script/cpp_api/s_server.h
+++ b/src/script/cpp_api/s_server.h
@@ -49,6 +49,12 @@ public:
const std::string &password);
bool setPassword(const std::string &playername,
const std::string &password);
+
+ /* dynamic media handling */
+ static u32 allocateDynamicMediaCallback(lua_State *L, int f_idx);
+ void freeDynamicMediaCallback(u32 token);
+ void on_dynamic_media_added(u32 token, const char *playername);
+
private:
void getAuthHandler();
void readPrivileges(int index, std::set<std::string> &result);