1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-08-01 17:38:41 +00:00

In-game settings menu using separate Lua environment (#15614)

This commit is contained in:
grorp 2025-01-19 13:07:04 -05:00 committed by GitHub
parent 3cb07d5fb6
commit eeb6cab4c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 652 additions and 290 deletions

View file

@ -15,6 +15,7 @@ set(common_SCRIPT_SRCS
set(client_SCRIPT_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/scripting_mainmenu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/scripting_client.cpp
${CMAKE_CURRENT_SOURCE_DIR}/scripting_pause_menu.cpp
${client_SCRIPT_COMMON_SRCS}
${client_SCRIPT_CPP_API_SRCS}
${client_SCRIPT_LUA_API_SRCS}

View file

@ -16,6 +16,8 @@ set(common_SCRIPT_CPP_API_SRCS
set(client_SCRIPT_CPP_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/s_client.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_client_common.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_mainmenu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_pause_menu.cpp
PARENT_SCOPE)

View file

@ -203,9 +203,13 @@ void ScriptApiBase::checkSetByBuiltin()
{
lua_State *L = getStack();
if (m_gamedef) {
CHECK(CUSTOM_RIDX_READ_VECTOR, "read_vector");
CHECK(CUSTOM_RIDX_PUSH_VECTOR, "push_vector");
CHECK(CUSTOM_RIDX_READ_VECTOR, "read_vector");
CHECK(CUSTOM_RIDX_PUSH_VECTOR, "push_vector");
if (getType() == ScriptingType::Server ||
(getType() == ScriptingType::Async && m_gamedef) ||
getType() == ScriptingType::Emerge ||
getType() == ScriptingType::Client) {
CHECK(CUSTOM_RIDX_READ_NODE, "read_node");
CHECK(CUSTOM_RIDX_PUSH_NODE, "push_node");
}

View file

@ -47,7 +47,8 @@ enum class ScriptingType: u8 {
Client,
MainMenu,
Server,
Emerge
Emerge,
PauseMenu,
};
class Server;

View file

@ -126,34 +126,6 @@ void ScriptApiClient::environment_step(float dtime)
}
}
void ScriptApiClient::on_formspec_input(const std::string &formname,
const StringMap &fields)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_chat_messages
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_formspec_input");
// Call callbacks
// param 1
lua_pushstring(L, formname.c_str());
// param 2
lua_newtable(L);
StringMap::const_iterator it;
for (it = fields.begin(); it != fields.end(); ++it) {
const std::string &name = it->first;
const std::string &value = it->second;
lua_pushstring(L, name.c_str());
lua_pushlstring(L, value.c_str(), value.size());
lua_settable(L, -3);
}
try {
runCallbacks(2, RUN_CALLBACKS_MODE_OR_SC);
} catch (LuaError &e) {
getClient()->setFatalError(e);
}
}
bool ScriptApiClient::on_dignode(v3s16 p, MapNode node)
{
SCRIPTAPI_PRECHECKHEADER

View file

@ -35,7 +35,6 @@ public:
void on_damage_taken(int32_t damage_amount);
void on_hp_modification(int32_t newhp);
void environment_step(float dtime);
void on_formspec_input(const std::string &formname, const StringMap &fields);
bool on_dignode(v3s16 p, MapNode node);
bool on_punchnode(v3s16 p, MapNode node);

View file

@ -0,0 +1,39 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
// Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
// Copyright (C) 2025 grorp
#include "s_client_common.h"
#include "s_internal.h"
#include "client/client.h"
bool ScriptApiClientCommon::on_formspec_input(const std::string &formname,
const StringMap &fields)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_formspec_inputs
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_formspec_input");
// Call callbacks
// param 1
lua_pushstring(L, formname.c_str());
// param 2
lua_newtable(L);
StringMap::const_iterator it;
for (it = fields.begin(); it != fields.end(); ++it) {
const std::string &name = it->first;
const std::string &value = it->second;
lua_pushstring(L, name.c_str());
lua_pushlstring(L, value.c_str(), value.size());
lua_settable(L, -3);
}
try {
runCallbacks(2, RUN_CALLBACKS_MODE_OR_SC);
} catch (LuaError &e) {
getClient()->setFatalError(e);
return true;
}
return readParam<bool>(L, -1);
}

View file

@ -0,0 +1,16 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
// Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
// Copyright (C) 2025 grorp
#pragma once
#include "cpp_api/s_base.h"
#include "util/string.h"
class ScriptApiClientCommon : virtual public ScriptApiBase
{
public:
bool on_formspec_input(const std::string &formname, const StringMap &fields);
};

View file

@ -0,0 +1,20 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2025 grorp
#include "s_pause_menu.h"
#include "cpp_api/s_internal.h"
void ScriptApiPauseMenu::open_settings()
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
lua_getglobal(L, "core");
lua_getfield(L, -1, "open_settings");
PCALL_RES(lua_pcall(L, 0, 0, error_handler));
lua_pop(L, 2); // Pop core, error handler
}

View file

@ -0,0 +1,13 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2025 grorp
#pragma once
#include "cpp_api/s_base.h"
class ScriptApiPauseMenu : virtual public ScriptApiBase
{
public:
void open_settings();
};

View file

@ -29,11 +29,14 @@ set(common_SCRIPT_LUA_API_SRCS
set(client_SCRIPT_LUA_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/l_camera.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_client.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_client_common.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_client_sound.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_localplayer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_mainmenu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_mainmenu_sound.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_menu_common.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_minimap.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_particles_local.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_pause_menu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_storage.cpp
PARENT_SCOPE)

View file

@ -127,21 +127,6 @@ int ModApiClient::l_get_player_names(lua_State *L)
return 1;
}
// show_formspec(formspec)
int ModApiClient::l_show_formspec(lua_State *L)
{
if (!lua_isstring(L, 1) || !lua_isstring(L, 2))
return 0;
ClientEvent *event = new ClientEvent();
event->type = CE_SHOW_LOCAL_FORMSPEC;
event->show_formspec.formname = new std::string(luaL_checkstring(L, 1));
event->show_formspec.formspec = new std::string(luaL_checkstring(L, 2));
getClient(L)->pushToEventQueue(event);
lua_pushboolean(L, true);
return 1;
}
// disconnect()
int ModApiClient::l_disconnect(lua_State *L)
{
@ -325,7 +310,6 @@ void ModApiClient::Initialize(lua_State *L, int top)
API_FCT(send_chat_message);
API_FCT(clear_out_chat_queue);
API_FCT(get_player_names);
API_FCT(show_formspec);
API_FCT(gettext);
API_FCT(get_node_or_nil);
API_FCT(disconnect);

View file

@ -33,9 +33,6 @@ private:
// get_player_names()
static int l_get_player_names(lua_State *L);
// show_formspec(name, formspec)
static int l_show_formspec(lua_State *L);
// disconnect()
static int l_disconnect(lua_State *L);

View file

@ -0,0 +1,33 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
// Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
// Copyright (C) 2025 grorp
#include "l_client_common.h"
#include "client/client.h"
#include "client/clientevent.h"
#include "cpp_api/s_base.h"
#include "lua_api/l_internal.h"
// show_formspec(formspec)
int ModApiClientCommon::l_show_formspec(lua_State *L)
{
ClientEvent *event = new ClientEvent();
event->type = getScriptApiBase(L)->getType() == ScriptingType::PauseMenu
? CE_SHOW_PAUSE_MENU_FORMSPEC
: CE_SHOW_CSM_FORMSPEC;
event->show_formspec.formname = new std::string(luaL_checkstring(L, 1));
event->show_formspec.formspec = new std::string(luaL_checkstring(L, 2));
getClient(L)->pushToEventQueue(event);
lua_pushboolean(L, true);
return 1;
}
void ModApiClientCommon::Initialize(lua_State *L, int top)
{
API_FCT(show_formspec);
}

View file

@ -0,0 +1,19 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
// Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
// Copyright (C) 2025 grorp
#pragma once
#include "lua_api/l_base.h"
class ModApiClientCommon : public ModApiBase
{
private:
// show_formspec(name, formspec)
static int l_show_formspec(lua_State *L);
public:
static void Initialize(lua_State *L, int top);
};

View file

@ -875,27 +875,6 @@ int ModApiMainMenu::l_download_file(lua_State *L)
return 1;
}
/******************************************************************************/
int ModApiMainMenu::l_get_video_drivers(lua_State *L)
{
auto drivers = RenderingEngine::getSupportedVideoDrivers();
lua_newtable(L);
for (u32 i = 0; i != drivers.size(); i++) {
auto &info = RenderingEngine::getVideoDriverInfo(drivers[i]);
lua_newtable(L);
lua_pushstring(L, info.name.c_str());
lua_setfield(L, -2, "name");
lua_pushstring(L, info.friendly_name.c_str());
lua_setfield(L, -2, "friendly_name");
lua_rawseti(L, -2, i + 1);
}
return 1;
}
/******************************************************************************/
int ModApiMainMenu::l_get_language(lua_State *L)
{
@ -907,16 +886,6 @@ int ModApiMainMenu::l_get_language(lua_State *L)
return 1;
}
/******************************************************************************/
int ModApiMainMenu::l_gettext(lua_State *L)
{
const char *srctext = luaL_checkstring(L, 1);
const char *text = *srctext ? gettext(srctext) : "";
lua_pushstring(L, text);
return 1;
}
/******************************************************************************/
int ModApiMainMenu::l_get_window_info(lua_State *L)
{
@ -949,14 +918,6 @@ int ModApiMainMenu::l_get_window_info(lua_State *L)
}
/******************************************************************************/
int ModApiMainMenu::l_get_active_driver(lua_State *L)
{
auto drivertype = RenderingEngine::get_video_driver()->getDriverType();
lua_pushstring(L, RenderingEngine::getVideoDriverInfo(drivertype).name.c_str());
return 1;
}
int ModApiMainMenu::l_get_active_renderer(lua_State *L)
{
lua_pushstring(L, RenderingEngine::get_video_driver()->getName());
@ -980,14 +941,6 @@ int ModApiMainMenu::l_get_active_irrlicht_device(lua_State *L)
return 1;
}
/******************************************************************************/
int ModApiMainMenu::l_irrlicht_device_supports_touch(lua_State *L)
{
lua_pushboolean(L, RenderingEngine::get_raw_device()->supportsTouchEvents());
return 1;
}
/******************************************************************************/
int ModApiMainMenu::l_get_min_supp_proto(lua_State *L)
{
@ -1122,13 +1075,9 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
API_FCT(show_path_select_dialog);
API_FCT(download_file);
API_FCT(get_language);
API_FCT(gettext);
API_FCT(get_video_drivers);
API_FCT(get_window_info);
API_FCT(get_active_driver);
API_FCT(get_active_renderer);
API_FCT(get_active_irrlicht_device);
API_FCT(irrlicht_device_supports_touch);
API_FCT(get_min_supp_proto);
API_FCT(get_max_supp_proto);
API_FCT(get_formspec_version);
@ -1165,5 +1114,4 @@ void ModApiMainMenu::InitializeAsync(lua_State *L, int top)
API_FCT(get_max_supp_proto);
API_FCT(get_formspec_version);
API_FCT(get_language);
API_FCT(gettext);
}

View file

@ -53,8 +53,6 @@ private:
static int l_get_language(lua_State *L);
static int l_gettext(lua_State *L);
//packages
static int l_get_games(lua_State *L);
@ -89,14 +87,10 @@ private:
static int l_get_window_info(lua_State *L);
static int l_get_active_driver(lua_State *L);
static int l_get_active_renderer(lua_State *L);
static int l_get_active_irrlicht_device(lua_State *L);
static int l_irrlicht_device_supports_touch(lua_State *L);
//filesystem
static int l_get_mainmenu_path(lua_State *L);
@ -133,8 +127,6 @@ private:
static int l_download_file(lua_State *L);
static int l_get_video_drivers(lua_State *L);
//version compatibility
static int l_get_min_supp_proto(lua_State *L);

View file

@ -0,0 +1,49 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2013 sapier
// Copyright (C) 2025 grorp
#include "l_menu_common.h"
#include "client/renderingengine.h"
#include "gettext.h"
#include "lua_api/l_internal.h"
int ModApiMenuCommon::l_gettext(lua_State *L)
{
const char *srctext = luaL_checkstring(L, 1);
const char *text = *srctext ? gettext(srctext) : "";
lua_pushstring(L, text);
return 1;
}
int ModApiMenuCommon::l_get_active_driver(lua_State *L)
{
auto drivertype = RenderingEngine::get_video_driver()->getDriverType();
lua_pushstring(L, RenderingEngine::getVideoDriverInfo(drivertype).name.c_str());
return 1;
}
int ModApiMenuCommon::l_irrlicht_device_supports_touch(lua_State *L)
{
lua_pushboolean(L, RenderingEngine::get_raw_device()->supportsTouchEvents());
return 1;
}
void ModApiMenuCommon::Initialize(lua_State *L, int top)
{
API_FCT(gettext);
API_FCT(get_active_driver);
API_FCT(irrlicht_device_supports_touch);
}
void ModApiMenuCommon::InitializeAsync(lua_State *L, int top)
{
API_FCT(gettext);
}

View file

@ -0,0 +1,20 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2013 sapier
// Copyright (C) 2025 grorp
#pragma once
#include "l_base.h"
class ModApiMenuCommon: public ModApiBase
{
private:
static int l_gettext(lua_State *L);
static int l_get_active_driver(lua_State *L);
static int l_irrlicht_device_supports_touch(lua_State *L);
public:
static void Initialize(lua_State *L, int top);
static void InitializeAsync(lua_State *L, int top);
};

View file

@ -0,0 +1,28 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2025 grorp
#include "l_pause_menu.h"
#include "gui/mainmenumanager.h"
#include "lua_api/l_internal.h"
int ModApiPauseMenu::l_show_keys_menu(lua_State *L)
{
g_gamecallback->keyConfig();
return 0;
}
int ModApiPauseMenu::l_show_touchscreen_layout(lua_State *L)
{
g_gamecallback->touchscreenLayout();
return 0;
}
void ModApiPauseMenu::Initialize(lua_State *L, int top)
{
API_FCT(show_keys_menu);
API_FCT(show_touchscreen_layout);
}

View file

@ -0,0 +1,17 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2025 grorp
#pragma once
#include "l_base.h"
class ModApiPauseMenu: public ModApiBase
{
private:
static int l_show_keys_menu(lua_State *L);
static int l_show_touchscreen_layout(lua_State *L);
public:
static void Initialize(lua_State *L, int top);
};

View file

@ -30,8 +30,9 @@
static inline int checkSettingSecurity(lua_State* L, const std::string &name)
{
#if CHECK_CLIENT_BUILD()
// Main menu is allowed everything
if (ModApiBase::getGuiEngine(L) != nullptr)
// Main menu and pause menu are allowed everything
auto context = ModApiBase::getScriptApiBase(L)->getType();
if (context == ScriptingType::MainMenu || context == ScriptingType::PauseMenu)
return 0;
#endif

View file

@ -7,6 +7,7 @@
#include "client/client.h"
#include "cpp_api/s_internal.h"
#include "lua_api/l_client.h"
#include "lua_api/l_client_common.h"
#include "lua_api/l_env.h"
#include "lua_api/l_item.h"
#include "lua_api/l_itemstackmeta.h"
@ -63,6 +64,7 @@ void ClientScripting::InitializeModApi(lua_State *L, int top)
ClientSoundHandle::Register(L);
ModApiUtil::InitializeClient(L, top);
ModApiClientCommon::Initialize(L, top);
ModApiClient::Initialize(L, top);
ModApiItem::InitializeClient(L, top);
ModApiStorage::Initialize(L, top);

View file

@ -9,6 +9,7 @@
#include "cpp_api/s_base.h"
#include "cpp_api/s_client.h"
#include "cpp_api/s_client_common.h"
#include "cpp_api/s_modchannels.h"
#include "cpp_api/s_security.h"
@ -20,6 +21,7 @@ class Minimap;
class ClientScripting:
virtual public ScriptApiBase,
public ScriptApiSecurity,
public ScriptApiClientCommon,
public ScriptApiClient,
public ScriptApiModChannels
{

View file

@ -9,6 +9,7 @@
#include "lua_api/l_http.h"
#include "lua_api/l_mainmenu.h"
#include "lua_api/l_mainmenu_sound.h"
#include "lua_api/l_menu_common.h"
#include "lua_api/l_util.h"
#include "lua_api/l_settings.h"
#include "log.h"
@ -53,12 +54,14 @@ void MainMenuScripting::initializeModApi(lua_State *L, int top)
registerLuaClasses(L, top);
// Initialize mod API modules
ModApiMenuCommon::Initialize(L, top);
ModApiMainMenu::Initialize(L, top);
ModApiUtil::Initialize(L, top);
ModApiMainMenuSound::Initialize(L, top);
ModApiHttp::Initialize(L, top);
asyncEngine.registerStateInitializer(registerLuaClasses);
asyncEngine.registerStateInitializer(ModApiMenuCommon::InitializeAsync);
asyncEngine.registerStateInitializer(ModApiMainMenu::InitializeAsync);
asyncEngine.registerStateInitializer(ModApiUtil::InitializeAsync);
asyncEngine.registerStateInitializer(ModApiHttp::InitializeAsync);

View file

@ -0,0 +1,68 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2025 grorp
#include "scripting_pause_menu.h"
#include "client/client.h"
#include "cpp_api/s_internal.h"
#include "filesys.h"
#include "lua_api/l_client_common.h"
#include "lua_api/l_menu_common.h"
#include "lua_api/l_pause_menu.h"
#include "lua_api/l_settings.h"
#include "lua_api/l_util.h"
#include "porting.h"
PauseMenuScripting::PauseMenuScripting(Client *client):
ScriptApiBase(ScriptingType::PauseMenu)
{
setGameDef(client);
SCRIPTAPI_PRECHECKHEADER
initializeSecurity();
lua_getglobal(L, "core");
int top = lua_gettop(L);
// Initialize our lua_api modules
initializeModApi(L, top);
lua_pop(L, 1);
// Push builtin initialization type
lua_pushstring(L, "pause_menu");
lua_setglobal(L, "INIT");
infostream << "SCRIPTAPI: Initialized pause menu modules" << std::endl;
}
void PauseMenuScripting::initializeModApi(lua_State *L, int top)
{
// Register reference classes (userdata)
LuaSettings::Register(L);
// Initialize mod API modules
ModApiPauseMenu::Initialize(L, top);
ModApiMenuCommon::Initialize(L, top);
ModApiClientCommon::Initialize(L, top);
ModApiUtil::Initialize(L, top);
}
void PauseMenuScripting::loadBuiltin()
{
loadScript(porting::path_share + DIR_DELIM "builtin" DIR_DELIM "init.lua");
checkSetByBuiltin();
}
bool PauseMenuScripting::checkPathInternal(const std::string &abs_path, bool write_required,
bool *write_allowed)
{
// NOTE: The pause menu env is on the same level of trust as the mainmenu env.
// However, since it doesn't need anything else at the moment, there's no
// reason to give it access to anything else.
if (write_required)
return false;
std::string path_share = fs::AbsolutePath(porting::path_share);
return !path_share.empty() && fs::PathStartsWith(abs_path, path_share + DIR_DELIM "builtin");
}

View file

@ -0,0 +1,28 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2025 grorp
#pragma once
#include "cpp_api/s_base.h"
#include "cpp_api/s_client_common.h"
#include "cpp_api/s_pause_menu.h"
#include "cpp_api/s_security.h"
class PauseMenuScripting:
virtual public ScriptApiBase,
public ScriptApiPauseMenu,
public ScriptApiClientCommon,
public ScriptApiSecurity
{
public:
PauseMenuScripting(Client *client);
void loadBuiltin();
protected:
bool checkPathInternal(const std::string &abs_path, bool write_required,
bool *write_allowed) override;
private:
void initializeModApi(lua_State *L, int top);
};