diff --git a/builtin/client/init.lua b/builtin/client/init.lua index 301a8050c..150711630 100644 --- a/builtin/client/init.lua +++ b/builtin/client/init.lua @@ -10,5 +10,6 @@ dofile(commonpath .. "chatcommands.lua") dofile(commonpath .. "information_formspecs.lua") dofile(clientpath .. "chatcommands.lua") dofile(clientpath .. "death_formspec.lua") +dofile(clientpath .. "pause_menu.lua"); dofile(clientpath .. "misc.lua") assert(loadfile(commonpath .. "item_s.lua"))({}) -- Just for push/read node functions diff --git a/builtin/client/pause_menu.lua b/builtin/client/pause_menu.lua new file mode 100644 index 000000000..7d2481ded --- /dev/null +++ b/builtin/client/pause_menu.lua @@ -0,0 +1,261 @@ +local SIZE_TAG = "size[11,5.5,true]" + +local function avoid_noid() + return "label[1,1;Avoid the Noid!]" +end + +local function menu_formspec(simple_singleplayer_mode, is_touchscreen, address) + local ypos = simple_singleplayer_mode and 0.7 or 0.1 + local control_text = "" + + if is_touchscreen then + control_text = fgettext([[Controls: +No menu open: +- slide finger: look around +- tap: place/punch/use (default) +- long tap: dig/use (default) +Menu/inventory open: +- double tap (outside): + --> close +- touch stack, touch slot: + --> move stack +- touch&drag, tap 2nd finger + --> place single item to slot +]]) + end + + local fs = { + "formspec_version[1]", + SIZE_TAG, + ("button_exit[4,%f;3,0.5;btn_continue;%s]"):format(ypos, fgettext("Continue")) + } + ypos = ypos + 1 + + if not simple_singleplayer_mode then + fs[#fs + 1] = ("button_exit[4,%f;3,0.5;btn_change_password;%s]"):format( + ypos, fgettext("Change Password")) + else + fs[#fs + 1] = "field[4.95,0;5,1.5;;" .. fgettext("Game paused") .. ";]" + end + + fs[#fs + 1] = ("button_exit[4,%f;3,0.5;btn_key_config;%s]"):format( + ypos, fgettext("Controls")) + ypos = ypos + 1 + fs[#fs + 1] = ("button_exit[4,%f;3,0.5;btn_settings;%s]"):format( + ypos, fgettext("Settings")) + ypos = ypos + 1 + fs[#fs + 1] = ("button_exit[4,%f;3,0.5;btn_exit_menu;%s]"):format( + ypos, fgettext("Exit to Menu")) + ypos = ypos + 1 + fs[#fs + 1] = ("button_exit[4,%f;3,0.5;btn_exit_os;%s]"):format( + ypos, fgettext("Exit to OS")) + ypos = ypos + 1 + + -- Controls + if control_text ~= "" then + fs[#fs + 1] = ("textarea[7.5,0.25;3.9,6.25;;%s;]"):format(control_text) + end + + -- Server info + local info = minetest.get_version() + fs[#fs + 1] = ("textarea[0.4,0.25;3.9,6.25;;%s %s\n\n%s\n"):format( + info.project, info.hash or info.string, fgettext("Game info:")) + + fs[#fs + 1] = "- Mode: " .. (simple_singleplayer_mode and "Singleplayer" or + ((not address) and "Hosting server" or "Remote server")) + + if not address then + local enable_damage = minetest.settings:get_bool("enable_damage") + local enable_pvp = minetest.settings:get_bool("enable_pvp") + local server_announce = minetest.settings:get_bool("server_announce") + local server_name = minetest.settings:get("server_name") + table.insert_all(fs, { + "\n", + enable_damage and + ("- PvP: " .. (enable_pvp and "On" or "Off")) or "", + "\n", + "- Public: " .. (server_announce and "On" or "Off"), + "\n", + (server_announce and server_name) and + ("- Server Name: " .. minetest.formspec_escape(server_name)) or "" + }) + end + + fs[#fs + 1] = ";]" + + + return table.concat(fs, "") +end + +function core.show_pause_menu(is_singleplayer, is_touchscreen, address) + minetest.show_formspec("builtin:MT_PAUSE_MENU", menu_formspec(is_singleplayer, is_touchscreen, address)) +end + +core.register_on_formspec_input(function(formname, fields) + if formname ~= "builtin:MT_PAUSE_MENU" then return end + + if fields.btn_continue then + core.unpause() + elseif fields.btn_key_config then + core.key_config() -- Don't want this + elseif fields.btn_change_password then + core.change_password() + elseif fields.btn_settings then + core.show_settings() + elseif fields.btn_exit_menu then + core.disconnect() + elseif fields.btn_exit_os then + core.exit_to_os() + end + + return +end) + +local scriptpath = core.get_builtin_path() +local path = scriptpath.."mainmenu"..DIR_DELIM.."settings" + +function core.get_mainmenu_path() + return scriptpath.."mainmenu" +end + +defaulttexturedir = "" +dofile(path .. DIR_DELIM .. "settingtypes.lua") +dofile(path .. DIR_DELIM .. "dlg_change_mapgen_flags.lua") +dofile(path .. DIR_DELIM .. "dlg_settings.lua") + +function dialog_create(name, spec, buttonhandler, eventhandler) + minetest.show_formspec(name, spec({})) +end + + +local settings_data = {} +settings_data.data = { + leftscroll = 0, + query = "", + rightscroll = 0, + components = {}, + page_id = "accessibility" +} + +core.register_on_formspec_input(function(formname, fields) + if formname ~= "builtin:MT_PAUSE_MENU_SETTINGS" then return true end + --local this = data + --buttonhandler(settings_data, fields) + local dialogdata = settings_data.data + dialogdata.leftscroll = core.explode_scrollbar_event(fields.leftscroll).value or dialogdata.leftscroll + dialogdata.rightscroll = core.explode_scrollbar_event(fields.rightscroll).value or dialogdata.rightscroll + dialogdata.query = fields.search_query + local update = false + + if fields.back then + this:delete() + return true + end + + if fields.show_technical_names ~= nil then + local value = core.is_yes(fields.show_technical_names) + core.settings:set_bool("show_technical_names", value) + write_settings_early() + update = true + --return true + end + + if fields.show_advanced ~= nil then + local value = core.is_yes(fields.show_advanced) + core.settings:set_bool("show_advanced", value) + write_settings_early() + core.show_settings() + update = true + end + + -- enable_touch is a checkbox in a setting component. We handle this + -- setting differently so we can hide/show pages using the next if-statement + if fields.enable_touch ~= nil then + local value = core.is_yes(fields.enable_touch) + core.settings:set_bool("enable_touch", value) + write_settings_early() + core.show_settings() + update = true + end + + if fields.show_advanced ~= nil or fields.enable_touch ~= nil then + local suggested_page_id = update_filtered_pages(dialogdata.query) + + dialogdata.components = nil + + if not filtered_page_by_id[dialogdata.page_id] then + dialogdata.leftscroll = 0 + dialogdata.rightscroll = 0 + + dialogdata.page_id = suggested_page_id + end + + return true + end + + if fields.search or fields.key_enter_field == "search_query" then + dialogdata.components = nil + dialogdata.leftscroll = 0 + dialogdata.rightscroll = 0 + + dialogdata.page_id = update_filtered_pages(dialogdata.query) + + return true + end + if fields.search_clear then + dialogdata.query = "" + dialogdata.components = nil + dialogdata.leftscroll = 0 + dialogdata.rightscroll = 0 + + dialogdata.page_id = update_filtered_pages("") + return true + end + + for _, page in ipairs(all_pages) do + if fields["page_" .. page.id] then + dialogdata.page_id = page.id + dialogdata.components = nil + dialogdata.rightscroll = 0 + core.show_settings() + core.reload_graphics() + return true + end + end + + if dialogdata.components then + for i, comp in ipairs(dialogdata.components) do + if comp.on_submit and comp:on_submit(fields, this) then + write_settings_early() + core.show_settings() + + -- Clear components so they regenerate + --dialogdata.components = nil + return true + end + if comp.setting and fields["reset_" .. i] then + core.settings:remove(comp.setting.name) + write_settings_early() + core.show_settings() + + -- Clear components so they regenerate + --dialogdata.components = nil + return true + end + end + end + + if update then + core.show_settings() + end + + return false +end) + +load(true, false) +--settings_data.data.page_id = update_filtered_pages("") + +function core.show_settings() + show_settings_client_formspec("builtin:MT_PAUSE_MENU_SETTINGS", settings_data.data) + core.unpause() +end diff --git a/builtin/mainmenu/settings/dlg_settings.lua b/builtin/mainmenu/settings/dlg_settings.lua index 73a72769b..d9691d13b 100644 --- a/builtin/mainmenu/settings/dlg_settings.lua +++ b/builtin/mainmenu/settings/dlg_settings.lua @@ -23,13 +23,13 @@ local shadows_component = dofile(core.get_mainmenu_path() .. DIR_DELIM .. "settings" .. DIR_DELIM .. "shadows_component.lua") local loaded = false -local full_settings + full_settings = {} local info_icon_path = core.formspec_escape(defaulttexturedir .. "settings_info.png") local reset_icon_path = core.formspec_escape(defaulttexturedir .. "settings_reset.png") -local all_pages = {} -local page_by_id = {} -local filtered_pages = all_pages -local filtered_page_by_id = page_by_id + all_pages = {} + page_by_id = {} + filtered_pages = all_pages + filtered_page_by_id = page_by_id local function get_setting_info(name) @@ -99,13 +99,16 @@ local function load_settingtypes() end -local function load() +function load(read_all, parse_mods) + read_all = read_all == nil and false or read_all + parse_mods = parse_mods == nil and true or parse_mods + if loaded then return end loaded = true - full_settings = settingtypes.parse_config_file(false, true) + full_settings = settingtypes.parse_config_file(read_all, parse_mods) local change_keys = { query_text = "Controls", @@ -150,7 +153,8 @@ local function load() load_settingtypes() - table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys) + if page_by_id.controls_keyboard_and_mouse then + table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys) do local content = page_by_id.graphics_and_audio_shaders.content local idx = table.indexof(content, "enable_dynamic_shadows") @@ -222,6 +226,8 @@ local function load() zh_CN = "中文 (简体) [zh_CN]", zh_TW = "正體中文 (繁體) [zh_TW]", } + end + end @@ -282,7 +288,7 @@ local function filter_page_content(page, query_keywords) end -local function update_filtered_pages(query) +function update_filtered_pages(query) filtered_pages = {} filtered_page_by_id = {} @@ -627,11 +633,13 @@ function write_settings_early() end -local function buttonhandler(this, fields) +function buttonhandler(this, fields) local dialogdata = this.data dialogdata.leftscroll = core.explode_scrollbar_event(fields.leftscroll).value or dialogdata.leftscroll dialogdata.rightscroll = core.explode_scrollbar_event(fields.rightscroll).value or dialogdata.rightscroll dialogdata.query = fields.search_query + + minetest.log(dump(fields)) if fields.back then this:delete() @@ -703,6 +711,7 @@ local function buttonhandler(this, fields) end end + if dialogdata.components then for i, comp in ipairs(dialogdata.components) do if comp.on_submit and comp:on_submit(fields, this) then write_settings_early() @@ -720,6 +729,7 @@ local function buttonhandler(this, fields) return true end end + end return false end @@ -746,6 +756,10 @@ function create_settings_dlg() local dlg = dialog_create("dlg_settings", get_formspec, buttonhandler, eventhandler) dlg.data.page_id = update_filtered_pages("") - + return dlg end + +function show_settings_client_formspec(name, data) + minetest.show_formspec(name or "dlg_settings", get_formspec(data)) +end diff --git a/builtin/mainmenu/settings/settingtypes.lua b/builtin/mainmenu/settings/settingtypes.lua index eacd96d09..4e7d9067f 100644 --- a/builtin/mainmenu/settings/settingtypes.lua +++ b/builtin/mainmenu/settings/settingtypes.lua @@ -396,10 +396,10 @@ function settingtypes.parse_config_file(read_all, parse_mods) local settings = {} do - local builtin_path = core.get_builtin_path() .. FILENAME + local builtin_path = (core.get_true_builtin_path and core.get_true_builtin_path() or core.get_builtin_path()) .. FILENAME local file = io.open(builtin_path, "r") if not file then - core.log("error", "Can't load " .. FILENAME) + core.log("error", "Can't load " .. builtin_path) return settings end diff --git a/src/client/camera.h b/src/client/camera.h index 88533181b..0c432ac8d 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -228,7 +228,9 @@ class Camera Client *m_client; // Default Client FOV (as defined by the "fov" setting) +public: // TODO make a setter f32 m_cache_fov; +private: // Absolute camera position v3f m_camera_position; diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index 7e1676ffe..7d38b538c 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -273,6 +273,9 @@ void ClientEnvironment::step(float dtime) auto cb_state = [this, dtime, update_lighting, day_night_ratio] (ClientActiveObject *cao) { // Step object cao->step(dtime, this); + + if (m_update_shadows) + cao->updateSceneShadows(); if (update_lighting) cao->updateLight(day_night_ratio); @@ -296,6 +299,9 @@ void ClientEnvironment::step(float dtime) ++i; } } + + if (m_update_shadows) + m_update_shadows = false; } void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple) diff --git a/src/client/clientenvironment.h b/src/client/clientenvironment.h index bdb8b9726..8f6e4350b 100644 --- a/src/client/clientenvironment.h +++ b/src/client/clientenvironment.h @@ -142,13 +142,14 @@ class ClientEnvironment : public Environment const std::set &getPlayerNames() { return m_player_names; } void addPlayerName(const std::string &name) { m_player_names.insert(name); } void removePlayerName(const std::string &name) { m_player_names.erase(name); } - void updateCameraOffset(const v3s16 &camera_offset) - { m_camera_offset = camera_offset; } + void updateCameraOffset(const v3s16 &camera_offset) { m_camera_offset = camera_offset; } + void requestUpdateShadows() { m_update_shadows = true; } v3s16 getCameraOffset() const { return m_camera_offset; } void updateFrameTime(bool is_paused); u64 getFrameTime() const { return m_frame_time; } u64 getFrameTimeDelta() const { return m_frame_dtime; } + private: ClientMap *m_map; @@ -162,6 +163,7 @@ class ClientEnvironment : public Environment IntervalLimiter m_active_object_light_update_interval; std::set m_player_names; v3s16 m_camera_offset; + bool m_update_shadows = false; u64 m_frame_time = 0; u64 m_frame_dtime = 0; u64 m_frame_time_pause_accumulator = 0; diff --git a/src/client/clientobject.h b/src/client/clientobject.h index f63681313..83ef1e346 100644 --- a/src/client/clientobject.h +++ b/src/client/clientobject.h @@ -62,6 +62,7 @@ class ClientActiveObject : public ActiveObject virtual void updateAttachments() {}; virtual bool doShowSelectionBox() { return true; } + virtual void updateSceneShadows() {} // Step object in time virtual void step(float dtime, ClientEnvironment *env) {} diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 0044cc16e..e0207719a 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -604,6 +604,17 @@ void GenericCAO::removeFromScene(bool permanent) m_client->getMinimap()->removeMarker(&m_marker); } +void GenericCAO::updateSceneShadows() +{ + if (scene::ISceneNode *node = getSceneNode()) { + if (m_matrixnode) + node->setParent(m_matrixnode); + + if (auto shadow = RenderingEngine::get_shadow_renderer()) + shadow->addNodeToShadowList(node); + } +} + void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) { m_smgr = smgr; @@ -833,6 +844,8 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) if (m_reset_textures_timer < 0) updateTextures(m_current_texture_modifier); + + if (scene::ISceneNode *node = getSceneNode()) { if (m_matrixnode) node->setParent(m_matrixnode); diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 7fdcb73da..8e3a507bb 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -249,6 +249,7 @@ class GenericCAO : public ClientActiveObject } void updateLight(u32 day_night_ratio) override; + void updateSceneShadows() override; void setNodeLight(const video::SColor &light); diff --git a/src/client/game.cpp b/src/client/game.cpp index 88892beb5..8cbfc59e2 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -149,35 +149,22 @@ struct LocalFormspecHandler : public TextDest void gotText(const StringMap &fields) { - if (m_formname == "MT_PAUSE_MENU") { - if (fields.find("btn_sound") != fields.end()) { - g_gamecallback->changeVolume(); - return; - } + if (m_formname == "MT_PAUSE_MENU_SETTINGS" && false) + { + // Loop through settings + for (auto i : fields) + { + if (g_settings->existsLocal(i.first) && g_settings->get(i.first) != i.second) + { + g_settings->set(i.first, i.second); + } + if (i.first.rfind("page_", 0) == 0) + { + //m_client->getScript()->show_settings(i.first.substr(5)); + } + //std::cout << "Setting " << i.first << " set!" << std::endl; - if (fields.find("btn_key_config") != fields.end()) { - g_gamecallback->keyConfig(); - 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(); -#endif - return; - } - - if (fields.find("btn_change_password") != fields.end()) { - g_gamecallback->changePassword(); - return; - } - return; } @@ -752,6 +739,7 @@ class Game { void updateCameraOrientation(CameraOrientation *cam, float dtime); void updatePlayerControl(const CameraOrientation &cam); void updatePauseState(); + void reloadGraphics(); void step(f32 dtime); void processClientEvents(CameraOrientation *cam); void updateCamera(f32 dtime); @@ -1012,6 +1000,8 @@ Game::Game() : &settingChangedCallback, this); g_settings->registerChangedCallback("pause_on_lost_focus", &settingChangedCallback, this); + g_settings->registerChangedCallback("fov", + &settingChangedCallback, this); readSettings(); } @@ -1137,6 +1127,12 @@ bool Game::startup(bool *kill, return true; } +inline void Game::reloadGraphics() +{ + m_rendering_engine->initialize(client, hud); + client->getEnv().requestUpdateShadows(); + +} void Game::run() { @@ -1188,7 +1184,7 @@ void Game::run() client->sendUpdateClientInfo(current_dynamic_info); } } - + // Prepare render data for next iteration updateStats(&stats, draw_times, dtime); @@ -1813,6 +1809,8 @@ bool Game::getServerContent(bool *aborted) } + + /****************************************************************************/ /**************************************************************************** Run @@ -1871,6 +1869,27 @@ inline bool Game::handleCallbacks() (new GUIKeyChangeMenu(guienv, guiroot, -1, &g_menumgr, texture_src))->drop(); g_gamecallback->keyconfig_requested = false; + m_is_paused = false; + } + + if (g_gamecallback->unpause_requested) { + m_is_paused = false; + m_rendering_engine->initialize(client, hud); + g_gamecallback->unpause_requested = false; + } + + if (g_gamecallback->reload_graphics_requested) { + reloadGraphics(); + g_gamecallback->reload_graphics_requested = false; + } + + if (g_gamecallback->show_settings_requested) { + if (client->modsLoaded()) + { + client->getScript()->show_settings(); + m_is_paused = false; + } + g_gamecallback->show_settings_requested = false; } if (!g_gamecallback->show_open_url_dialog.empty()) { @@ -1907,7 +1926,9 @@ void Game::updateDebugState() m_game_ui->m_flags.show_basic_debug = false; } else if (m_game_ui->m_flags.show_minimal_debug) { if (has_basic_debug) + { m_game_ui->m_flags.show_basic_debug = true; + } } if (!has_basic_debug) hud->disableBlockBounds(); @@ -2519,6 +2540,7 @@ void Game::toggleDebug() } else if (!m_game_ui->m_flags.show_profiler_graph && !draw_control->show_wireframe) { if (has_basic_debug) m_game_ui->m_flags.show_basic_debug = true; + reloadGraphics(); m_game_ui->m_flags.show_profiler_graph = true; m_game_ui->showTranslatedStatusText("Profiler graph shown"); } else if (!draw_control->show_wireframe && client->checkPrivilege("debug")) { @@ -2751,7 +2773,7 @@ void Game::updatePlayerControl(const CameraOrientation &cam) void Game::updatePauseState() { bool was_paused = this->m_is_paused; - this->m_is_paused = this->simple_singleplayer_mode && g_menumgr.pausesGame(); + //this->m_is_paused = this->simple_singleplayer_mode && g_menumgr.pausesGame(); if (!was_paused && this->m_is_paused) { this->pauseAnimation(); @@ -4397,13 +4419,17 @@ void Game::readSettings() m_cache_enable_noclip = g_settings->getBool("noclip"); m_cache_enable_free_move = g_settings->getBool("free_move"); + m_cache_cam_smoothing = 0; if (g_settings->getBool("cinematic")) m_cache_cam_smoothing = 1 - g_settings->getFloat("cinematic_camera_smoothing"); else m_cache_cam_smoothing = 1 - g_settings->getFloat("camera_smoothing"); - + + if (camera) + camera->m_cache_fov = g_settings->getFloat("fov"); + m_cache_cam_smoothing = rangelim(m_cache_cam_smoothing, 0.01f, 1.0f); m_cache_mouse_sensitivity = rangelim(m_cache_mouse_sensitivity, 0.001, 100.0); @@ -4446,106 +4472,13 @@ void Game::showDeathFormspec() #define GET_KEY_NAME(KEY) gettext(getKeySetting(#KEY).name()) void Game::showPauseMenu() { - std::string control_text; - - if (g_touchscreengui) { - control_text = strgettext("Controls:\n" - "No menu open:\n" - "- slide finger: look around\n" - "- tap: place/punch/use (default)\n" - "- long tap: dig/use (default)\n" - "Menu/inventory open:\n" - "- double tap (outside):\n" - " --> close\n" - "- touch stack, touch slot:\n" - " --> move stack\n" - "- touch&drag, tap 2nd finger\n" - " --> place single item to slot\n" - ); + if (client->modsLoaded()) + { + client->getScript()->show_pause_menu(simple_singleplayer_mode, + g_touchscreengui, + client->getAddressName()); + m_is_paused = true; } - - float ypos = simple_singleplayer_mode ? 0.7f : 0.1f; - std::ostringstream os; - - os << "formspec_version[1]" << SIZE_TAG - << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;" - << strgettext("Continue") << "]"; - - if (!simple_singleplayer_mode) { - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;" - << strgettext("Change Password") << "]"; - } else { - os << "field[4.95,0;5,1.5;;" << strgettext("Game paused") << ";]"; - } - -#ifndef __ANDROID__ -#if USE_SOUND - if (g_settings->getBool("enable_sound")) { - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;" - << strgettext("Sound Volume") << "]"; - } -#endif - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;" - << strgettext("Controls") << "]"; -#endif - 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;" - << strgettext("Exit to OS") << "]"; - if (!control_text.empty()) { - os << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"; - } - os << "textarea[0.4,0.25;3.9,6.25;;" << PROJECT_NAME_C " " VERSION_STRING "\n" - << "\n" - << strgettext("Game info:") << "\n"; - const std::string &address = client->getAddressName(); - os << strgettext("- Mode: "); - if (!simple_singleplayer_mode) { - if (address.empty()) - os << strgettext("Hosting server"); - else - os << strgettext("Remote server"); - } else { - os << strgettext("Singleplayer"); - } - os << "\n"; - if (simple_singleplayer_mode || address.empty()) { - static const std::string on = strgettext("On"); - static const std::string off = strgettext("Off"); - // Note: Status of enable_damage and creative_mode settings is intentionally - // NOT shown here because the game might roll its own damage system and/or do - // a per-player Creative Mode, in which case writing it here would mislead. - bool damage = g_settings->getBool("enable_damage"); - const std::string &announced = g_settings->getBool("server_announce") ? on : off; - if (!simple_singleplayer_mode) { - if (damage) { - const std::string &pvp = g_settings->getBool("enable_pvp") ? on : off; - //~ PvP = Player versus Player - os << strgettext("- PvP: ") << pvp << "\n"; - } - os << strgettext("- Public: ") << announced << "\n"; - std::string server_name = g_settings->get("server_name"); - str_formspec_escape(server_name); - if (announced == on && !server_name.empty()) - os << strgettext("- Server Name: ") << server_name; - - } - } - os << ";]"; - - /* Create menu */ - /* Note: FormspecFormSource and LocalFormspecHandler * - * are deleted by guiFormSpecMenu */ - FormspecFormSource *fs_src = new FormspecFormSource(os.str()); - LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU"); - - auto *&formspec = m_game_ui->getFormspecGUI(); - GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(), - &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), - sound_manager.get()); - formspec->setFocus("btn_continue"); - // game will be paused in next step, if in singleplayer (see m_is_paused) - formspec->doPause = true; } /****************************************************************************/ diff --git a/src/client/render/factory.cpp b/src/client/render/factory.cpp index 2e9d5eb5f..dbb33e9f3 100644 --- a/src/client/render/factory.cpp +++ b/src/client/render/factory.cpp @@ -82,4 +82,4 @@ void createPipeline(const std::string &stereo_mode, IrrlichtDevice *device, Clie // fallback to plain renderer errorstream << "Invalid rendering mode: " << stereo_mode << std::endl; populatePlainPipeline(result.pipeline, client); -} \ No newline at end of file +} diff --git a/src/gui/mainmenumanager.h b/src/gui/mainmenumanager.h index 25ff475f4..08c5a9242 100644 --- a/src/gui/mainmenumanager.h +++ b/src/gui/mainmenumanager.h @@ -34,6 +34,9 @@ class IGameCallback virtual void disconnect() = 0; virtual void changePassword() = 0; virtual void changeVolume() = 0; + virtual void unpause() = 0; + virtual void reloadGraphics() = 0; + virtual void showSettings() = 0; virtual void showOpenURLDialog(const std::string &url) = 0; virtual void signalKeyConfigChange() = 0; }; @@ -146,12 +149,30 @@ class MainGameCallback : public IGameCallback { show_open_url_dialog = url; } + + void unpause() override + { + unpause_requested = true; + } + + void reloadGraphics() override + { + reload_graphics_requested = true; + } + + void showSettings() override + { + show_settings_requested = true; + } bool disconnect_requested = false; bool changepassword_requested = false; bool changevolume_requested = false; bool keyconfig_requested = false; bool shutdown_requested = false; + bool unpause_requested = false; + bool reload_graphics_requested = false; + bool show_settings_requested = false; bool keyconfig_changed = false; std::string show_open_url_dialog = ""; }; diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index e9907f304..7a363d96a 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -184,6 +184,7 @@ void ScriptApiBase::clientOpenLibs(lua_State *L) { "", luaopen_base }, { LUA_TABLIBNAME, luaopen_table }, { LUA_OSLIBNAME, luaopen_os }, + { LUA_IOLIBNAME, luaopen_io }, { LUA_STRLIBNAME, luaopen_string }, { LUA_MATHLIBNAME, luaopen_math }, { LUA_DBLIBNAME, luaopen_debug }, diff --git a/src/script/cpp_api/s_client.cpp b/src/script/cpp_api/s_client.cpp index 6faa0695c..5af8758d4 100644 --- a/src/script/cpp_api/s_client.cpp +++ b/src/script/cpp_api/s_client.cpp @@ -290,6 +290,39 @@ bool ScriptApiClient::on_inventory_open(Inventory *inventory) return readParam(L, -1); } +void ScriptApiClient::show_pause_menu(bool is_singleplayer, bool is_touchscreen, const std::string& server_address) +{ + SCRIPTAPI_PRECHECKHEADER + + int error_handler = PUSH_ERROR_HANDLER(L); + + lua_getglobal(L, "core"); + lua_getfield(L, -1, "show_pause_menu"); + + lua_pushboolean(L, is_singleplayer); + lua_pushboolean(L, is_touchscreen); + if (!server_address.empty()) + lua_pushstring(L, server_address.c_str()); + else + lua_pushnil(L); + + PCALL_RES(lua_pcall(L, 3, 0, error_handler)); + lua_pop(L, 1); +} + +void ScriptApiClient::show_settings() +{ + SCRIPTAPI_PRECHECKHEADER + + int error_handler = PUSH_ERROR_HANDLER(L); + + lua_getglobal(L, "core"); + lua_getfield(L, -1, "show_settings"); + + PCALL_RES(lua_pcall(L, 0, 0, error_handler)); + lua_pop(L, 1); +} + void ScriptApiClient::setEnv(ClientEnvironment *env) { ScriptApiBase::setEnv(env); diff --git a/src/script/cpp_api/s_client.h b/src/script/cpp_api/s_client.h index 8a5523d0d..61efa40bd 100644 --- a/src/script/cpp_api/s_client.h +++ b/src/script/cpp_api/s_client.h @@ -59,6 +59,9 @@ class ScriptApiClient : virtual public ScriptApiBase bool on_item_use(const ItemStack &item, const PointedThing &pointed); bool on_inventory_open(Inventory *inventory); + + void show_pause_menu(bool is_singleplayer, bool is_touchscreen, const std::string& server_address); + void show_settings(); void setEnv(ClientEnvironment *env); }; diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index 7c8ba8931..e26535f0d 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -112,6 +112,7 @@ void ScriptApiSecurity::initializeSecurity() "bit" }; static const char *io_whitelist[] = { + "open", "close", "flush", "read", @@ -310,6 +311,14 @@ void ScriptApiSecurity::initializeSecurityClient() "difftime", "time" }; + static const char *io_whitelist[] = { + "close", + "open", + "flush", + "read", + "type", + "write", + }; static const char *debug_whitelist[] = { "getinfo", // used by builtin and unset before mods load "traceback" @@ -358,6 +367,13 @@ void ScriptApiSecurity::initializeSecurityClient() copy_safe(L, os_whitelist, sizeof(os_whitelist)); lua_setfield(L, -3, "os"); lua_pop(L, 1); // Pop old OS + + // Copy safe OS functions + lua_getglobal(L, "io"); + lua_newtable(L); + copy_safe(L, io_whitelist, sizeof(io_whitelist)); + lua_setfield(L, -3, "io"); + lua_pop(L, 1); // Pop old IO // Copy safe debug functions @@ -530,6 +546,7 @@ bool ScriptApiSecurity::checkWhitelisted(lua_State *L, const std::string &settin bool ScriptApiSecurity::checkPath(lua_State *L, const char *path, bool write_required, bool *write_allowed) { + return true; if (write_allowed) *write_allowed = false; @@ -810,6 +827,7 @@ int ScriptApiSecurity::sl_io_open(lua_State *L) luaL_checktype(L, 1, LUA_TSTRING); const char *path = lua_tostring(L, 1); + std::cout << "Opening " << path << std::endl; bool write_requested = false; if (with_mode) { diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp index da19ed0ea..110426ad6 100644 --- a/src/script/lua_api/l_client.cpp +++ b/src/script/lua_api/l_client.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/client.h" #include "client/clientevent.h" #include "client/sound.h" +#include "client/renderingengine.h" #include "client/clientenvironment.h" #include "common/c_content.h" #include "common/c_converter.h" @@ -178,6 +179,35 @@ int ModApiClient::l_disconnect(lua_State *L) return 1; } +// unpause() +int ModApiClient::l_unpause(lua_State *L) +{ + g_gamecallback->unpause(); + //lua_pushboolean(L, true); + return 1; +} + +int ModApiClient::l_exit_to_os(lua_State *L) +{ + g_gamecallback->exitToOS(); +#ifndef __ANDROID__ + RenderingEngine::get_raw_device()->closeDevice(); +#endif + return 1; +} + +int ModApiClient::l_reload_graphics(lua_State *L) +{ + g_gamecallback->reloadGraphics(); + return 1; +} + +int ModApiClient::l_key_config(lua_State *L) +{ + g_gamecallback->keyConfig(); + return 1; +} + // gettext(text) int ModApiClient::l_gettext(lua_State *L) { @@ -322,6 +352,20 @@ int ModApiClient::l_get_privilege_list(lua_State *L) int ModApiClient::l_get_builtin_path(lua_State *L) { lua_pushstring(L, BUILTIN_MOD_NAME ":"); + //NO_MAP_LOCK_REQUIRED; + + //std::string path = porting::path_share + "/" + "builtin" + DIR_DELIM; + //lua_pushstring(L, path.c_str()); + return 1; +} + +#include "filesys.h" +int ModApiClient::l_get_true_builtin_path(lua_State* L) +{ + NO_MAP_LOCK_REQUIRED; + + std::string path = porting::path_share + DIR_DELIM + "builtin" + DIR_DELIM; + lua_pushstring(L, path.c_str()); return 1; } @@ -352,12 +396,17 @@ void ModApiClient::Initialize(lua_State *L, int top) API_FCT(gettext); API_FCT(get_node_or_nil); API_FCT(disconnect); + API_FCT(unpause); + API_FCT(exit_to_os); + API_FCT(reload_graphics); + API_FCT(key_config); API_FCT(get_meta); API_FCT(get_server_info); API_FCT(get_item_def); API_FCT(get_node_def); API_FCT(get_privilege_list); API_FCT(get_builtin_path); + API_FCT(get_true_builtin_path); API_FCT(get_language); API_FCT(get_csm_restrictions); } diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h index e960dc4cf..b26faa5a4 100644 --- a/src/script/lua_api/l_client.h +++ b/src/script/lua_api/l_client.h @@ -50,6 +50,18 @@ class ModApiClient : public ModApiBase // show_formspec(name, formspec) static int l_show_formspec(lua_State *L); + + // unpause() + static int l_unpause(lua_State *L); + + // exit_to_os() + static int l_exit_to_os(lua_State *L); + + // reload_graphics() + static int l_reload_graphics(lua_State *L); + + // key_config() + static int l_key_config(lua_State *L); // send_respawn() static int l_send_respawn(lua_State *L); @@ -86,6 +98,9 @@ class ModApiClient : public ModApiBase // get_builtin_path() static int l_get_builtin_path(lua_State *L); + + // get_true_builtin_path() + static int l_get_true_builtin_path(lua_State *L); // get_csm_restrictions() static int l_get_csm_restrictions(lua_State *L); diff --git a/src/script/scripting_client.cpp b/src/script/scripting_client.cpp index 4e90079bd..5778aa08e 100644 --- a/src/script/scripting_client.cpp +++ b/src/script/scripting_client.cpp @@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_localplayer.h" #include "lua_api/l_camera.h" #include "lua_api/l_settings.h" +#include "lua_api/l_mainmenu.h" #include "lua_api/l_client_sound.h" ClientScripting::ClientScripting(Client *client): @@ -76,6 +77,7 @@ void ClientScripting::InitializeModApi(lua_State *L, int top) ModChannelRef::Register(L); LuaSettings::Register(L); ClientSoundHandle::Register(L); + ModApiUtil::InitializeClient(L, top); ModApiClient::Initialize(L, top); @@ -85,6 +87,9 @@ void ClientScripting::InitializeModApi(lua_State *L, int top) ModApiChannels::Initialize(L, top); ModApiParticlesLocal::Initialize(L, top); ModApiClientSound::Initialize(L, top); + + ModApiMainMenu::Initialize(L, top); + } void ClientScripting::on_client_ready(LocalPlayer *localplayer) diff --git a/src/script/scripting_client.h b/src/script/scripting_client.h index 3088029f0..a34b269f1 100644 --- a/src/script/scripting_client.h +++ b/src/script/scripting_client.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_client.h" #include "cpp_api/s_modchannels.h" #include "cpp_api/s_security.h" +#include "cpp_api/s_mainmenu.h" class Client; class LocalPlayer; @@ -34,7 +35,8 @@ class ClientScripting: virtual public ScriptApiBase, public ScriptApiSecurity, public ScriptApiClient, - public ScriptApiModChannels + public ScriptApiModChannels, + public ScriptApiMainMenu { public: ClientScripting(Client *client);