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

@ -9,6 +9,7 @@
#include "renderingengine.h"
#include "client.h"
#include "scripting_client.h"
#include "cpp_api/s_client_common.h"
#include "clientmap.h"
#include "gui/guiFormSpecMenu.h"
#include "gui/mainmenumanager.h"
@ -70,69 +71,73 @@ struct TextDestPlayerInventory : public TextDest
Client *m_client;
};
struct LocalFormspecHandler : public TextDest
struct LocalScriptingFormspecHandler : public TextDest
{
LocalFormspecHandler(const std::string &formname)
{
m_formname = formname;
}
LocalFormspecHandler(const std::string &formname, Client *client):
m_client(client)
LocalScriptingFormspecHandler(const std::string &formname, ScriptApiClientCommon *script)
{
m_formname = formname;
m_script = script;
}
void gotText(const StringMap &fields)
{
if (m_formname == "MT_PAUSE_MENU") {
if (fields.find("btn_sound") != fields.end()) {
g_gamecallback->changeVolume();
return;
}
m_script->on_formspec_input(m_formname, fields);
}
if (fields.find("btn_key_config") != fields.end()) {
g_gamecallback->keyConfig();
return;
}
ScriptApiClientCommon *m_script = nullptr;
};
if (fields.find("btn_touchscreen_layout") != fields.end()) {
g_gamecallback->touchscreenLayout();
return;
}
struct HardcodedPauseFormspecHandler : public TextDest
{
HardcodedPauseFormspecHandler()
{
m_formname = "MT_PAUSE_MENU";
}
if (fields.find("btn_exit_menu") != fields.end()) {
g_gamecallback->disconnect();
return;
}
void gotText(const StringMap &fields)
{
if (fields.find("btn_settings") != fields.end()) {
g_gamecallback->openSettings();
return;
}
if (fields.find("btn_exit_os") != fields.end()) {
g_gamecallback->exitToOS();
if (fields.find("btn_sound") != fields.end()) {
g_gamecallback->changeVolume();
return;
}
if (fields.find("btn_exit_menu") != fields.end()) {
g_gamecallback->disconnect();
return;
}
if (fields.find("btn_exit_os") != fields.end()) {
g_gamecallback->exitToOS();
#ifndef __ANDROID__
RenderingEngine::get_raw_device()->closeDevice();
RenderingEngine::get_raw_device()->closeDevice();
#endif
return;
}
if (fields.find("btn_change_password") != fields.end()) {
g_gamecallback->changePassword();
return;
}
return;
}
if (m_formname == "MT_DEATH_SCREEN") {
assert(m_client != nullptr);
if (fields.find("quit") != fields.end())
m_client->sendRespawnLegacy();
if (fields.find("btn_change_password") != fields.end()) {
g_gamecallback->changePassword();
return;
}
}
};
if (m_client->modsLoaded())
m_client->getScript()->on_formspec_input(m_formname, fields);
struct LegacyDeathFormspecHandler : public TextDest
{
LegacyDeathFormspecHandler(Client *client)
{
m_formname = "MT_DEATH_SCREEN";
m_client = client;
}
void gotText(const StringMap &fields)
{
if (fields.find("quit") != fields.end())
m_client->sendRespawnLegacy();
}
Client *m_client = nullptr;
@ -193,6 +198,15 @@ public:
//// GameFormSpec
void GameFormSpec::init(Client *client, RenderingEngine *rendering_engine, InputHandler *input)
{
m_client = client;
m_rendering_engine = rendering_engine;
m_input = input;
m_pause_script = std::make_unique<PauseMenuScripting>(client);
m_pause_script->loadBuiltin();
}
void GameFormSpec::deleteFormspec()
{
if (m_formspec) {
@ -208,35 +222,72 @@ GameFormSpec::~GameFormSpec() {
this->deleteFormspec();
}
void GameFormSpec::showFormSpec(const std::string &formspec, const std::string &formname)
bool GameFormSpec::handleEmptyFormspec(const std::string &formspec, const std::string &formname)
{
if (formspec.empty()) {
if (m_formspec && (formname.empty() || formname == m_formname)) {
m_formspec->quitMenu();
}
} else {
FormspecFormSource *fs_src =
new FormspecFormSource(formspec);
TextDestPlayerInventory *txt_dst =
new TextDestPlayerInventory(m_client, formname);
m_formname = formname;
GUIFormSpecMenu::create(m_formspec, m_client, m_rendering_engine->get_gui_env(),
&m_input->joystick, fs_src, txt_dst, m_client->getFormspecPrepend(),
m_client->getSoundManager());
return true;
}
return false;
}
void GameFormSpec::showLocalFormSpec(const std::string &formspec, const std::string &formname)
void GameFormSpec::showFormSpec(const std::string &formspec, const std::string &formname)
{
if (handleEmptyFormspec(formspec, formname))
return;
FormspecFormSource *fs_src =
new FormspecFormSource(formspec);
TextDestPlayerInventory *txt_dst =
new TextDestPlayerInventory(m_client, formname);
m_formname = formname;
GUIFormSpecMenu::create(m_formspec, m_client, m_rendering_engine->get_gui_env(),
&m_input->joystick, fs_src, txt_dst, m_client->getFormspecPrepend(),
m_client->getSoundManager());
}
void GameFormSpec::showCSMFormSpec(const std::string &formspec, const std::string &formname)
{
if (handleEmptyFormspec(formspec, formname))
return;
FormspecFormSource *fs_src = new FormspecFormSource(formspec);
LocalFormspecHandler *txt_dst =
new LocalFormspecHandler(formname, m_client);
LocalScriptingFormspecHandler *txt_dst =
new LocalScriptingFormspecHandler(formname, m_client->getScript());
m_formname = formname;
GUIFormSpecMenu::create(m_formspec, m_client, m_rendering_engine->get_gui_env(),
&m_input->joystick, fs_src, txt_dst, m_client->getFormspecPrepend(),
m_client->getSoundManager());
}
void GameFormSpec::showPauseMenuFormSpec(const std::string &formspec, const std::string &formname)
{
// The pause menu env is a trusted context like the mainmenu env and provides
// the in-game settings formspec.
// Neither CSM nor the server must be allowed to mess with it.
if (handleEmptyFormspec(formspec, formname))
return;
FormspecFormSource *fs_src = new FormspecFormSource(formspec);
LocalScriptingFormspecHandler *txt_dst =
new LocalScriptingFormspecHandler(formname, m_pause_script.get());
m_formname = formname;
GUIFormSpecMenu::create(m_formspec, m_client, m_rendering_engine->get_gui_env(),
// Ignore formspec prepend.
&m_input->joystick, fs_src, txt_dst, "",
m_client->getSoundManager());
// FIXME: can't enable this for now because "fps_max_unfocused" also applies
// when the game is paused, making the settings menu much less enjoyable.
// m_formspec->doPause = true;
}
void GameFormSpec::showNodeFormspec(const std::string &formspec, const v3s16 &nodepos)
{
infostream << "Launching custom inventory view" << std::endl;
@ -331,6 +382,9 @@ void GameFormSpec::showPauseMenu()
os << "field[4.95,0;5,1.5;;" << strgettext("Game paused") << ";]";
}
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_settings;"
<< strgettext("Settings") << "]";
#ifndef __ANDROID__
#if USE_SOUND
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;"
@ -338,13 +392,6 @@ void GameFormSpec::showPauseMenu()
#endif
#endif
if (g_touchcontrols) {
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_touchscreen_layout;"
<< strgettext("Touchscreen Layout") << "]";
} else {
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;"
<< strgettext("Controls") << "]";
}
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;"
<< strgettext("Exit to Menu") << "]";
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
@ -394,13 +441,13 @@ void GameFormSpec::showPauseMenu()
/* Note: FormspecFormSource and LocalFormspecHandler *
* are deleted by guiFormSpecMenu */
FormspecFormSource *fs_src = new FormspecFormSource(os.str());
LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU");
HardcodedPauseFormspecHandler *txt_dst = new HardcodedPauseFormspecHandler();
GUIFormSpecMenu::create(m_formspec, m_client, m_rendering_engine->get_gui_env(),
&m_input->joystick, fs_src, txt_dst, m_client->getFormspecPrepend(),
m_client->getSoundManager());
m_formspec->setFocus("btn_continue");
// game will be paused in next step, if in singleplayer (see m_is_paused)
// game will be paused in next step, if in singleplayer (see Game::m_is_paused)
m_formspec->doPause = true;
}
@ -418,7 +465,7 @@ void GameFormSpec::showDeathFormspecLegacy()
/* Note: FormspecFormSource and LocalFormspecHandler *
* are deleted by guiFormSpecMenu */
FormspecFormSource *fs_src = new FormspecFormSource(formspec_str);
LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", m_client);
LegacyDeathFormspecHandler *txt_dst = new LegacyDeathFormspecHandler(m_client);
GUIFormSpecMenu::create(m_formspec, m_client, m_rendering_engine->get_gui_env(),
&m_input->joystick, fs_src, txt_dst, m_client->getFormspecPrepend(),
@ -473,6 +520,11 @@ bool GameFormSpec::handleCallbacks()
return false;
}
if (g_gamecallback->settings_requested) {
m_pause_script->open_settings();
g_gamecallback->settings_requested = false;
}
if (g_gamecallback->changepassword_requested) {
(void)make_irr<GUIPasswordChange>(guienv, guiroot, -1,
&g_menumgr, m_client, texture_src);