5 changes: 4 additions & 1 deletion builtin/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ end
math.randomseed(os.time())
minetest = core

local _S = INIT == "game" and core.request_insecure_environment()

-- Load other files
-- Note: we know that core.get_builtin_path was not hooked because this is builtin
local scriptdir = core.get_builtin_path()
local gamepath = scriptdir .. "game" .. DIR_DELIM
local clientpath = scriptdir .. "client" .. DIR_DELIM
Expand All @@ -35,7 +38,7 @@ dofile(commonpath .. "serialize.lua")
dofile(commonpath .. "misc_helpers.lua")

if INIT == "game" then
dofile(gamepath .. "init.lua")
_S.loadfile(gamepath .. "init.lua")(_S)
assert(not core.get_http_api)
elseif INIT == "mainmenu" then
local mm_script = core.settings:get("main_menu_script")
Expand Down
6 changes: 6 additions & 0 deletions builtin/settingtypes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1338,6 +1338,12 @@ secure.trusted_mods (Trusted mods) string
# allow them to upload and download data to/from the internet.
secure.http_mods (HTTP mods) string

# Replace very common Lua-API functions with an implementation using LuaJIT's
# FFI library. This can result in much better performance.
# Does not work without LuaJIT.
# Warning: *experimental*
secure.use_luajit_ffi (Use LuaJIT FFI) bool false

[*Advanced]

[**Profiling]
Expand Down
842 changes: 842 additions & 0 deletions doc/ffi_nyi.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion doc/lua_api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5116,7 +5116,7 @@ Environment access
Difference between `"A*"` and `"A*_noprefetch"` is that
`"A*"` will pre-calculate the cost-data, the other will calculate it
on-the-fly
* `minetest.spawn_tree (pos, {treedef})`
* `minetest.spawn_tree(pos, {treedef})`
* spawns L-system tree at given `pos` with definition in `treedef` table
* `minetest.transforming_liquid_add(pos)`
* add node to liquid update queue
Expand Down
5 changes: 5 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,11 @@ else()
endif()
endif()

if(USE_LUAJIT)
# luajit ffi needs dynamic symbols
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--dynamic-list=${PROJECT_SOURCE_DIR}/dynamic_list_file.txt")
endif()

if(MINGW)
set(OTHER_FLAGS "${OTHER_FLAGS} -mthreads -fexceptions")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWIN32_LEAN_AND_MEAN")
Expand Down
6 changes: 6 additions & 0 deletions src/dynamic_list_file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
extern "C"
{
ffi_*;
};
};
2 changes: 2 additions & 0 deletions src/script/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
add_subdirectory(common)
add_subdirectory(cpp_api)
add_subdirectory(ffi_api)
add_subdirectory(lua_api)

# Used by server and client
set(common_SCRIPT_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/scripting_server.cpp
${common_SCRIPT_COMMON_SRCS}
${common_SCRIPT_CPP_API_SRCS}
${common_SCRIPT_FFI_API_SRCS}
${common_SCRIPT_LUA_API_SRCS}
PARENT_SCOPE)

Expand Down
1 change: 1 addition & 0 deletions src/script/cpp_api/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
set(common_SCRIPT_CPP_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/s_async.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_base.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_core.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_entity.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_env.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_inventory.cpp
Expand Down
31 changes: 10 additions & 21 deletions src/script/cpp_api/s_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class ModNameStorer
*/

ScriptApiBase::ScriptApiBase(ScriptingType type):
m_type(type)
ScriptApiCore(type)
{
#ifdef SCRIPTAPI_LOCK_DEBUG
m_lock_recursion_count = 0;
Expand All @@ -83,7 +83,7 @@ ScriptApiBase::ScriptApiBase(ScriptingType type):

lua_atpanic(m_luastack, &luaPanic);

if (m_type == ScriptingType::Client)
if (getType() == ScriptingType::Client)
clientOpenLibs(m_luastack);
else
luaL_openlibs(m_luastack);
Expand Down Expand Up @@ -114,7 +114,12 @@ ScriptApiBase::ScriptApiBase(ScriptingType type):
lua_newtable(m_luastack);
lua_setglobal(m_luastack, "core");

if (m_type == ScriptingType::Client)
if (getType() == ScriptingType::Server) {
lua_newtable(m_luastack);
lua_setglobal(m_luastack, "insec_private");
}

if (getType() == ScriptingType::Client)
lua_pushstring(m_luastack, "/");
else
lua_pushstring(m_luastack, DIR_DELIM);
Expand Down Expand Up @@ -202,7 +207,7 @@ void ScriptApiBase::loadModFromMemory(const std::string &mod_name)
{
ModNameStorer mod_name_storer(getStack(), mod_name);

sanity_check(m_type == ScriptingType::Client);
sanity_check(getType() == ScriptingType::Client);

const std::string init_filename = mod_name + ":init.lua";
const std::string chunk_name = "@" + init_filename;
Expand Down Expand Up @@ -246,7 +251,7 @@ void ScriptApiBase::runCallbacksRaw(int nargs,
#ifndef SERVER
// Hard fail for bad guarded callbacks
// Only run callbacks when the scripting enviroment is loaded
FATAL_ERROR_IF(m_type == ScriptingType::Client &&
FATAL_ERROR_IF(getType() == ScriptingType::Client &&
!getClient()->modsLoaded(), fxn);
#endif

Expand Down Expand Up @@ -324,11 +329,6 @@ void ScriptApiBase::stackDump(std::ostream &o)
o << std::endl;
}

void ScriptApiBase::setOriginDirect(const char *origin)
{
m_last_run_mod = origin ? origin : "??";
}

void ScriptApiBase::setOriginFromTableRaw(int index, const char *fxn)
{
#ifdef SCRIPTAPI_DEBUG
Expand Down Expand Up @@ -441,14 +441,3 @@ void ScriptApiBase::pushPlayerHPChangeReason(lua_State *L, const PlayerHPChangeR
lua_setfield(L, -2, "node");
}
}

Server* ScriptApiBase::getServer()
{
return dynamic_cast<Server *>(m_gamedef);
}
#ifndef SERVER
Client* ScriptApiBase::getClient()
{
return dynamic_cast<Client *>(m_gamedef);
}
#endif
50 changes: 3 additions & 47 deletions src/script/cpp_api/s_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <mutex>
#include <unordered_map>
#include "common/helper.h"
#include "cpp_api/s_core.h"
#include "util/basic_macros.h"

extern "C" {
Expand Down Expand Up @@ -58,31 +59,13 @@ extern "C" {
#define setOriginFromTable(index) \
setOriginFromTableRaw(index, __FUNCTION__)

enum class ScriptingType: u8 {
Async,
Client,
MainMenu,
Server
};

class Server;
#ifndef SERVER
class Client;
#endif
class IGameDef;
class Environment;
class GUIEngine;
class ServerActiveObject;
struct PlayerHPChangeReason;

class ScriptApiBase : protected LuaHelper {
class ScriptApiBase : public ScriptApiCore, protected LuaHelper {
public:
ScriptApiBase(ScriptingType type);
// fake constructor to allow script API classes (e.g ScriptApiEnv) to virtually inherit from this one.
ScriptApiBase()
{
FATAL_ERROR("ScriptApiBase created without ScriptingType!");
}
ScriptApiBase() : ScriptApiCore() {};
virtual ~ScriptApiBase();
DISABLE_CLASS_COPY(ScriptApiBase);

Expand All @@ -101,15 +84,6 @@ class ScriptApiBase : protected LuaHelper {
void addObjectReference(ServerActiveObject *cobj);
void removeObjectReference(ServerActiveObject *cobj);

IGameDef *getGameDef() { return m_gamedef; }
Server* getServer();
ScriptingType getType() { return m_type; }
#ifndef SERVER
Client* getClient();
#endif

std::string getOrigin() { return m_last_run_mod; }
void setOriginDirect(const char *origin);
void setOriginFromTableRaw(int index, const char *fxn);

void clientOpenLibs(lua_State *L);
Expand All @@ -131,22 +105,11 @@ class ScriptApiBase : protected LuaHelper {
void scriptError(int result, const char *fxn);
void stackDump(std::ostream &o);

void setGameDef(IGameDef* gamedef) { m_gamedef = gamedef; }

Environment* getEnv() { return m_environment; }
void setEnv(Environment* env) { m_environment = env; }

#ifndef SERVER
GUIEngine* getGuiEngine() { return m_guiengine; }
void setGuiEngine(GUIEngine* guiengine) { m_guiengine = guiengine; }
#endif

void objectrefGetOrCreate(lua_State *L, ServerActiveObject *cobj);

void pushPlayerHPChangeReason(lua_State *L, const PlayerHPChangeReason& reason);

std::recursive_mutex m_luastackmutex;
std::string m_last_run_mod;
bool m_secure = false;
#ifdef SCRIPTAPI_LOCK_DEBUG
int m_lock_recursion_count{};
Expand All @@ -157,11 +120,4 @@ class ScriptApiBase : protected LuaHelper {
static int luaPanic(lua_State *L);

lua_State *m_luastack = nullptr;

IGameDef *m_gamedef = nullptr;
Environment *m_environment = nullptr;
#ifndef SERVER
GUIEngine *m_guiengine = nullptr;
#endif
ScriptingType m_type;
};
41 changes: 41 additions & 0 deletions src/script/cpp_api/s_core.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Minetest
Copyright (C) 2021 DS
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 "cpp_api/s_core.h"

#include "server.h"
#ifndef SERVER
#include "client/client.h"
#endif

Server *ScriptApiCore::getServer()
{
return dynamic_cast<Server *>(m_gamedef);
}
#ifndef SERVER
Client *ScriptApiCore::getClient()
{
return dynamic_cast<Client *>(m_gamedef);
}
#endif

void ScriptApiCore::setOriginDirect(const char *origin)
{
m_last_run_mod = origin ? origin : "??";
}
84 changes: 84 additions & 0 deletions src/script/cpp_api/s_core.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
Minetest
Copyright (C) 2021 DS
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#pragma once

#include "irrlichttypes.h"
#include "debug.h"

#include "util/basic_macros.h"
#include "config.h"

enum class ScriptingType: u8 {
Async,
Client,
MainMenu,
Server
};

class Server;
#ifndef SERVER
class Client;
#endif
class IGameDef;
class Environment;
class GUIEngine;

class ScriptApiCore {
public:
ScriptApiCore(ScriptingType type) : m_type(type) {};
// fake constructor to allow script API classes (e.g ScriptApiEnv) to virtually inherit from this one.
ScriptApiCore()
{
FATAL_ERROR("ScriptApiCore created without ScriptingType!");
}
DISABLE_CLASS_COPY(ScriptApiCore);

IGameDef *getGameDef() { return m_gamedef; }
Server *getServer();
ScriptingType getType() { return m_type; }
#ifndef SERVER
Client *getClient();
#endif

Environment *getEnv() { return m_environment; }

std::string getOrigin() { return m_last_run_mod; }
void setOriginDirect(const char *origin);

protected:
void setGameDef(IGameDef *gamedef) { m_gamedef = gamedef; }

void setEnv(Environment *env) { m_environment = env; }

#ifndef SERVER
GUIEngine *getGuiEngine() { return m_guiengine; }
void setGuiEngine(GUIEngine *guiengine) { m_guiengine = guiengine; }
#endif

std::string m_last_run_mod;

private:
IGameDef *m_gamedef = nullptr;
Environment *m_environment = nullptr;
#ifndef SERVER
GUIEngine *m_guiengine = nullptr;
#endif
ScriptingType m_type;
};
3 changes: 3 additions & 0 deletions src/script/ffi_api/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
set(common_SCRIPT_FFI_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/ffi_bla.cpp
PARENT_SCOPE)
404 changes: 404 additions & 0 deletions src/script/ffi_api/ffi_bla.cpp

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/script/lua_api/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set(common_SCRIPT_LUA_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/l_craft.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_env.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_http.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_insec_private.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_inventory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_item.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_itemstackmeta.cpp
Expand Down
54 changes: 54 additions & 0 deletions src/script/lua_api/l_insec_private.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
Minetest
Copyright (C) 2021 DS
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 "cpp_api/s_base.h"
#include "lua_api/l_insec_private.h"
#include "lua_api/l_internal.h"
#include "lua_api/l_object.h"
#include "log.h"

// get_script_api_core()
int ModApiInsecPrivate::l_get_script_api_core(lua_State *L)
{
MAP_LOCK_REQUIRED;
ScriptApiCore *sac = getScriptApiBase(L);
*(ScriptApiCore **)lua_newuserdata(L, sizeof(sac)) = sac;
return 1;
}

int ModApiInsecPrivate::l_get_objref_metatable(lua_State *L)
{
MAP_LOCK_REQUIRED;
luaL_getmetatable(L, ObjectRef::className);
return 1;
}

int ModApiInsecPrivate::l_objref_check(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef::checkobject(L, 1);
return 0;
}

void ModApiInsecPrivate::Initialize(lua_State *L, int insec_top)
{
API_FCT_INSEC(get_script_api_core);
API_FCT_INSEC(get_objref_metatable);
API_FCT_INSEC(objref_check);
}
44 changes: 44 additions & 0 deletions src/script/lua_api/l_insec_private.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Minetest
Copyright (C) 2021 DS
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#pragma once

#include "lua_api/l_base.h"

class ModApiInsecPrivate : public ModApiBase
{
private:
/*
NOTE:
The functions in this module are available in the insec_private table
in the insecure environment.
*/

// get_script_api_core()
static int l_get_script_api_core(lua_State *L);

// get_objref_metatable()
static int l_get_objref_metatable(lua_State *L);

// objref_check(obj)
static int l_objref_check(lua_State *L);

public:
static void Initialize(lua_State *L, int insec_top);
};
1 change: 1 addition & 0 deletions src/script/lua_api/l_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
luamethod_dep(class, good, bad)

#define API_FCT(name) registerFunction(L, #name, l_##name, top)
#define API_FCT_INSEC(name) registerFunction(L, #name, l_##name, insec_top)

// For future use
#define MAP_LOCK_REQUIRED ((void)0)
Expand Down
14 changes: 7 additions & 7 deletions src/script/lua_api/l_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,16 @@ class ObjectRef : public ModApiBase {

static ObjectRef *checkobject(lua_State *L, int narg);

static ServerActiveObject* getobject(ObjectRef *ref);
private:
ServerActiveObject *m_object = nullptr;
static const char className[];
static luaL_Reg methods[];
static ServerActiveObject *getobject(ObjectRef *ref);

static LuaEntitySAO *getluaobject(ObjectRef *ref);

static LuaEntitySAO* getluaobject(ObjectRef *ref);
static PlayerSAO *getplayersao(ObjectRef *ref);

static PlayerSAO* getplayersao(ObjectRef *ref);
static const char className[];
private:
ServerActiveObject *m_object = nullptr;
static luaL_Reg methods[];

static RemotePlayer *getplayer(ObjectRef *ref);

Expand Down
3 changes: 2 additions & 1 deletion src/script/lua_api/l_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,13 +415,14 @@ int ModApiUtil::l_request_insecure_environment(lua_State *L)
return 0;
}

// Check secure.trusted_mods
// Check secure.trusted_mods and allow for *builtin*
std::string mod_name = readParam<std::string>(L, -1);
std::string trusted_mods = g_settings->get("secure.trusted_mods");
trusted_mods.erase(std::remove_if(trusted_mods.begin(),
trusted_mods.end(), static_cast<int(*)(int)>(&std::isspace)),
trusted_mods.end());
std::vector<std::string> mod_list = str_split(trusted_mods, ',');
mod_list.emplace_back(BUILTIN_MOD_NAME);
if (std::find(mod_list.begin(), mod_list.end(), mod_name) ==
mod_list.end()) {
return 0;
Expand Down
19 changes: 16 additions & 3 deletions src/script/scripting_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_base.h"
#include "lua_api/l_craft.h"
#include "lua_api/l_env.h"
#include "lua_api/l_insec_private.h"
#include "lua_api/l_inventory.h"
#include "lua_api/l_item.h"
#include "lua_api/l_itemstackmeta.h"
Expand Down Expand Up @@ -62,11 +63,20 @@ ServerScripting::ServerScripting(Server* server):

if (g_settings->getBool("secure.enable_security")) {
initializeSecurity();

lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
int insec_env = lua_gettop(L);
lua_pushstring(L, "insec_private");
lua_rawget(L, insec_env);
lua_remove(L, insec_env);
} else {
warningstream << "\\!/ Mod security should never be disabled, as it allows any mod to "
<< "access the host machine."
<< "Mods should use minetest.request_insecure_environment() instead \\!/" << std::endl;

lua_getglobal(L, "insec_private");
}
int insec_top = lua_gettop(L);

lua_getglobal(L, "core");
int top = lua_gettop(L);
Expand All @@ -78,8 +88,8 @@ ServerScripting::ServerScripting(Server* server):
lua_setfield(L, -2, "luaentities");

// Initialize our lua_api modules
InitializeModApi(L, top);
lua_pop(L, 1);
InitializeModApi(L, top, insec_top);
lua_pop(L, 2);

// Push builtin initialization type
lua_pushstring(L, "game");
Expand All @@ -88,7 +98,7 @@ ServerScripting::ServerScripting(Server* server):
infostream << "SCRIPTAPI: Initialized game modules" << std::endl;
}

void ServerScripting::InitializeModApi(lua_State *L, int top)
void ServerScripting::InitializeModApi(lua_State *L, int top, int insec_top)
{
// Register reference classes (userdata)
InvRef::Register(L);
Expand Down Expand Up @@ -124,4 +134,7 @@ void ServerScripting::InitializeModApi(lua_State *L, int top)
ModApiHttp::Initialize(L, top);
ModApiStorage::Initialize(L, top);
ModApiChannels::Initialize(L, top);

// Initialize private-to-insecure module
ModApiInsecPrivate::Initialize(L, insec_top);
}
2 changes: 1 addition & 1 deletion src/script/scripting_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,5 @@ class ServerScripting:
// use ScriptApiBase::loadMod() to load mods

private:
void InitializeModApi(lua_State *L, int top);
void InitializeModApi(lua_State *L, int top, int insec_top);
};